From 17a531520b752618535ef527b53a2dc85b107760 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:40:29 +0300 Subject: [PATCH 001/144] 1_0_rename --- .../model/{UserMeal.java => Meal.java} | 4 +- .../{UserMealWithExcess.java => MealTo.java} | 6 +-- .../javawebinar/topjava/util/MealsUtil.java | 39 +++++++++++++++++++ .../topjava/util/UserMealsUtil.java | 39 ------------------- 4 files changed, 44 insertions(+), 44 deletions(-) rename src/main/java/ru/javawebinar/topjava/model/{UserMeal.java => Meal.java} (83%) rename src/main/java/ru/javawebinar/topjava/model/{UserMealWithExcess.java => MealTo.java} (77%) create mode 100644 src/main/java/ru/javawebinar/topjava/util/MealsUtil.java delete mode 100644 src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java similarity index 83% rename from src/main/java/ru/javawebinar/topjava/model/UserMeal.java rename to src/main/java/ru/javawebinar/topjava/model/Meal.java index d8f91b12..f546cef0 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -2,14 +2,14 @@ import java.time.LocalDateTime; -public class UserMeal { +public class Meal { private final LocalDateTime dateTime; private final String description; private final int calories; - public UserMeal(LocalDateTime dateTime, String description, int calories) { + public Meal(LocalDateTime dateTime, String description, int calories) { this.dateTime = dateTime; this.description = description; this.calories = calories; diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExcess.java b/src/main/java/ru/javawebinar/topjava/model/MealTo.java similarity index 77% rename from src/main/java/ru/javawebinar/topjava/model/UserMealWithExcess.java rename to src/main/java/ru/javawebinar/topjava/model/MealTo.java index d0aa431a..07f04f8d 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExcess.java +++ b/src/main/java/ru/javawebinar/topjava/model/MealTo.java @@ -2,7 +2,7 @@ import java.time.LocalDateTime; -public class UserMealWithExcess { +public class MealTo { private final LocalDateTime dateTime; private final String description; @@ -11,7 +11,7 @@ public class UserMealWithExcess { private final boolean excess; - public UserMealWithExcess(LocalDateTime dateTime, String description, int calories, boolean excess) { + public MealTo(LocalDateTime dateTime, String description, int calories, boolean excess) { this.dateTime = dateTime; this.description = description; this.calories = calories; @@ -20,7 +20,7 @@ public UserMealWithExcess(LocalDateTime dateTime, String description, int calori @Override public String toString() { - return "UserMealWithExcess{" + + return "MealTo{" + "dateTime=" + dateTime + ", description='" + description + '\'' + ", calories=" + calories + diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java new file mode 100644 index 00000000..bb5ddbf5 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -0,0 +1,39 @@ +package ru.javawebinar.topjava.util; + +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.model.MealTo; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.util.Arrays; +import java.util.List; + +public class MealsUtil { + public static void main(String[] args) { + List meals = Arrays.asList( + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) + ); + + List mealsTo = filteredByCycles(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + mealsTo.forEach(System.out::println); + +// System.out.println(filteredByStreams(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + } + + public static List filteredByCycles(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + // TODO return filtered list with excess. Implement by cycles + return null; + } + + public static List filteredByStreams(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + // TODO Implement by streams + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java deleted file mode 100644 index 3c171b4a..00000000 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.javawebinar.topjava.util; - -import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.model.UserMealWithExcess; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.util.Arrays; -import java.util.List; - -public class UserMealsUtil { - public static void main(String[] args) { - List meals = Arrays.asList( - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 1000), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 31, 13, 0), "Обед", 500), - new UserMeal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) - ); - - List mealsTo = filteredByCycles(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); - mealsTo.forEach(System.out::println); - -// System.out.println(filteredByStreams(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); - } - - public static List filteredByCycles(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO return filtered list with excess. Implement by cycles - return null; - } - - public static List filteredByStreams(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO Implement by streams - return null; - } -} From 235bf0134248ef11a4201387ae8616039dc14bc7 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:41:12 +0300 Subject: [PATCH 002/144] 1_1_HW0_streams --- .../ru/javawebinar/topjava/model/Meal.java | 10 +++++++ .../javawebinar/topjava/util/MealsUtil.java | 26 ++++++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index f546cef0..943ff5cd 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.model; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; public class Meal { private final LocalDateTime dateTime; @@ -26,4 +28,12 @@ public String getDescription() { public int getCalories() { return calories; } + + public LocalDate getDate() { + return dateTime.toLocalDate(); + } + + public LocalTime getTime() { + return dateTime.toLocalTime(); + } } diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index bb5ddbf5..c29e1fbb 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -3,11 +3,14 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.model.MealTo; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; public class MealsUtil { public static void main(String[] args) { @@ -21,19 +24,24 @@ public static void main(String[] args) { new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) ); - List mealsTo = filteredByCycles(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + List mealsTo = filteredByStreams(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); mealsTo.forEach(System.out::println); - -// System.out.println(filteredByStreams(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); } - public static List filteredByCycles(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO return filtered list with excess. Implement by cycles - return null; + public static List filteredByStreams(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + Map caloriesSumByDate = meals.stream() + .collect( + Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) +// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum) + ); + + return meals.stream() + .filter(meal -> TimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)) + .map(meal -> createTo(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .collect(Collectors.toList()); } - public static List filteredByStreams(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO Implement by streams - return null; + private static MealTo createTo(Meal meal, boolean excess) { + return new MealTo(meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess); } } From f1202fb9778f19345a3af8ed7d9077be87e96497 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:53:57 +0300 Subject: [PATCH 003/144] 1_2_switch_to_war --- pom.xml | 2 +- src/main/webapp/WEB-INF/web.xml | 19 +++++++++++++++++++ src/main/webapp/index.html | 13 +++++++++++++ src/main/webapp/users.jsp | 11 +++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.html create mode 100644 src/main/webapp/users.jsp diff --git a/pom.xml b/pom.xml index 0b1c2896..b4426c1a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ ru.javawebinar topjava - jar + war 1.0-SNAPSHOT diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..c63810c4 --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,19 @@ + + + Topjava + + + userServlet + ru.javawebinar.topjava.web.UserServlet + 0 + + + userServlet + /users + + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 00000000..58d8d5ab --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,13 @@ + + + + Java Enterprise (Topjava) + + +

Проект Java Enterprise (Topjava)

+
+ + + diff --git a/src/main/webapp/users.jsp b/src/main/webapp/users.jsp new file mode 100644 index 00000000..650c8dda --- /dev/null +++ b/src/main/webapp/users.jsp @@ -0,0 +1,11 @@ +<%@ page contentType="text/html;charset=UTF-8" %> + + + Users + + +

Home

+
+

Users

+ + \ No newline at end of file From d549cfd2e815b7b6f9da5b84660606598fbc3526 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:55:33 +0300 Subject: [PATCH 004/144] 1_3_add_servlet_api --- pom.xml | 7 +++++++ .../ru/javawebinar/topjava/web/UserServlet.java | 15 +++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/web/UserServlet.java diff --git a/pom.xml b/pom.xml index b4426c1a..3cd703f9 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,13 @@ + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java new file mode 100644 index 00000000..76056e06 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -0,0 +1,15 @@ +package ru.javawebinar.topjava.web; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class UserServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getRequestDispatcher("/users.jsp").forward(request, response); + } +} From 707a312ec3152f75e208a24b9e4621f0837ab943 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:56:12 +0300 Subject: [PATCH 005/144] 1_4_forward_to_redirect --- pom.xml | 2 +- src/main/java/ru/javawebinar/topjava/web/UserServlet.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3cd703f9..f7abfea2 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ topjava - install + package org.apache.maven.plugins diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 76056e06..11f282ba 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -10,6 +10,7 @@ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - request.getRequestDispatcher("/users.jsp").forward(request, response); +// request.getRequestDispatcher("/users.jsp").forward(request, response); + response.sendRedirect("users.jsp"); } } From 6b26351e702a92bcfa8c1a870e56e53a312802b4 Mon Sep 17 00:00:00 2001 From: "admin@javaops.ru" Date: Wed, 3 Feb 2021 23:58:29 +0300 Subject: [PATCH 006/144] 1_5_logging --- pom.xml | 19 ++++++++++++ .../javawebinar/topjava/web/UserServlet.java | 7 +++++ src/main/resources/logback.xml | 29 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index f7abfea2..75660f07 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,10 @@ 1.8 UTF-8 UTF-8 + + + 1.2.3 + 1.7.30 @@ -34,6 +38,21 @@ + + + org.slf4j + slf4j-api + ${slf4j.version} + compile + + + + ch.qos.logback + logback-classic + ${logback.version} + runtime + + javax.servlet diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 11f282ba..ef52d675 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,15 +1,22 @@ package ru.javawebinar.topjava.web; +import org.slf4j.Logger; + import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import static org.slf4j.LoggerFactory.getLogger; + public class UserServlet extends HttpServlet { + private static final Logger log = getLogger(UserServlet.class); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + log.debug("redirect to users"); + // request.getRequestDispatcher("/users.jsp").forward(request, response); response.sendRedirect("users.jsp"); } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 00000000..e9b900b2 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + + ${TOPJAVA_ROOT}/log/topjava.log + + + UTF-8 + %date %-5level %logger{0} [%file:%line] %msg%n + + + + + + UTF-8 + %-5level %logger{0} [%file:%line] %msg%n + + + + + + + + + + From 00511303b1a34f251ae1d334119d3b853836e38b Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Feb 2021 19:11:50 +0300 Subject: [PATCH 007/144] lesson02 --- pom.xml | 15 +++ .../java/ru/javawebinar/topjava/Main.java | 11 --- .../ru/javawebinar/topjava/SpringMain.java | 20 ++++ .../topjava/model/AbstractBaseEntity.java | 26 ++++++ .../topjava/model/AbstractNamedEntity.java | 24 +++++ .../ru/javawebinar/topjava/model/Meal.java | 29 ++++++ .../ru/javawebinar/topjava/model/MealTo.java | 28 +++++- .../ru/javawebinar/topjava/model/Role.java | 6 ++ .../ru/javawebinar/topjava/model/User.java | 91 +++++++++++++++++++ .../topjava/repository/MealRepository.java | 19 ++++ .../topjava/repository/UserRepository.java | 21 +++++ .../inmemory/InMemoryMealRepository.java | 46 ++++++++++ .../inmemory/InMemoryUserRepository.java | 45 +++++++++ .../topjava/service/MealService.java | 9 ++ .../topjava/service/UserService.java | 44 +++++++++ .../topjava/util/DateTimeUtil.java | 18 ++++ .../javawebinar/topjava/util/MealsUtil.java | 39 ++++---- .../ru/javawebinar/topjava/util/TimeUtil.java | 9 -- .../topjava/util/ValidationUtil.java | 43 +++++++++ .../util/exception/NotFoundException.java | 7 ++ .../javawebinar/topjava/web/MealServlet.java | 77 ++++++++++++++++ .../javawebinar/topjava/web/SecurityUtil.java | 14 +++ .../javawebinar/topjava/web/UserServlet.java | 6 +- .../topjava/web/meal/MealRestController.java | 8 ++ .../web/user/AbstractUserController.java | 51 +++++++++++ .../topjava/web/user/AdminRestController.java | 40 ++++++++ .../web/user/ProfileRestController.java | 22 +++++ src/main/resources/spring/spring-app.xml | 18 ++++ src/main/webapp/WEB-INF/tld/functions.tld | 16 ++++ src/main/webapp/WEB-INF/web.xml | 10 ++ src/main/webapp/index.html | 1 + src/main/webapp/mealForm.jsp | 51 +++++++++++ src/main/webapp/meals.jsp | 54 +++++++++++ 33 files changed, 876 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/Main.java create mode 100644 src/main/java/ru/javawebinar/topjava/SpringMain.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/Role.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/User.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/MealRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/UserRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/MealService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserService.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java delete mode 100644 src/main/java/ru/javawebinar/topjava/util/TimeUtil.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/MealServlet.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java create mode 100644 src/main/resources/spring/spring-app.xml create mode 100644 src/main/webapp/WEB-INF/tld/functions.tld create mode 100644 src/main/webapp/mealForm.jsp create mode 100644 src/main/webapp/meals.jsp diff --git a/pom.xml b/pom.xml index 75660f07..f490543f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ UTF-8 UTF-8 + 5.3.3 + 1.2.3 1.7.30 @@ -53,6 +55,13 @@ runtime + + + org.springframework + spring-context + ${spring.version} + + javax.servlet @@ -60,6 +69,12 @@ 4.0.1 provided + + + javax.servlet + jstl + 1.2 + diff --git a/src/main/java/ru/javawebinar/topjava/Main.java b/src/main/java/ru/javawebinar/topjava/Main.java deleted file mode 100644 index c2f9cc61..00000000 --- a/src/main/java/ru/javawebinar/topjava/Main.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.javawebinar.topjava; - -/** - * @see Demo application - * @see Initial project - */ -public class Main { - public static void main(String[] args) { - System.out.format("Hello TopJava Enterprise!"); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java new file mode 100644 index 00000000..85d0832e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -0,0 +1,20 @@ +package ru.javawebinar.topjava; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.web.user.AdminRestController; + +import java.util.Arrays; + +public class SpringMain { + public static void main(String[] args) { + // java 7 automatic resource management (ARM) + try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml")) { + System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); + AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); + adminUserController.create(new User(null, "userName", "email@mail.ru", "password", Role.ADMIN)); + } + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java new file mode 100644 index 00000000..8f27c902 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava.model; + +public abstract class AbstractBaseEntity { + protected Integer id; + + protected AbstractBaseEntity(Integer id) { + this.id = id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public boolean isNew() { + return this.id == null; + } + + @Override + public String toString() { + return getClass().getSimpleName() + ":" + id; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java new file mode 100644 index 00000000..2054a3d3 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.model; + +public abstract class AbstractNamedEntity extends AbstractBaseEntity { + + protected String name; + + protected AbstractNamedEntity(Integer id, String name) { + super(id); + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + @Override + public String toString() { + return super.toString() + '(' + name + ')'; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 943ff5cd..3abbee42 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -5,6 +5,8 @@ import java.time.LocalTime; public class Meal { + private Integer id; + private final LocalDateTime dateTime; private final String description; @@ -12,11 +14,24 @@ public class Meal { private final int calories; public Meal(LocalDateTime dateTime, String description, int calories) { + this(null, dateTime, description, calories); + } + + public Meal(Integer id, LocalDateTime dateTime, String description, int calories) { + this.id = id; this.dateTime = dateTime; this.description = description; this.calories = calories; } + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + public LocalDateTime getDateTime() { return dateTime; } @@ -36,4 +51,18 @@ public LocalDate getDate() { public LocalTime getTime() { return dateTime.toLocalTime(); } + + public boolean isNew() { + return id == null; + } + + @Override + public String toString() { + return "Meal{" + + "id=" + id + + ", dateTime=" + dateTime + + ", description='" + description + '\'' + + ", calories=" + calories + + '}'; + } } diff --git a/src/main/java/ru/javawebinar/topjava/model/MealTo.java b/src/main/java/ru/javawebinar/topjava/model/MealTo.java index 07f04f8d..01b3a5fd 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealTo.java +++ b/src/main/java/ru/javawebinar/topjava/model/MealTo.java @@ -3,6 +3,8 @@ import java.time.LocalDateTime; public class MealTo { + private final Integer id; + private final LocalDateTime dateTime; private final String description; @@ -11,17 +13,39 @@ public class MealTo { private final boolean excess; - public MealTo(LocalDateTime dateTime, String description, int calories, boolean excess) { + public MealTo(Integer id, LocalDateTime dateTime, String description, int calories, boolean excess) { + this.id = id; this.dateTime = dateTime; this.description = description; this.calories = calories; this.excess = excess; } + public Integer getId() { + return id; + } + + public LocalDateTime getDateTime() { + return dateTime; + } + + public String getDescription() { + return description; + } + + public int getCalories() { + return calories; + } + + public boolean isExcess() { + return excess; + } + @Override public String toString() { return "MealTo{" + - "dateTime=" + dateTime + + "id=" + id + + ", dateTime=" + dateTime + ", description='" + description + '\'' + ", calories=" + calories + ", excess=" + excess + diff --git a/src/main/java/ru/javawebinar/topjava/model/Role.java b/src/main/java/ru/javawebinar/topjava/model/Role.java new file mode 100644 index 00000000..acb7a276 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/Role.java @@ -0,0 +1,6 @@ +package ru.javawebinar.topjava.model; + +public enum Role { + USER, + ADMIN +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java new file mode 100644 index 00000000..d88e3819 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -0,0 +1,91 @@ +package ru.javawebinar.topjava.model; + +import java.util.Date; +import java.util.EnumSet; +import java.util.Set; + +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; + +public class User extends AbstractNamedEntity { + + private String email; + + private String password; + + private boolean enabled = true; + + private Date registered = new Date(); + + private Set roles; + + private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; + + public User(Integer id, String name, String email, String password, Role role, Role... roles) { + this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); + } + + public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) { + super(id, name); + this.email = email; + this.password = password; + this.caloriesPerDay = caloriesPerDay; + this.enabled = enabled; + this.roles = roles; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setPassword(String password) { + this.password = password; + } + + public Date getRegistered() { + return registered; + } + + public void setRegistered(Date registered) { + this.registered = registered; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getCaloriesPerDay() { + return caloriesPerDay; + } + + public void setCaloriesPerDay(int caloriesPerDay) { + this.caloriesPerDay = caloriesPerDay; + } + + public boolean isEnabled() { + return enabled; + } + + public Set getRoles() { + return roles; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + return "User (" + + "id=" + id + + ", email=" + email + + ", name=" + name + + ", enabled=" + enabled + + ", roles=" + roles + + ", caloriesPerDay=" + caloriesPerDay + + ')'; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java new file mode 100644 index 00000000..14b93756 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -0,0 +1,19 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.Meal; + +import java.util.Collection; + +public interface MealRepository { + // null if updated meal do not belong to userId + Meal save(Meal meal); + + // false if meal do not belong to userId + boolean delete(int id); + + // null if meal do not belong to userId + Meal get(int id); + + // ORDERED dateTime desc + Collection getAll(); +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java new file mode 100644 index 00000000..13836978 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -0,0 +1,21 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +public interface UserRepository { + // null if not found, when updated + User save(User user); + + // false if not found + boolean delete(int id); + + // null if not found + User get(int id); + + // null if not found + User getByEmail(String email); + + List getAll(); +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java new file mode 100644 index 00000000..3c7c9ff9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -0,0 +1,46 @@ +package ru.javawebinar.topjava.repository.inmemory; + +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.MealsUtil; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public class InMemoryMealRepository implements MealRepository { + private final Map repository = new ConcurrentHashMap<>(); + private final AtomicInteger counter = new AtomicInteger(0); + + { + MealsUtil.meals.forEach(this::save); + } + + @Override + public Meal save(Meal meal) { + if (meal.isNew()) { + meal.setId(counter.incrementAndGet()); + repository.put(meal.getId(), meal); + return meal; + } + // handle case: update, but not present in storage + return repository.computeIfPresent(meal.getId(), (id, oldMeal) -> meal); + } + + @Override + public boolean delete(int id) { + return repository.remove(id) != null; + } + + @Override + public Meal get(int id) { + return repository.get(id); + } + + @Override + public Collection getAll() { + return repository.values(); + } +} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java new file mode 100644 index 00000000..e2f8a9b8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -0,0 +1,45 @@ +package ru.javawebinar.topjava.repository.inmemory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Collections; +import java.util.List; + +@Repository +public class InMemoryUserRepository implements UserRepository { + private static final Logger log = LoggerFactory.getLogger(InMemoryUserRepository.class); + + @Override + public boolean delete(int id) { + log.info("delete {}", id); + return true; + } + + @Override + public User save(User user) { + log.info("save {}", user); + return user; + } + + @Override + public User get(int id) { + log.info("get {}", id); + return null; + } + + @Override + public List getAll() { + log.info("getAll"); + return Collections.emptyList(); + } + + @Override + public User getByEmail(String email) { + log.info("getByEmail {}", email); + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java new file mode 100644 index 00000000..0dc4a43c --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -0,0 +1,9 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.repository.MealRepository; + +public class MealService { + + private MealRepository repository; + +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java new file mode 100644 index 00000000..8fbe8dc0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -0,0 +1,44 @@ +package ru.javawebinar.topjava.service; + +import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + +@Service +public class UserService { + + private final UserRepository repository; + + public UserService(UserRepository repository) { + this.repository = repository; + } + + public User create(User user) { + return repository.save(user); + } + + public void delete(int id) { + checkNotFoundWithId(repository.delete(id), id); + } + + public User get(int id) { + return checkNotFoundWithId(repository.get(id), id); + } + + public User getByEmail(String email) { + return checkNotFound(repository.getByEmail(email), "email=" + email); + } + + public List getAll() { + return repository.getAll(); + } + + public void update(User user) { + checkNotFoundWithId(repository.save(user), user.getId()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java new file mode 100644 index 00000000..3f23f83f --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -0,0 +1,18 @@ +package ru.javawebinar.topjava.util; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +public class DateTimeUtil { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + + public static boolean isBetweenHalfOpen(LocalTime lt, LocalTime startTime, LocalTime endTime) { + return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) < 0; + } + + public static String toString(LocalDateTime ldt) { + return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); + } +} + diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index c29e1fbb..a74cc3ed 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -8,27 +8,34 @@ import java.time.LocalTime; import java.time.Month; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import java.util.stream.Collectors; public class MealsUtil { - public static void main(String[] args) { - List meals = Arrays.asList( - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 1000), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 13, 0), "Обед", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) - ); - - List mealsTo = filteredByStreams(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); - mealsTo.forEach(System.out::println); + public static final int DEFAULT_CALORIES_PER_DAY = 2000; + + public static final List meals = Arrays.asList( + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) + ); + + public static List getTos(Collection meals, int caloriesPerDay) { + return filterByPredicate(meals, caloriesPerDay, meal -> true); + } + + public static List getFilteredTos(Collection meals, int caloriesPerDay, LocalTime startTime, LocalTime endTime) { + return filterByPredicate(meals, caloriesPerDay, meal -> DateTimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)); } - public static List filteredByStreams(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + public static List filterByPredicate(Collection meals, int caloriesPerDay, Predicate filter) { Map caloriesSumByDate = meals.stream() .collect( Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) @@ -36,12 +43,12 @@ public static List filteredByStreams(List meals, LocalTime startTi ); return meals.stream() - .filter(meal -> TimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)) + .filter(filter) .map(meal -> createTo(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) .collect(Collectors.toList()); } private static MealTo createTo(Meal meal, boolean excess) { - return new MealTo(meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess); + return new MealTo(meal.getId(), meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess); } } diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java deleted file mode 100644 index 0ebfdb5f..00000000 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.javawebinar.topjava.util; - -import java.time.LocalTime; - -public class TimeUtil { - public static boolean isBetweenHalfOpen(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) < 0; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java new file mode 100644 index 00000000..971eb9c0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -0,0 +1,43 @@ +package ru.javawebinar.topjava.util; + + +import ru.javawebinar.topjava.model.AbstractBaseEntity; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +public class ValidationUtil { + + public static T checkNotFoundWithId(T object, int id) { + checkNotFoundWithId(object != null, id); + return object; + } + + public static void checkNotFoundWithId(boolean found, int id) { + checkNotFound(found, "id=" + id); + } + + public static T checkNotFound(T object, String msg) { + checkNotFound(object != null, msg); + return object; + } + + public static void checkNotFound(boolean found, String msg) { + if (!found) { + throw new NotFoundException("Not found entity with " + msg); + } + } + + public static void checkNew(AbstractBaseEntity entity) { + if (!entity.isNew()) { + throw new IllegalArgumentException(entity + " must be new (id=null)"); + } + } + + public static void assureIdConsistent(AbstractBaseEntity entity, int id) { +// conservative when you reply, but accept liberally (http://stackoverflow.com/a/32728226/548473) + if (entity.isNew()) { + entity.setId(id); + } else if (entity.getId() != id) { + throw new IllegalArgumentException(entity + " must be with id=" + id); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java new file mode 100644 index 00000000..f1e9b0e4 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.javawebinar.topjava.util.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java new file mode 100644 index 00000000..f3299023 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -0,0 +1,77 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.repository.inmemory.InMemoryMealRepository; +import ru.javawebinar.topjava.util.MealsUtil; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; + +public class MealServlet extends HttpServlet { + private static final Logger log = LoggerFactory.getLogger(MealServlet.class); + + private MealRepository repository; + + @Override + public void init() { + repository = new InMemoryMealRepository(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.setCharacterEncoding("UTF-8"); + String id = request.getParameter("id"); + + Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.parseInt(request.getParameter("calories"))); + + log.info(meal.isNew() ? "Create {}" : "Update {}", meal); + repository.save(meal); + response.sendRedirect("meals"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String action = request.getParameter("action"); + + switch (action == null ? "all" : action) { + case "delete": + int id = getId(request); + log.info("Delete {}", id); + repository.delete(id); + response.sendRedirect("meals"); + break; + case "create": + case "update": + final Meal meal = "create".equals(action) ? + new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : + repository.get(getId(request)); + request.setAttribute("meal", meal); + request.getRequestDispatcher("/mealForm.jsp").forward(request, response); + break; + case "all": + default: + log.info("getAll"); + request.setAttribute("meals", + MealsUtil.getTos(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + break; + } + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.parseInt(paramId); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java new file mode 100644 index 00000000..e78a4b28 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java @@ -0,0 +1,14 @@ +package ru.javawebinar.topjava.web; + +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; + +public class SecurityUtil { + + public static int authUserId() { + return 1; + } + + public static int authUserCaloriesPerDay() { + return DEFAULT_CALORIES_PER_DAY; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index ef52d675..f6cf12e6 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -15,9 +15,7 @@ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - log.debug("redirect to users"); - -// request.getRequestDispatcher("/users.jsp").forward(request, response); - response.sendRedirect("users.jsp"); + log.debug("forward to users"); + request.getRequestDispatcher("/users.jsp").forward(request, response); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java new file mode 100644 index 00000000..ab4e8ea8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -0,0 +1,8 @@ +package ru.javawebinar.topjava.web.meal; + +import ru.javawebinar.topjava.service.MealService; + +public class MealRestController { + private MealService service; + +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java new file mode 100644 index 00000000..0000f1c1 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -0,0 +1,51 @@ +package ru.javawebinar.topjava.web.user; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; + +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; + +public abstract class AbstractUserController { + protected final Logger log = LoggerFactory.getLogger(getClass()); + + @Autowired + private UserService service; + + public List getAll() { + log.info("getAll"); + return service.getAll(); + } + + public User get(int id) { + log.info("get {}", id); + return service.get(id); + } + + public User create(User user) { + log.info("create {}", user); + checkNew(user); + return service.create(user); + } + + public void delete(int id) { + log.info("delete {}", id); + service.delete(id); + } + + public void update(User user, int id) { + log.info("update {} with id={}", user, id); + assureIdConsistent(user, id); + service.update(user); + } + + public User getByMail(String email) { + log.info("getByEmail {}", email); + return service.getByEmail(email); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java new file mode 100644 index 00000000..b37a8ed6 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -0,0 +1,40 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +@Controller +public class AdminRestController extends AbstractUserController { + + @Override + public List getAll() { + return super.getAll(); + } + + @Override + public User get(int id) { + return super.get(id); + } + + @Override + public User create(User user) { + return super.create(user); + } + + @Override + public void delete(int id) { + super.delete(id); + } + + @Override + public void update(User user, int id) { + super.update(user, id); + } + + @Override + public User getByMail(String email) { + return super.getByMail(email); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java new file mode 100644 index 00000000..7d3702c3 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -0,0 +1,22 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.model.User; + +import static ru.javawebinar.topjava.web.SecurityUtil.authUserId; + +@Controller +public class ProfileRestController extends AbstractUserController { + + public User get() { + return super.get(authUserId()); + } + + public void delete() { + super.delete(authUserId()); + } + + public void update(User user) { + super.update(user, authUserId()); + } +} \ No newline at end of file diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml new file mode 100644 index 00000000..cac42ba1 --- /dev/null +++ b/src/main/resources/spring/spring-app.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tld/functions.tld b/src/main/webapp/WEB-INF/tld/functions.tld new file mode 100644 index 00000000..d138fecd --- /dev/null +++ b/src/main/webapp/WEB-INF/tld/functions.tld @@ -0,0 +1,16 @@ + + + + 1.0 + functions + http://topjava.javawebinar.ru/functions + + + formatDateTime + ru.javawebinar.topjava.util.DateTimeUtil + java.lang.String toString(java.time.LocalDateTime) + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index c63810c4..bd98d3bf 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -16,4 +16,14 @@ /users + + mealServlet + ru.javawebinar.topjava.web.MealServlet + 0 + + + mealServlet + /meals + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 58d8d5ab..45d61b08 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -8,6 +8,7 @@

Проект Users +
  • Meals
  • diff --git a/src/main/webapp/mealForm.jsp b/src/main/webapp/mealForm.jsp new file mode 100644 index 00000000..01830008 --- /dev/null +++ b/src/main/webapp/mealForm.jsp @@ -0,0 +1,51 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + Meal + + + +
    +

    Home

    +
    +

    ${param.action == 'create' ? 'Create meal' : 'Edit meal'}

    + +
    + +
    +
    DateTime:
    +
    +
    +
    +
    Description:
    +
    +
    +
    +
    Calories:
    +
    +
    + + +
    +
    + + diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp new file mode 100644 index 00000000..b90cadc9 --- /dev/null +++ b/src/main/webapp/meals.jsp @@ -0,0 +1,54 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> +<%--<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>--%> + + + Meal list + + + +
    +

    Home

    +
    +

    Meals

    + Add Meal +

    + + + + + + + + + + + + + + + + + + + + +
    DateDescriptionCalories
    + <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> + <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> + <%--${fn:replace(meal.dateTime, 'T', ' ')}--%> + ${fn:formatDateTime(meal.dateTime)} + ${meal.description}${meal.calories}UpdateDelete
    +
    + + \ No newline at end of file From aaee5f11ab79612f9b37110cd6bed2662eb81cf3 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:03:38 +0300 Subject: [PATCH 008/144] 3_0_fix.patch --- .../ru/javawebinar/topjava/repository/MealRepository.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index 14b93756..d7637d04 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -5,13 +5,13 @@ import java.util.Collection; public interface MealRepository { - // null if updated meal do not belong to userId + // null if updated meal does not belong to userId Meal save(Meal meal); - // false if meal do not belong to userId + // false if meal does not belong to userId boolean delete(int id); - // null if meal do not belong to userId + // null if meal does not belong to userId Meal get(int id); // ORDERED dateTime desc From a885524f235bc2fb0f45d3e81c0119b0afe57c02 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:05:46 +0300 Subject: [PATCH 009/144] 3_01_HW2_repositories.patch --- .../topjava/repository/MealRepository.java | 10 ++-- .../inmemory/InMemoryMealRepository.java | 51 +++++++++++++------ .../inmemory/InMemoryUserRepository.java | 46 ++++++++++------- .../javawebinar/topjava/web/MealServlet.java | 8 +-- 4 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index d7637d04..4311fcd2 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -2,18 +2,18 @@ import ru.javawebinar.topjava.model.Meal; -import java.util.Collection; +import java.util.List; public interface MealRepository { // null if updated meal does not belong to userId - Meal save(Meal meal); + Meal save(Meal meal, int userId); // false if meal does not belong to userId - boolean delete(int id); + boolean delete(int id, int userId); // null if meal does not belong to userId - Meal get(int id); + Meal get(int id, int userId); // ORDERED dateTime desc - Collection getAll(); + List getAll(int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 3c7c9ff9..ff32a8fb 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -1,46 +1,67 @@ package ru.javawebinar.topjava.repository.inmemory; +import org.springframework.stereotype.Repository; +import org.springframework.util.CollectionUtils; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; -import java.util.Collection; +import java.time.LocalDateTime; +import java.time.Month; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import static ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository.ADMIN_ID; +import static ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository.USER_ID; + +@Repository public class InMemoryMealRepository implements MealRepository { - private final Map repository = new ConcurrentHashMap<>(); + + // Map userId -> (mealId-> meal) + private final Map> usersMealsMap = new ConcurrentHashMap<>(); private final AtomicInteger counter = new AtomicInteger(0); { - MealsUtil.meals.forEach(this::save); + MealsUtil.meals.forEach(meal -> save(meal, USER_ID)); + save(new Meal(LocalDateTime.of(2015, Month.JUNE, 1, 14, 0), "Админ ланч", 510), ADMIN_ID); + save(new Meal(LocalDateTime.of(2015, Month.JUNE, 1, 21, 0), "Админ ужин", 1500), ADMIN_ID); } + @Override - public Meal save(Meal meal) { + public Meal save(Meal meal, int userId) { + Map meals = usersMealsMap.computeIfAbsent(userId, ConcurrentHashMap::new); if (meal.isNew()) { meal.setId(counter.incrementAndGet()); - repository.put(meal.getId(), meal); + meals.put(meal.getId(), meal); return meal; } - // handle case: update, but not present in storage - return repository.computeIfPresent(meal.getId(), (id, oldMeal) -> meal); + return meals.computeIfPresent(meal.getId(), (id, oldMeal) -> meal); } @Override - public boolean delete(int id) { - return repository.remove(id) != null; + public boolean delete(int id, int userId) { + Map meals = usersMealsMap.get(userId); + return meals != null && meals.remove(id) != null; } @Override - public Meal get(int id) { - return repository.get(id); + public Meal get(int id, int userId) { + Map meals = usersMealsMap.get(userId); + return meals == null ? null : meals.get(id); } @Override - public Collection getAll() { - return repository.values(); + public List getAll(int userId) { + Map meals = usersMealsMap.get(userId); + return CollectionUtils.isEmpty(meals) ? Collections.emptyList() : + meals.values().stream() + .sorted(Comparator.comparing(Meal::getDateTime).reversed()) + .collect(Collectors.toList()); } -} - +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index e2f8a9b8..3565868c 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -1,45 +1,57 @@ package ru.javawebinar.topjava.repository.inmemory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; -import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; @Repository public class InMemoryUserRepository implements UserRepository { - private static final Logger log = LoggerFactory.getLogger(InMemoryUserRepository.class); + + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + + private final Map usersMap = new ConcurrentHashMap<>(); + private final AtomicInteger counter = new AtomicInteger(0); @Override - public boolean delete(int id) { - log.info("delete {}", id); - return true; + public User save(User user) { + if (user.isNew()) { + user.setId(counter.incrementAndGet()); + usersMap.put(user.getId(), user); + return user; + } + return usersMap.computeIfPresent(user.getId(), (id, oldUser) -> user); } @Override - public User save(User user) { - log.info("save {}", user); - return user; + public boolean delete(int id) { + return usersMap.remove(id) != null; } @Override public User get(int id) { - log.info("get {}", id); - return null; + return usersMap.get(id); } @Override public List getAll() { - log.info("getAll"); - return Collections.emptyList(); + return usersMap.values().stream() + .sorted(Comparator.comparing(User::getName).thenComparing(User::getEmail)) + .collect(Collectors.toList()); } @Override public User getByEmail(String email) { - log.info("getByEmail {}", email); - return null; + return usersMap.values().stream() + .filter(u -> email.equals(u.getEmail())) + .findFirst() + .orElse(null); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index f3299023..6f6ac998 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -37,7 +37,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) Integer.parseInt(request.getParameter("calories"))); log.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal); + repository.save(meal, SecurityUtil.authUserId()); response.sendRedirect("meals"); } @@ -49,14 +49,14 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "delete": int id = getId(request); log.info("Delete {}", id); - repository.delete(id); + repository.delete(id, SecurityUtil.authUserId()); response.sendRedirect("meals"); break; case "create": case "update": final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request)); + repository.get(getId(request), SecurityUtil.authUserId()); request.setAttribute("meal", meal); request.getRequestDispatcher("/mealForm.jsp").forward(request, response); break; @@ -64,7 +64,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t default: log.info("getAll"); request.setAttribute("meals", - MealsUtil.getTos(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + MealsUtil.getTos(repository.getAll(SecurityUtil.authUserId()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); request.getRequestDispatcher("/meals.jsp").forward(request, response); break; } From 16c3d23ab91ef16aced67253e997500a177e13a9 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:12:05 +0300 Subject: [PATCH 010/144] 3_02_HW2_repo_filters.patch --- .../topjava/repository/MealRepository.java | 4 ++++ .../repository/inmemory/InMemoryMealRepository.java | 12 ++++++++++++ .../ru/javawebinar/topjava/util/DateTimeUtil.java | 6 ------ .../java/ru/javawebinar/topjava/util/MealsUtil.java | 2 +- src/main/java/ru/javawebinar/topjava/util/Util.java | 9 +++++++++ 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/Util.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index 4311fcd2..9461d5f9 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -2,6 +2,7 @@ import ru.javawebinar.topjava.model.Meal; +import java.time.LocalDateTime; import java.util.List; public interface MealRepository { @@ -16,4 +17,7 @@ public interface MealRepository { // ORDERED dateTime desc List getAll(int userId); + + // ORDERED dateTime desc + List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index ff32a8fb..2cd9136b 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -5,6 +5,7 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.util.Util; import java.time.LocalDateTime; import java.time.Month; @@ -14,6 +15,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; import java.util.stream.Collectors; import static ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository.ADMIN_ID; @@ -56,11 +58,21 @@ public Meal get(int id, int userId) { return meals == null ? null : meals.get(id); } + @Override + public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return filterByPredicate(userId, meal -> Util.isBetweenHalfOpen(meal.getDateTime(), startDateTime, endDateTime)); + } + @Override public List getAll(int userId) { + return filterByPredicate(userId, meal -> true); + } + + private List filterByPredicate(int userId, Predicate filter) { Map meals = usersMealsMap.get(userId); return CollectionUtils.isEmpty(meals) ? Collections.emptyList() : meals.values().stream() + .filter(filter) .sorted(Comparator.comparing(Meal::getDateTime).reversed()) .collect(Collectors.toList()); } diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 3f23f83f..9db7a809 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,18 +1,12 @@ package ru.javawebinar.topjava.util; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.format.DateTimeFormatter; public class DateTimeUtil { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - public static boolean isBetweenHalfOpen(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) < 0; - } - public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); } } - diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index a74cc3ed..0a9f5956 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -32,7 +32,7 @@ public static List getTos(Collection meals, int caloriesPerDay) { } public static List getFilteredTos(Collection meals, int caloriesPerDay, LocalTime startTime, LocalTime endTime) { - return filterByPredicate(meals, caloriesPerDay, meal -> DateTimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)); + return filterByPredicate(meals, caloriesPerDay, meal -> Util.isBetweenHalfOpen(meal.getTime(), startTime, endTime)); } public static List filterByPredicate(Collection meals, int caloriesPerDay, Predicate filter) { diff --git a/src/main/java/ru/javawebinar/topjava/util/Util.java b/src/main/java/ru/javawebinar/topjava/util/Util.java new file mode 100644 index 00000000..a17a6927 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/Util.java @@ -0,0 +1,9 @@ +package ru.javawebinar.topjava.util; + +import org.springframework.lang.Nullable; + +public class Util { + public static > boolean isBetweenHalfOpen(T value, @Nullable T start, @Nullable T end) { + return (start == null || value.compareTo(start) >= 0) && (end == null || value.compareTo(end) < 0); + } +} \ No newline at end of file From 5beefac9274b73414986dfc4bd23d0f8701bfb13 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:19:49 +0300 Subject: [PATCH 011/144] 3_03_HW2_meal_layers.patch --- .../ru/javawebinar/topjava/SpringMain.java | 16 +++++ .../ru/javawebinar/topjava/model/Meal.java | 18 +---- .../topjava/service/MealService.java | 40 ++++++++++- .../topjava/{model => to}/MealTo.java | 2 +- .../topjava/util/DateTimeUtil.java | 14 ++++ .../javawebinar/topjava/util/MealsUtil.java | 2 +- .../topjava/web/meal/MealRestController.java | 70 ++++++++++++++++++- src/main/webapp/meals.jsp | 2 +- 8 files changed, 143 insertions(+), 21 deletions(-) rename src/main/java/ru/javawebinar/topjava/{model => to}/MealTo.java (96%) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 85d0832e..b869d1c6 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -4,9 +4,15 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.to.MealTo; +import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; import java.util.Arrays; +import java.util.List; public class SpringMain { public static void main(String[] args) { @@ -15,6 +21,16 @@ public static void main(String[] args) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email@mail.ru", "password", Role.ADMIN)); + System.out.println(); + + MealRestController mealController = appCtx.getBean(MealRestController.class); + List filteredMealsWithExcess = + mealController.getBetween( + LocalDate.of(2020, Month.JANUARY, 30), LocalTime.of(7, 0), + LocalDate.of(2020, Month.JANUARY, 31), LocalTime.of(11, 0)); + filteredMealsWithExcess.forEach(System.out::println); + System.out.println(); + System.out.println(mealController.getBetween(null, null, null, null)); } } } diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 3abbee42..9eed15f7 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -4,9 +4,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; -public class Meal { - private Integer id; - +public class Meal extends AbstractBaseEntity { private final LocalDateTime dateTime; private final String description; @@ -18,20 +16,12 @@ public Meal(LocalDateTime dateTime, String description, int calories) { } public Meal(Integer id, LocalDateTime dateTime, String description, int calories) { - this.id = id; + super(id); this.dateTime = dateTime; this.description = description; this.calories = calories; } - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - public LocalDateTime getDateTime() { return dateTime; } @@ -52,10 +42,6 @@ public LocalTime getTime() { return dateTime.toLocalTime(); } - public boolean isNew() { - return id == null; - } - @Override public String toString() { return "Meal{" + diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index 0dc4a43c..7957a089 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -1,9 +1,47 @@ package ru.javawebinar.topjava.service; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import java.time.LocalDate; +import java.util.List; + +import static ru.javawebinar.topjava.util.DateTimeUtil.atStartOfDayOrMin; +import static ru.javawebinar.topjava.util.DateTimeUtil.atStartOfNextDayOrMax; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + +@Service public class MealService { - private MealRepository repository; + private final MealRepository repository; + + public MealService(MealRepository repository) { + this.repository = repository; + } + + public Meal get(int id, int userId) { + return checkNotFoundWithId(repository.get(id, userId), id); + } + + public void delete(int id, int userId) { + checkNotFoundWithId(repository.delete(id, userId), id); + } + + public List getBetweenInclusive(@Nullable LocalDate startDate, @Nullable LocalDate endDate, int userId) { + return repository.getBetweenHalfOpen(atStartOfDayOrMin(startDate), atStartOfNextDayOrMax(endDate), userId); + } + + public List getAll(int userId) { + return repository.getAll(userId); + } + + public void update(Meal meal, int userId) { + checkNotFoundWithId(repository.save(meal, userId), meal.getId()); + } + public Meal create(Meal meal, int userId) { + return repository.save(meal, userId); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/MealTo.java b/src/main/java/ru/javawebinar/topjava/to/MealTo.java similarity index 96% rename from src/main/java/ru/javawebinar/topjava/model/MealTo.java rename to src/main/java/ru/javawebinar/topjava/to/MealTo.java index 01b3a5fd..d14feae7 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealTo.java @@ -1,4 +1,4 @@ -package ru.javawebinar.topjava.model; +package ru.javawebinar.topjava.to; import java.time.LocalDateTime; diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 9db7a809..b2240cad 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,11 +1,25 @@ package ru.javawebinar.topjava.util; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; public class DateTimeUtil { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + // DB doesn't support LocalDate.MIN/MAX + private static final LocalDateTime MIN_DATE = LocalDateTime.of(1, 1, 1, 0, 0); + private static final LocalDateTime MAX_DATE = LocalDateTime.of(3000, 1, 1, 0, 0); + + public static LocalDateTime atStartOfDayOrMin(LocalDate localDate) { + return localDate != null ? localDate.atStartOfDay() : MIN_DATE; + } + + public static LocalDateTime atStartOfNextDayOrMax(LocalDate localDate) { + return localDate != null ? localDate.plus(1, ChronoUnit.DAYS).atStartOfDay() : MAX_DATE; + } + public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); } diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 0a9f5956..37923f74 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.util; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.model.MealTo; +import ru.javawebinar.topjava.to.MealTo; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index ab4e8ea8..bbfe35e3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,8 +1,76 @@ package ru.javawebinar.topjava.web.meal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.to.MealTo; +import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.SecurityUtil; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; + +@Controller public class MealRestController { - private MealService service; + private static final Logger log = LoggerFactory.getLogger(MealRestController.class); + + private final MealService service; + + public MealRestController(MealService service) { + this.service = service; + } + + public Meal get(int id) { + int userId = SecurityUtil.authUserId(); + log.info("get meal {} for user {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = SecurityUtil.authUserId(); + log.info("delete meal {} for user {}", id, userId); + service.delete(id, userId); + } + + public List getAll() { + int userId = SecurityUtil.authUserId(); + log.info("getAll for user {}", userId); + return MealsUtil.getTos(service.getAll(userId), SecurityUtil.authUserCaloriesPerDay()); + } + + public Meal create(Meal meal) { + int userId = SecurityUtil.authUserId(); + checkNew(meal); + log.info("create {} for user {}", meal, userId); + return service.create(meal, userId); + } + + public void update(Meal meal, int id) { + int userId = SecurityUtil.authUserId(); + assureIdConsistent(meal, id); + log.info("update {} for user {}", meal, userId); + service.update(meal, userId); + } + + /** + *
      Filter separately + *
    1. by date
    2. + *
    3. by time for every date
    4. + *
    + */ + public List getBetween(@Nullable LocalDate startDate, @Nullable LocalTime startTime, + @Nullable LocalDate endDate, @Nullable LocalTime endTime) { + int userId = SecurityUtil.authUserId(); + log.info("getBetween dates({} - {}) time({} - {}) for user {}", startDate, endDate, startTime, endTime, userId); + List mealsDateFiltered = service.getBetweenInclusive(startDate, endDate, userId); + return MealsUtil.getFilteredTos(mealsDateFiltered, SecurityUtil.authUserCaloriesPerDay(), startTime, endTime); + } } \ No newline at end of file diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index b90cadc9..6310069c 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -34,7 +34,7 @@ - + <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> From 5b5c50ae77eddf7d9a9cf9f0540eb39f7dbc7762 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:20:44 +0300 Subject: [PATCH 012/144] 3_04_refactor_repository.patch --- .../inmemory/InMemoryBaseRepository.java | 36 +++++++++++++++++++ .../inmemory/InMemoryMealRepository.java | 28 ++++++--------- .../inmemory/InMemoryUserRepository.java | 36 +++---------------- 3 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java new file mode 100644 index 00000000..aabc36aa --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java @@ -0,0 +1,36 @@ +package ru.javawebinar.topjava.repository.inmemory; + +import ru.javawebinar.topjava.model.AbstractBaseEntity; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public class InMemoryBaseRepository { + + private static final AtomicInteger counter = new AtomicInteger(0); + + private final Map map = new ConcurrentHashMap<>(); + + public T save(T entry) { + if (entry.isNew()) { + entry.setId(counter.incrementAndGet()); + map.put(entry.getId(), entry); + return entry; + } + return map.computeIfPresent(entry.getId(), (id, oldT) -> entry); + } + + public boolean delete(int id) { + return map.remove(id) != null; + } + + public T get(int id) { + return map.get(id); + } + + Collection getCollection() { + return map.values(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 2cd9136b..51251053 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -1,7 +1,6 @@ package ru.javawebinar.topjava.repository.inmemory; import org.springframework.stereotype.Repository; -import org.springframework.util.CollectionUtils; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; @@ -14,7 +13,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -24,9 +22,8 @@ @Repository public class InMemoryMealRepository implements MealRepository { - // Map userId -> (mealId-> meal) - private final Map> usersMealsMap = new ConcurrentHashMap<>(); - private final AtomicInteger counter = new AtomicInteger(0); + // Map userId -> mealRepository + private final Map> usersMealsMap = new ConcurrentHashMap<>(); { MealsUtil.meals.forEach(meal -> save(meal, USER_ID)); @@ -37,24 +34,19 @@ public class InMemoryMealRepository implements MealRepository { @Override public Meal save(Meal meal, int userId) { - Map meals = usersMealsMap.computeIfAbsent(userId, ConcurrentHashMap::new); - if (meal.isNew()) { - meal.setId(counter.incrementAndGet()); - meals.put(meal.getId(), meal); - return meal; - } - return meals.computeIfPresent(meal.getId(), (id, oldMeal) -> meal); + InMemoryBaseRepository meals = usersMealsMap.computeIfAbsent(userId, uid -> new InMemoryBaseRepository<>()); + return meals.save(meal); } @Override public boolean delete(int id, int userId) { - Map meals = usersMealsMap.get(userId); - return meals != null && meals.remove(id) != null; + InMemoryBaseRepository meals = usersMealsMap.get(userId); + return meals != null && meals.delete(id); } @Override public Meal get(int id, int userId) { - Map meals = usersMealsMap.get(userId); + InMemoryBaseRepository meals = usersMealsMap.get(userId); return meals == null ? null : meals.get(id); } @@ -69,9 +61,9 @@ public List getAll(int userId) { } private List filterByPredicate(int userId, Predicate filter) { - Map meals = usersMealsMap.get(userId); - return CollectionUtils.isEmpty(meals) ? Collections.emptyList() : - meals.values().stream() + InMemoryBaseRepository meals = usersMealsMap.get(userId); + return meals == null ? Collections.emptyList() : + meals.getCollection().stream() .filter(filter) .sorted(Comparator.comparing(Meal::getDateTime).reversed()) .collect(Collectors.toList()); diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index 3565868c..f8968a81 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -6,50 +6,24 @@ import java.util.Comparator; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Repository -public class InMemoryUserRepository implements UserRepository { +public class InMemoryUserRepository extends InMemoryBaseRepository implements UserRepository { - public static final int USER_ID = 1; - public static final int ADMIN_ID = 2; - - private final Map usersMap = new ConcurrentHashMap<>(); - private final AtomicInteger counter = new AtomicInteger(0); - - @Override - public User save(User user) { - if (user.isNew()) { - user.setId(counter.incrementAndGet()); - usersMap.put(user.getId(), user); - return user; - } - return usersMap.computeIfPresent(user.getId(), (id, oldUser) -> user); - } - - @Override - public boolean delete(int id) { - return usersMap.remove(id) != null; - } - - @Override - public User get(int id) { - return usersMap.get(id); - } + static final int USER_ID = 1; + static final int ADMIN_ID = 2; @Override public List getAll() { - return usersMap.values().stream() + return getCollection().stream() .sorted(Comparator.comparing(User::getName).thenComparing(User::getEmail)) .collect(Collectors.toList()); } @Override public User getByEmail(String email) { - return usersMap.values().stream() + return getCollection().stream() .filter(u -> email.equals(u.getEmail())) .findFirst() .orElse(null); From 7bb12ac1d3db6c3cfb7c903687fa77c8deaa75a4 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:30:25 +0300 Subject: [PATCH 013/144] 3_05_HW2_optional_MealServlet.patch --- .../javawebinar/topjava/web/MealServlet.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 6f6ac998..692ade4f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,11 +1,10 @@ package ru.javawebinar.topjava.web; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.util.StringUtils; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.repository.inmemory.InMemoryMealRepository; -import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.meal.MealRestController; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -17,27 +16,35 @@ import java.util.Objects; public class MealServlet extends HttpServlet { - private static final Logger log = LoggerFactory.getLogger(MealServlet.class); - private MealRepository repository; + private ConfigurableApplicationContext springContext; + private MealRestController mealController; @Override public void init() { - repository = new InMemoryMealRepository(); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + mealController = springContext.getBean(MealRestController.class); + } + + @Override + public void destroy() { + springContext.close(); + super.destroy(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); - String id = request.getParameter("id"); - - Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), + Meal meal = new Meal( LocalDateTime.parse(request.getParameter("dateTime")), request.getParameter("description"), Integer.parseInt(request.getParameter("calories"))); - log.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal, SecurityUtil.authUserId()); + if (StringUtils.hasLength(request.getParameter("id"))) { + mealController.create(meal); + } else { + mealController.update(meal, getId(request)); + } response.sendRedirect("meals"); } @@ -48,23 +55,20 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t switch (action == null ? "all" : action) { case "delete": int id = getId(request); - log.info("Delete {}", id); - repository.delete(id, SecurityUtil.authUserId()); + mealController.delete(id); response.sendRedirect("meals"); break; case "create": case "update": final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request), SecurityUtil.authUserId()); + mealController.get(getId(request)); request.setAttribute("meal", meal); request.getRequestDispatcher("/mealForm.jsp").forward(request, response); break; case "all": default: - log.info("getAll"); - request.setAttribute("meals", - MealsUtil.getTos(repository.getAll(SecurityUtil.authUserId()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.setAttribute("meals", mealController.getAll()); request.getRequestDispatcher("/meals.jsp").forward(request, response); break; } From 22985c1ba4e2744aa6178340e6a213b96497c495 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:31:21 +0300 Subject: [PATCH 014/144] 3_06_HW2_optional_filter.patch --- .../topjava/util/DateTimeUtil.java | 14 ++++++++ .../javawebinar/topjava/web/MealServlet.java | 13 +++++++ src/main/webapp/css/style.css | 24 +++++++++++++ src/main/webapp/mealForm.jsp | 19 +--------- src/main/webapp/meals.jsp | 36 ++++++++++++------- 5 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 src/main/webapp/css/style.css diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index b2240cad..b63ecf50 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,7 +1,11 @@ package ru.javawebinar.topjava.util; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; @@ -23,4 +27,14 @@ public static LocalDateTime atStartOfNextDayOrMax(LocalDate localDate) { public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); } + + public static @Nullable + LocalDate parseLocalDate(@Nullable String str) { + return StringUtils.hasLength(str) ? LocalDate.parse(str) : null; + } + + public static @Nullable + LocalTime parseLocalTime(@Nullable String str) { + return StringUtils.hasLength(str) ? LocalTime.parse(str) : null; + } } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 692ade4f..39868d2e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -11,10 +11,15 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.temporal.ChronoUnit; import java.util.Objects; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; + public class MealServlet extends HttpServlet { private ConfigurableApplicationContext springContext; @@ -66,6 +71,14 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t request.setAttribute("meal", meal); request.getRequestDispatcher("/mealForm.jsp").forward(request, response); break; + case "filter": + LocalDate startDate = parseLocalDate(request.getParameter("startDate")); + LocalDate endDate = parseLocalDate(request.getParameter("endDate")); + LocalTime startTime = parseLocalTime(request.getParameter("startTime")); + LocalTime endTime = parseLocalTime(request.getParameter("endTime")); + request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + break; case "all": default: request.setAttribute("meals", mealController.getAll()); diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css new file mode 100644 index 00000000..26a14ce4 --- /dev/null +++ b/src/main/webapp/css/style.css @@ -0,0 +1,24 @@ +dl { + background: none repeat scroll 0 0 #FAFAFA; + margin: 8px 0; + padding: 0; +} + +dt { + display: inline-block; + width: 170px; +} + +dd { + display: inline-block; + margin-left: 8px; + vertical-align: top; +} + +tr[data-mealExcess="false"] { + color: green; +} + +tr[data-mealExcess="true"] { + color: red; +} diff --git a/src/main/webapp/mealForm.jsp b/src/main/webapp/mealForm.jsp index 01830008..e9c01a94 100644 --- a/src/main/webapp/mealForm.jsp +++ b/src/main/webapp/mealForm.jsp @@ -4,24 +4,7 @@ Meal - +
    diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 6310069c..e1ce51e2 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -2,25 +2,37 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> -<%--<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>--%> - Meal list - + Meals +

    Home


    Meals

    +
    + +
    +
    From Date (inclusive):
    +
    +
    +
    +
    To Date (inclusive):
    +
    +
    +
    +
    From Time (inclusive):
    +
    +
    +
    +
    To Time (exclusive):
    +
    +
    + +
    +
    Add Meal

    @@ -35,7 +47,7 @@ - + + + + + + + + + + +
    <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> From de4c87019b5660eef92fb7f88bbb13de6de9321e Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 09:31:54 +0300 Subject: [PATCH 015/144] 3_07_HW2_optional_select_user.patch --- .../ru/javawebinar/topjava/web/SecurityUtil.java | 8 +++++++- .../java/ru/javawebinar/topjava/web/UserServlet.java | 7 +++++++ src/main/webapp/index.html | 12 ++++++++---- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java index e78a4b28..b9639bf1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java +++ b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java @@ -4,8 +4,14 @@ public class SecurityUtil { + private static int id = 1; + public static int authUserId() { - return 1; + return id; + } + + public static void setAuthUserId(int id) { + SecurityUtil.id = id; } public static int authUserCaloriesPerDay() { diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index f6cf12e6..22602340 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -13,6 +13,13 @@ public class UserServlet extends HttpServlet { private static final Logger log = getLogger(UserServlet.class); + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + int userId = Integer.parseInt(request.getParameter("userId")); + SecurityUtil.setAuthUserId(userId); + response.sendRedirect("meals"); + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { log.debug("forward to users"); diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 45d61b08..c476d1ed 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -6,9 +6,13 @@

    Проект Java Enterprise (Topjava)


    - +
    + Meals of  + + +
    From e405bd79490b1f44e7b1cb9a546f29289ee75db3 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 15:46:28 +0300 Subject: [PATCH 016/144] 3_08_bean_life_cycle.patch --- .../inmemory/InMemoryMealRepository.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 51251053..b7587381 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -1,11 +1,15 @@ package ru.javawebinar.topjava.repository.inmemory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; import ru.javawebinar.topjava.util.Util; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.time.LocalDateTime; import java.time.Month; import java.util.Collections; @@ -21,6 +25,7 @@ @Repository public class InMemoryMealRepository implements MealRepository { + private static final Logger log = LoggerFactory.getLogger(InMemoryMealRepository.class); // Map userId -> mealRepository private final Map> usersMealsMap = new ConcurrentHashMap<>(); @@ -38,6 +43,16 @@ public Meal save(Meal meal, int userId) { return meals.save(meal); } + @PostConstruct + public void postConstruct() { + log.info("+++ PostConstruct"); + } + + @PreDestroy + public void preDestroy() { + log.info("+++ PreDestroy"); + } + @Override public boolean delete(int id, int userId) { InMemoryBaseRepository meals = usersMealsMap.get(userId); From 7812d6423d79681512290c0755aa2a6ba9d03c0b Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 15:47:36 +0300 Subject: [PATCH 017/144] 3_09_add_junit.patch --- pom.xml | 19 +++++++ .../ru/javawebinar/topjava/UserTestData.java | 13 +++++ .../user/InMemoryAdminRestControllerTest.java | 52 +++++++++++++++++++ .../ru/javawebinar/topjava/SpringMain.java | 0 .../inmemory/InMemoryBaseRepository.java | 2 +- .../inmemory/InMemoryMealRepository.java | 4 +- .../inmemory/InMemoryUserRepository.java | 12 ++++- 7 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/UserTestData.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java rename src/{main => test}/java/ru/javawebinar/topjava/SpringMain.java (100%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java (92%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java (94%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java (73%) diff --git a/pom.xml b/pom.xml index f490543f..2a01bbd7 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,9 @@ 1.2.3 1.7.30 + + + 4.13.2 @@ -36,6 +39,14 @@ ${java.version} + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + -Dfile.encoding=UTF-8 + + @@ -75,6 +86,14 @@ jstl 1.2 + + + + junit + junit + ${junit.version} + test + diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/main/java/ru/javawebinar/topjava/UserTestData.java new file mode 100644 index 00000000..f6dfa8cb --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/UserTestData.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; + +public class UserTestData { + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + public static final int NOT_FOUND = 10; + + public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", Role.USER); + public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN); +} diff --git a/src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java new file mode 100644 index 00000000..33c83774 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -0,0 +1,52 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.Arrays; + +import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; +import static ru.javawebinar.topjava.UserTestData.USER_ID; + +public class InMemoryAdminRestControllerTest { + private static final Logger log = LoggerFactory.getLogger(InMemoryAdminRestControllerTest.class); + + private static ConfigurableApplicationContext appCtx; + private static AdminRestController controller; + private static InMemoryUserRepository repository; + + @BeforeClass + public static void beforeClass() { + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + log.info("\n{}\n", Arrays.toString(appCtx.getBeanDefinitionNames())); + controller = appCtx.getBean(AdminRestController.class); + repository = appCtx.getBean(InMemoryUserRepository.class); + } + + @AfterClass + public static void afterClass() { + appCtx.close(); + } + + @Before + public void setUp() { + // re-initialize + repository.init(); + } + + @Test + public void delete() { + controller.delete(USER_ID); + Assert.assertNull(repository.get(USER_ID)); + } + + @Test + public void deleteNotFound() { + Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java similarity index 100% rename from src/main/java/ru/javawebinar/topjava/SpringMain.java rename to src/test/java/ru/javawebinar/topjava/SpringMain.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java similarity index 92% rename from src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java rename to src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java index aabc36aa..53bc445d 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java @@ -11,7 +11,7 @@ public class InMemoryBaseRepository { private static final AtomicInteger counter = new AtomicInteger(0); - private final Map map = new ConcurrentHashMap<>(); + final Map map = new ConcurrentHashMap<>(); public T save(T entry) { if (entry.isNew()) { diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java similarity index 94% rename from src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java rename to src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index b7587381..645869cb 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -20,8 +20,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository.ADMIN_ID; -import static ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository.USER_ID; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.USER_ID; @Repository public class InMemoryMealRepository implements MealRepository { diff --git a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java similarity index 73% rename from src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java rename to src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index f8968a81..eee90943 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.repository.inmemory; import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; @@ -8,11 +9,18 @@ import java.util.List; import java.util.stream.Collectors; +import static ru.javawebinar.topjava.UserTestData.admin; +import static ru.javawebinar.topjava.UserTestData.user; + + @Repository public class InMemoryUserRepository extends InMemoryBaseRepository implements UserRepository { - static final int USER_ID = 1; - static final int ADMIN_ID = 2; + public void init() { + map.clear(); + map.put(UserTestData.USER_ID, user); + map.put(UserTestData.ADMIN_ID, admin); + } @Override public List getAll() { From 4b6106c3abab902630043099dbaf97c1c7ceab7c Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:07:12 +0300 Subject: [PATCH 018/144] 3_10_add_spring_test.patch --- pom.xml | 6 +++ ...InMemoryAdminRestControllerSpringTest.java | 41 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java diff --git a/pom.xml b/pom.xml index 2a01bbd7..d93b1649 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,12 @@ ${junit.version} test + + org.springframework + spring-test + ${spring.version} + test + diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java new file mode 100644 index 00000000..afdffdfa --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -0,0 +1,41 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; +import static ru.javawebinar.topjava.UserTestData.USER_ID; + +@ContextConfiguration("classpath:spring/spring-app.xml") +@RunWith(SpringRunner.class) +public class InMemoryAdminRestControllerSpringTest { + + @Autowired + private AdminRestController controller; + + @Autowired + private InMemoryUserRepository repository; + + @Before + public void setUp() { + repository.init(); + } + + @Test + public void delete() { + controller.delete(USER_ID); + Assert.assertNull(repository.get(USER_ID)); + } + + @Test + public void deleteNotFound() { + Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + } +} \ No newline at end of file From f2abcfb6642abc171512c24e44071bb54254c785 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 17:16:49 +0300 Subject: [PATCH 019/144] 3_11_add_postgresql.patch --- pom.xml | 9 +++++++++ src/main/resources/db/postgres.properties | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/resources/db/postgres.properties diff --git a/pom.xml b/pom.xml index d93b1649..5e536a9d 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,8 @@ 1.2.3 1.7.30 + + 42.2.18 4.13.2 @@ -73,6 +75,13 @@ ${spring.version} + + + org.postgresql + postgresql + ${postgresql.version} + + javax.servlet diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties new file mode 100644 index 00000000..a3dcb933 --- /dev/null +++ b/src/main/resources/db/postgres.properties @@ -0,0 +1,7 @@ +#database.url=jdbc:postgresql://ec2-54-247-74-197.eu-west-1.compute.amazonaws.com:5432/de4fjsqhdvl7ld?ssl=true&sslmode=require&sslfactory=org.postgresql.ssl.NonValidatingFactory +#database.username=anbxkjtzukqacj +#database.password=da1f25b2a38784fb0d46858e5b8fc168e08c9e1e9c72faea5bbac9c0e1f9c24f + +database.url=jdbc:postgresql://localhost:5432/topjava +database.username=user +database.password=password From 0defb11ba13a4b321367b7d22b151cafab3d41c9 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 17:37:06 +0300 Subject: [PATCH 020/144] 3_12_db_implementation.patch --- pom.xml | 5 ++ .../repository/jdbc/JdbcUserRepository.java | 81 +++++++++++++++++++ src/main/resources/db/initDB.sql | 25 ++++++ src/main/resources/db/populateDB.sql | 11 +++ src/main/resources/db/postgres.properties | 2 +- src/main/resources/spring/spring-db.xml | 25 ++++++ 6 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java create mode 100644 src/main/resources/db/initDB.sql create mode 100644 src/main/resources/db/populateDB.sql create mode 100644 src/main/resources/spring/spring-db.xml diff --git a/pom.xml b/pom.xml index 5e536a9d..7ec0e738 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,11 @@ spring-context ${spring.version} + + org.springframework + spring-jdbc + ${spring.version} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java new file mode 100644 index 00000000..7f6f8626 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -0,0 +1,81 @@ +package ru.javawebinar.topjava.repository.jdbc; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.support.DataAccessUtils; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.List; + +@Repository +public class JdbcUserRepository implements UserRepository { + + private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); + + private final JdbcTemplate jdbcTemplate; + + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private final SimpleJdbcInsert insertUser; + + @Autowired + public JdbcUserRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + this.insertUser = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("users") + .usingGeneratedKeyColumns("id"); + + this.jdbcTemplate = jdbcTemplate; + this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; + } + + @Override + public User save(User user) { + MapSqlParameterSource map = new MapSqlParameterSource() + .addValue("id", user.getId()) + .addValue("name", user.getName()) + .addValue("email", user.getEmail()) + .addValue("password", user.getPassword()) + .addValue("registered", user.getRegistered()) + .addValue("enabled", user.isEnabled()) + .addValue("caloriesPerDay", user.getCaloriesPerDay()); + + if (user.isNew()) { + Number newKey = insertUser.executeAndReturnKey(map); + user.setId(newKey.intValue()); + } else if (namedParameterJdbcTemplate.update( + "UPDATE users SET name=:name, email=:email, password=:password, " + + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", map) == 0) { + return null; + } + return user; + } + + @Override + public boolean delete(int id) { + return jdbcTemplate.update("DELETE FROM users WHERE id=?", id) != 0; + } + + @Override + public User get(int id) { + List users = jdbcTemplate.query("SELECT * FROM users WHERE id=?", ROW_MAPPER, id); + return DataAccessUtils.singleResult(users); + } + + @Override + public User getByEmail(String email) { +// return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); + List users = jdbcTemplate.query("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); + return DataAccessUtils.singleResult(users); + } + + @Override + public List getAll() { + return jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + } +} diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql new file mode 100644 index 00000000..57f3f2c0 --- /dev/null +++ b/src/main/resources/db/initDB.sql @@ -0,0 +1,25 @@ +DROP TABLE IF EXISTS user_roles; +DROP TABLE IF EXISTS users; +DROP SEQUENCE IF EXISTS global_seq; + +CREATE SEQUENCE global_seq START WITH 100000; + +CREATE TABLE users +( + id INTEGER PRIMARY KEY DEFAULT nextval('global_seq'), + name VARCHAR NOT NULL, + email VARCHAR NOT NULL, + password VARCHAR NOT NULL, + registered TIMESTAMP DEFAULT now() NOT NULL, + enabled BOOL DEFAULT TRUE NOT NULL, + calories_per_day INTEGER DEFAULT 2000 NOT NULL +); +CREATE UNIQUE INDEX users_unique_email_idx ON users (email); + +CREATE TABLE user_roles +( + user_id INTEGER NOT NULL, + role VARCHAR, + CONSTRAINT user_roles_idx UNIQUE (user_id, role), + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql new file mode 100644 index 00000000..8e083f96 --- /dev/null +++ b/src/main/resources/db/populateDB.sql @@ -0,0 +1,11 @@ +DELETE FROM user_roles; +DELETE FROM users; +ALTER SEQUENCE global_seq RESTART WITH 100000; + +INSERT INTO users (name, email, password) +VALUES ('User', 'user@yandex.ru', 'password'), + ('Admin', 'admin@gmail.com', 'admin'); + +INSERT INTO user_roles (role, user_id) +VALUES ('USER', 100000), + ('ADMIN', 100001); diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index a3dcb933..7b2f704a 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -4,4 +4,4 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user -database.password=password +database.password=user diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml new file mode 100644 index 00000000..8ec56c9e --- /dev/null +++ b/src/main/resources/spring/spring-db.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 7664ea8d3d7b301bad0d30dcc8529b214c7c65a9 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:26:22 +0300 Subject: [PATCH 021/144] 3_13_test_UserService.patch --- pom.xml | 7 ++ .../ru/javawebinar/topjava/UserTestData.java | 38 +++++++- .../topjava/model/AbstractBaseEntity.java | 22 +++++ .../topjava/model/AbstractNamedEntity.java | 3 + .../ru/javawebinar/topjava/model/User.java | 25 ++++-- .../repository/jdbc/JdbcMealRepository.java | 37 ++++++++ src/main/resources/spring/spring-app.xml | 2 +- .../inmemory/InMemoryBaseRepository.java | 4 +- .../topjava/service/UserServiceTest.java | 88 +++++++++++++++++++ 9 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java diff --git a/pom.xml b/pom.xml index 7ec0e738..c20fe4f5 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ 42.2.18 4.13.2 + 3.19.0 @@ -114,6 +115,12 @@ ${spring.version} test + + org.assertj + assertj-core + ${assertj.version} + test + diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/main/java/ru/javawebinar/topjava/UserTestData.java index f6dfa8cb..81494197 100644 --- a/src/main/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/main/java/ru/javawebinar/topjava/UserTestData.java @@ -3,11 +3,45 @@ import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; + public class UserTestData { - public static final int USER_ID = 1; - public static final int ADMIN_ID = 2; + public static final int USER_ID = START_SEQ; + public static final int ADMIN_ID = START_SEQ + 1; public static final int NOT_FOUND = 10; public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", Role.USER); public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN); + + public static User getNew() { + return new User(null, "New", "new@gmail.com", "newPass", 1555, false, new Date(), Collections.singleton(Role.USER)); + } + + public static User getUpdated() { + User updated = new User(user); + updated.setEmail("update@gmail.com"); + updated.setName("UpdatedName"); + updated.setCaloriesPerDay(330); + updated.setPassword("newPass"); + updated.setEnabled(false); + updated.setRoles(Collections.singletonList(Role.ADMIN)); + return updated; + } + + public static void assertMatch(User actual, User expected) { + assertThat(actual).usingRecursiveComparison().ignoringFields("registered", "roles").isEqualTo(expected); + } + + public static void assertMatch(Iterable actual, User... expected) { + assertMatch(actual, Arrays.asList(expected)); + } + + public static void assertMatch(Iterable actual, Iterable expected) { + assertThat(actual).usingElementComparatorIgnoringFields("registered", "roles").isEqualTo(expected); + } } diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 8f27c902..46e8d6e3 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,8 +1,13 @@ package ru.javawebinar.topjava.model; public abstract class AbstractBaseEntity { + public static final int START_SEQ = 100000; + protected Integer id; + public AbstractBaseEntity() { + } + protected AbstractBaseEntity(Integer id) { this.id = id; } @@ -23,4 +28,21 @@ public boolean isNew() { public String toString() { return getClass().getSimpleName() + ":" + id; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractBaseEntity that = (AbstractBaseEntity) o; + return id != null && id.equals(that.id); + } + + @Override + public int hashCode() { + return id == null ? 0 : id; + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java index 2054a3d3..5f0fde2b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java @@ -4,6 +4,9 @@ public abstract class AbstractNamedEntity extends AbstractBaseEntity { protected String name; + public AbstractNamedEntity() { + } + protected AbstractNamedEntity(Integer id, String name) { super(id); this.name = name; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index d88e3819..446d86ff 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,5 +1,8 @@ package ru.javawebinar.topjava.model; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; import java.util.Date; import java.util.EnumSet; import java.util.Set; @@ -20,17 +23,25 @@ public class User extends AbstractNamedEntity { private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; + public User() { + } + + public User(User u) { + this(u.getId(), u.getName(), u.getEmail(), u.getPassword(), u.getCaloriesPerDay(), u.isEnabled(), u.getRegistered(), u.getRoles()); + } + public User(Integer id, String name, String email, String password, Role role, Role... roles) { - this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); + this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, new Date(), EnumSet.of(role, roles)); } - public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) { + public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Date registered, Collection roles) { super(id, name); this.email = email; this.password = password; this.caloriesPerDay = caloriesPerDay; this.enabled = enabled; - this.roles = roles; + this.registered = registered; + setRoles(roles); } public String getEmail() { @@ -77,15 +88,19 @@ public String getPassword() { return password; } + public void setRoles(Collection roles) { + this.roles = CollectionUtils.isEmpty(roles) ? EnumSet.noneOf(Role.class) : EnumSet.copyOf(roles); + } + @Override public String toString() { - return "User (" + + return "User{" + "id=" + id + ", email=" + email + ", name=" + name + ", enabled=" + enabled + ", roles=" + roles + ", caloriesPerDay=" + caloriesPerDay + - ')'; + '}'; } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java new file mode 100644 index 00000000..c29288c3 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -0,0 +1,37 @@ +package ru.javawebinar.topjava.repository.jdbc; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public class JdbcMealRepository implements MealRepository { + + @Override + public Meal save(Meal meal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public Meal get(int id, int userId) { + return null; + } + + @Override + public List getAll(int userId) { + return null; + } + + @Override + public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return null; + } +} diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index cac42ba1..4c05aea4 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -9,7 +9,7 @@ --> - + diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java index 53bc445d..317bbcab 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java @@ -7,9 +7,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; + public class InMemoryBaseRepository { - private static final AtomicInteger counter = new AtomicInteger(0); + private static final AtomicInteger counter = new AtomicInteger(START_SEQ); final Map map = new ConcurrentHashMap<>(); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java new file mode 100644 index 00000000..6a8783b1 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -0,0 +1,88 @@ +package ru.javawebinar.topjava.service; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +import static org.junit.Assert.assertThrows; +import static ru.javawebinar.topjava.UserTestData.*; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringRunner.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +public class UserServiceTest { + + @Autowired + private UserService service; + + @Test + public void create() { + User newUser = getNew(); + User created = service.create(newUser); + Integer newId = created.getId(); + newUser.setId(newId); + assertMatch(created, newUser); + assertMatch(service.get(newId), newUser); + } + + @Test + public void duplicateMailCreate() { + assertThrows(DataAccessException.class, () -> + service.create(new User(null, "Duplicate", "user@yandex.ru", "newPass", Role.USER))); + } + + @Test + public void delete() { + service.delete(USER_ID); + assertThrows(NotFoundException.class, () -> service.get(USER_ID)); + } + + @Test + public void deletedNotFound() { + assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND)); + } + + @Test + public void get() { + User user = service.get(USER_ID); + assertMatch(user, UserTestData.user); + } + + @Test + public void getNotFound() { + assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND)); + } + + @Test + public void getByEmail() { + User user = service.getByEmail("admin@gmail.com"); + assertMatch(user, admin); + } + + @Test + public void update() { + User updated = getUpdated(); + service.update(updated); + assertMatch(service.get(USER_ID), updated); + } + + @Test + public void getAll() { + List all = service.getAll(); + assertMatch(all, admin, user); + } +} \ No newline at end of file From f3e1215d7cf0e43452fb90c0d66a864d0aaeba9d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:51:56 +0300 Subject: [PATCH 022/144] 3_14_test_logging.patch --- pom.xml | 7 ++++++ src/main/resources/logback.xml | 4 ++-- .../topjava/service/UserServiceTest.java | 7 ++++++ src/test/resources/logback-test.xml | 22 +++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index c20fe4f5..1ce2043b 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,13 @@ compile + + org.slf4j + jul-to-slf4j + ${slf4j.version} + runtime + + ch.qos.logback logback-classic diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e9b900b2..c7bffc3a 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -9,14 +9,14 @@ UTF-8 - %date %-5level %logger{0} [%file:%line] %msg%n + %date %-5level %logger{50}.%M:%L - %msg%n UTF-8 - %-5level %logger{0} [%file:%line] %msg%n + %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index 6a8783b1..50163eeb 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -2,6 +2,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; @@ -26,6 +27,12 @@ @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class UserServiceTest { + static { + // Only for postgres driver logging + // It uses java.util.logging and logged via jul-to-slf4j bridge + SLF4JBridgeHandler.install(); + } + @Autowired private UserService service; diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 00000000..428ceca2 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,22 @@ + + + + true + + + + + UTF-8 + %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n + + + + + + + + + + + + \ No newline at end of file From dcb4c3abb2f87ae528c2fb7fb2f5e3460fc5b982 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:52:38 +0300 Subject: [PATCH 023/144] 3_14_test_logging.patch2 --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 1ce2043b..6e174e31 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,12 @@ ${assertj.version} test + + org.assertj + assertj-core + 3.19.0 + compile + From 35e60080cf05747e44072c3f5b87e051f9ecd918 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:55:27 +0300 Subject: [PATCH 024/144] 3_15_fix_servlet.patch --- src/main/java/ru/javawebinar/topjava/web/MealServlet.java | 2 +- src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java | 4 +++- src/main/webapp/index.html | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 39868d2e..19c187ba 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -27,7 +27,7 @@ public class MealServlet extends HttpServlet { @Override public void init() { - springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/spring-db.xml"); mealController = springContext.getBean(MealRestController.class); } diff --git a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java index b9639bf1..58821754 100644 --- a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java +++ b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java @@ -1,10 +1,12 @@ package ru.javawebinar.topjava.web; +import ru.javawebinar.topjava.model.AbstractBaseEntity; + import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; public class SecurityUtil { - private static int id = 1; + private static int id = AbstractBaseEntity.START_SEQ; public static int authUserId() { return id; diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index c476d1ed..ce44f5b0 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -9,8 +9,8 @@

    Проект Meals of  From 4be406cd2ce356794e99a100409ca60c5030e922 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:56:35 +0300 Subject: [PATCH 025/144] 3_15_fix_servlet.patch2 --- src/main/java/ru/javawebinar/topjava/web/MealServlet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 19c187ba..a8f60993 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -46,9 +46,9 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) Integer.parseInt(request.getParameter("calories"))); if (StringUtils.hasLength(request.getParameter("id"))) { - mealController.create(meal); - } else { mealController.update(meal, getId(request)); + } else { + mealController.create(meal); } response.sendRedirect("meals"); } From 98dd227f0cb06649726a9b4541c4fe1cec11a84a Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Fri, 19 Feb 2021 08:45:06 +0300 Subject: [PATCH 026/144] 3_16_fix_junit.patch --- .../ru/javawebinar/topjava/service/UserServiceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index 50163eeb..ba3275eb 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -38,9 +38,9 @@ public class UserServiceTest { @Test public void create() { - User newUser = getNew(); - User created = service.create(newUser); + User created = service.create(getNew()); Integer newId = created.getId(); + User newUser = getNew(); newUser.setId(newId); assertMatch(created, newUser); assertMatch(service.get(newId), newUser); @@ -84,7 +84,7 @@ public void getByEmail() { public void update() { User updated = getUpdated(); service.update(updated); - assertMatch(service.get(USER_ID), updated); + assertMatch(service.get(USER_ID), getUpdated()); } @Test From f25861192cc86374233822b793dfbe17126b5fb3 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 09:46:01 +0300 Subject: [PATCH 027/144] 4_0_fix.patch --- lib/javax.annotation.jar | Bin 0 -> 7713 bytes lib/javax.ejb.jar | Bin 0 -> 47581 bytes lib/javax.jms.jar | Bin 0 -> 25957 bytes lib/javax.persistence.jar | Bin 0 -> 129793 bytes lib/javax.resource.jar | Bin 0 -> 44511 bytes lib/javax.servlet.jsp.jar | Bin 0 -> 78836 bytes lib/javax.transaction.jar | Bin 0 -> 9714 bytes pom.xml | 6 ------ .../inmemory/InMemoryBaseRepository.java | 18 +++++++++++------- .../inmemory/InMemoryUserRepository.java | 5 +++-- .../user/InMemoryAdminRestControllerTest.java | 0 11 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 lib/javax.annotation.jar create mode 100644 lib/javax.ejb.jar create mode 100644 lib/javax.jms.jar create mode 100644 lib/javax.persistence.jar create mode 100644 lib/javax.resource.jar create mode 100644 lib/javax.servlet.jsp.jar create mode 100644 lib/javax.transaction.jar rename src/{main => test}/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java (100%) diff --git a/lib/javax.annotation.jar b/lib/javax.annotation.jar new file mode 100644 index 0000000000000000000000000000000000000000..52dca7f561c5d579ca764cb25b96d3f539cf242a GIT binary patch literal 7713 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 ayGQqbfd2}7!o6gSEfc3Ot66$1-hTi%8LHX< literal 0 HcmV?d00001 diff --git a/lib/javax.ejb.jar b/lib/javax.ejb.jar new file mode 100644 index 0000000000000000000000000000000000000000..4ebf5ecd4c9ab83540ef206f6c5a1906db008098 GIT binary patch literal 47581 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

    x$jzCx>QKI9Dc;_z4e z0Yjof!jtYPhbWx5hKGB$%LhfCBm1`-iGRzk7{pT4|G=z*;ASA7z{7XMS%+kLwXk%W>j7B@a_J!C@WeN1+*~V>{Bl#-HQiNtX^sL^z#~Mk&VLmX>zsDS*--Z5!6_ zmyRl7s(i+;QDxn)?`n6UYg4?s_nq&1alMX7Ku2!YsIXC8I_2ltv;oQhymtI+SybF} z&5%yV8Fnis+qSDJR969o0ePgX?>;nd8ilT!8EO$W(t2lKb^a@_Kv!Z*Qr`py`u0g3 zZbg(P;3`80t34!4MxMUfAtf5-l2pfc`~4o`Nn>PHWMZ{b^}Tq!IwLnS=D7Q@nP_AJ z8sVco2#(CK5g9n>xsoML==2oKhcCtLFy{?{xX#>Z&lk>Rso+VQP2bI;*p#A39_3ua z3zya53Tr%pUuzV&vlWDW1X|$xfqs}%)d9;}8Om^7+L!id9t6GxKHtOdzTXeqV9Jx! z3tmZxybfD?S*MPdg8Yyzxeny(r4u#+Pi{N;939nD8jYt&PnrFIsp6>KC+OLmrLq&S^b}zlDjCytmd7*0xc^f^MC9@ z=&NP_ano*sGCn+17@8=dpTx`IX{m-AX2O1xKfI3v*rmYo7&K&ngiW$r9mr_rx)sM_ z#Oz=4Vv`zRZpXu&Mmb(!)N6q=g+V9i!6~U(QvqD8>wnK=8H%^MaQx|0(^ETQWB?(j z)>@rFuE&URSsM)!vFUiB*5>)ainsa+K~=f=`~V@-tbQ7awxjYeWi7nicAh733(G?(In)J1z$v! zL)gO*mdA8wN2rh$t0s*R_F)t71Fc-W$DmA4%YZ5OBX?nfnfoN)+l~Aka750IO#Bo~ zZe&XByG|qJYr-bX^f>*UgsC_FPBhVcu zE?M4|@Ruv)k2hBFW{aTLN~uCB#iXo{tcREvLbp5b!I5W6lPQIXmT&ztMU$l&mh67# zAVy>wOf`Z@GYN~<9~>2ZQ)I%iXWE?%z?cj|Q{04$##f^$+D6A`k9Np#ZT=(obE*6&ga+De#a*1}r|Tm%hJ3*HK%S{$iBTjr6E zcRAkU5^&yPn!YG)RYb1Mp?J?nfnrr6wM5#lYRlUNv%b0z1qno-+KO5py~U(0-o|J7 zZG>rh9+nU6g?vloX{oe!XiIW&JI|RXQKd3)S|&LDu#KENkxJ{^hzwD-$szetxtXs;FE fq85Hs^+j9}Vd)!)Er7{XjDPYXacZ#z&d&6IINz)K literal 0 HcmV?d00001 diff --git a/lib/javax.resource.jar b/lib/javax.resource.jar new file mode 100644 index 0000000000000000000000000000000000000000..696a2345878907025784d2e6e49c6e6b41d1cfac GIT binary patch literal 44511 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 Rg4PB8C6U3vu!Mnr`hR(p1f~E0 literal 0 HcmV?d00001 diff --git a/lib/javax.servlet.jsp.jar b/lib/javax.servlet.jsp.jar new file mode 100644 index 0000000000000000000000000000000000000000..9c0631cea0fd56031db19fc3edbb4db4bfbb923a GIT binary patch literal 78836 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.version} test - - org.assertj - assertj-core - 3.19.0 - compile - diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java index 317bbcab..41e70701 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java @@ -11,17 +11,17 @@ public class InMemoryBaseRepository { - private static final AtomicInteger counter = new AtomicInteger(START_SEQ); + static final AtomicInteger counter = new AtomicInteger(START_SEQ); final Map map = new ConcurrentHashMap<>(); - public T save(T entry) { - if (entry.isNew()) { - entry.setId(counter.incrementAndGet()); - map.put(entry.getId(), entry); - return entry; + public T save(T entity) { + if (entity.isNew()) { + entity.setId(counter.getAndIncrement()); + map.put(entity.getId(), entity); + return entity; } - return map.computeIfPresent(entry.getId(), (id, oldT) -> entry); + return map.computeIfPresent(entity.getId(), (id, oldT) -> entity); } public boolean delete(int id) { @@ -35,4 +35,8 @@ public T get(int id) { Collection getCollection() { return map.values(); } + + void put(T entity) { + map.put(entity.getId(), entity); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index eee90943..d79817af 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -18,8 +18,9 @@ public class InMemoryUserRepository extends InMemoryBaseRepository impleme public void init() { map.clear(); - map.put(UserTestData.USER_ID, user); - map.put(UserTestData.ADMIN_ID, admin); + put(user); + put(admin); + counter.getAndSet(UserTestData.ADMIN_ID + 1); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java similarity index 100% rename from src/main/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java rename to src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java From bf40d84f6170186884edd5def987f24b1f8003aa Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 09:47:18 +0300 Subject: [PATCH 028/144] 4_1_HW3.patch --- .../ru/javawebinar/topjava/model/Meal.java | 21 ++++++- .../repository/jdbc/JdbcMealRepository.java | 59 +++++++++++++++++-- .../repository/jdbc/JdbcUserRepository.java | 15 ++--- src/main/resources/db/initDB.sql | 14 ++++- src/main/resources/db/populateDB.sql | 12 ++++ 5 files changed, 101 insertions(+), 20 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 9eed15f7..1c1e67b1 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -5,11 +5,14 @@ import java.time.LocalTime; public class Meal extends AbstractBaseEntity { - private final LocalDateTime dateTime; + private LocalDateTime dateTime; - private final String description; + private String description; - private final int calories; + private int calories; + + public Meal() { + } public Meal(LocalDateTime dateTime, String description, int calories) { this(null, dateTime, description, calories); @@ -42,6 +45,18 @@ public LocalTime getTime() { return dateTime.toLocalTime(); } + public void setDateTime(LocalDateTime dateTime) { + this.dateTime = dateTime; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setCalories(int calories) { + this.calories = calories; + } + @Override public String toString() { return "Meal{" + diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index c29288c3..fa26d566 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -1,5 +1,13 @@ package ru.javawebinar.topjava.repository.jdbc; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.support.DataAccessUtils; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -10,28 +18,69 @@ @Repository public class JdbcMealRepository implements MealRepository { + private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); + + private final JdbcTemplate jdbcTemplate; + + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private final SimpleJdbcInsert insertMeal; + + @Autowired + public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + this.insertMeal = new SimpleJdbcInsert(jdbcTemplate) + .withTableName("meals") + .usingGeneratedKeyColumns("id"); + + this.jdbcTemplate = jdbcTemplate; + this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; + } + @Override public Meal save(Meal meal, int userId) { - return null; + MapSqlParameterSource map = new MapSqlParameterSource() + .addValue("id", meal.getId()) + .addValue("description", meal.getDescription()) + .addValue("calories", meal.getCalories()) + .addValue("date_time", meal.getDateTime()) + .addValue("user_id", userId); + + if (meal.isNew()) { + Number newId = insertMeal.executeAndReturnKey(map); + meal.setId(newId.intValue()); + } else { + if (namedParameterJdbcTemplate.update("" + + "UPDATE meals " + + " SET description=:description, calories=:calories, date_time=:date_time " + + " WHERE id=:id AND user_id=:user_id", map) == 0) { + return null; + } + } + return meal; } @Override public boolean delete(int id, int userId) { - return false; + return jdbcTemplate.update("DELETE FROM meals WHERE id=? AND user_id=?", id, userId) != 0; } @Override public Meal get(int id, int userId) { - return null; + List meals = jdbcTemplate.query( + "SELECT * FROM meals WHERE id = ? AND user_id = ?", ROW_MAPPER, id, userId); + return DataAccessUtils.singleResult(meals); } @Override public List getAll(int userId) { - return null; + return jdbcTemplate.query( + "SELECT * FROM meals WHERE user_id=? ORDER BY date_time DESC", ROW_MAPPER, userId); } @Override public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - return null; + return jdbcTemplate.query( + "SELECT * FROM meals WHERE user_id=? AND date_time >= ? AND date_time < ? ORDER BY date_time DESC", + ROW_MAPPER, userId, startDateTime, endDateTime); } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java index 7f6f8626..412bfbeb 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -4,7 +4,7 @@ import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; @@ -36,21 +36,14 @@ public JdbcUserRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate @Override public User save(User user) { - MapSqlParameterSource map = new MapSqlParameterSource() - .addValue("id", user.getId()) - .addValue("name", user.getName()) - .addValue("email", user.getEmail()) - .addValue("password", user.getPassword()) - .addValue("registered", user.getRegistered()) - .addValue("enabled", user.isEnabled()) - .addValue("caloriesPerDay", user.getCaloriesPerDay()); + BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user); if (user.isNew()) { - Number newKey = insertUser.executeAndReturnKey(map); + Number newKey = insertUser.executeAndReturnKey(parameterSource); user.setId(newKey.intValue()); } else if (namedParameterJdbcTemplate.update( "UPDATE users SET name=:name, email=:email, password=:password, " + - "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", map) == 0) { + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", parameterSource) == 0) { return null; } return user; diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql index 57f3f2c0..af0ff307 100644 --- a/src/main/resources/db/initDB.sql +++ b/src/main/resources/db/initDB.sql @@ -1,4 +1,5 @@ DROP TABLE IF EXISTS user_roles; +DROP TABLE IF EXISTS meals; DROP TABLE IF EXISTS users; DROP SEQUENCE IF EXISTS global_seq; @@ -22,4 +23,15 @@ CREATE TABLE user_roles role VARCHAR, CONSTRAINT user_roles_idx UNIQUE (user_id, role), FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE -); \ No newline at end of file +); + +CREATE TABLE meals +( + id INTEGER PRIMARY KEY DEFAULT nextval('global_seq'), + user_id INTEGER NOT NULL, + date_time TIMESTAMP NOT NULL, + description TEXT NOT NULL, + calories INT NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX meals_unique_user_datetime_idx ON meals (user_id, date_time); \ No newline at end of file diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index 8e083f96..a6c79fb9 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -1,4 +1,5 @@ DELETE FROM user_roles; +DELETE FROM meals; DELETE FROM users; ALTER SEQUENCE global_seq RESTART WITH 100000; @@ -9,3 +10,14 @@ VALUES ('User', 'user@yandex.ru', 'password'), INSERT INTO user_roles (role, user_id) VALUES ('USER', 100000), ('ADMIN', 100001); + +INSERT INTO meals (date_time, description, calories, user_id) +VALUES ('2020-01-30 10:00:00', 'Завтрак', 500, 100000), + ('2020-01-30 13:00:00', 'Обед', 1000, 100000), + ('2020-01-30 20:00:00', 'Ужин', 500, 100000), + ('2020-01-31 0:00:00', 'Еда на граничное значение', 100, 100000), + ('2020-01-31 10:00:00', 'Завтрак', 500, 100000), + ('2020-01-31 13:00:00', 'Обед', 1000, 100000), + ('2020-01-31 20:00:00', 'Ужин', 510, 100000), + ('2020-01-31 14:00:00', 'Админ ланч', 510, 100001), + ('2020-01-31 21:00:00', 'Админ ужин', 1500, 100001); \ No newline at end of file From f20a49184e74dcb01d598bff6c9ba3649f0f8bfd Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 09:56:24 +0300 Subject: [PATCH 029/144] 4_2_HW3_optional.patch --- .../javawebinar/topjava/util/MealsUtil.java | 13 -- .../ru/javawebinar/topjava/MealTestData.java | 50 ++++++++ .../inmemory/InMemoryMealRepository.java | 13 +- .../topjava/service/MealServiceTest.java | 117 ++++++++++++++++++ 4 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/MealTestData.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 37923f74..2d604bed 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -4,10 +4,7 @@ import ru.javawebinar.topjava.to.MealTo; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.Month; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -17,16 +14,6 @@ public class MealsUtil { public static final int DEFAULT_CALORIES_PER_DAY = 2000; - public static final List meals = Arrays.asList( - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 1000), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 13, 0), "Обед", 500), - new Meal(LocalDateTime.of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 410) - ); - public static List getTos(Collection meals, int caloriesPerDay) { return filterByPredicate(meals, caloriesPerDay, meal -> true); } diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java new file mode 100644 index 00000000..60d18376 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -0,0 +1,50 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.model.Meal; + +import java.time.Month; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; + +import static java.time.LocalDateTime.of; +import static org.assertj.core.api.Assertions.assertThat; +import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; + +public class MealTestData { + public static final int NOT_FOUND = 10; + public static final int MEAL1_ID = START_SEQ + 2; + public static final int ADMIN_MEAL_ID = START_SEQ + 9; + + public static final Meal meal1 = new Meal(MEAL1_ID, of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500); + public static final Meal meal2 = new Meal(MEAL1_ID + 1, of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000); + public static final Meal meal3 = new Meal(MEAL1_ID + 2, of(2020, Month.JANUARY, 30, 20, 0), "Ужин", 500); + public static final Meal meal4 = new Meal(MEAL1_ID + 3, of(2020, Month.JANUARY, 31, 0, 0), "Еда на граничное значение", 100); + public static final Meal meal5 = new Meal(MEAL1_ID + 4, of(2020, Month.JANUARY, 31, 10, 0), "Завтрак", 500); + public static final Meal meal6 = new Meal(MEAL1_ID + 5, of(2020, Month.JANUARY, 31, 13, 0), "Обед", 1000); + public static final Meal meal7 = new Meal(MEAL1_ID + 6, of(2020, Month.JANUARY, 31, 20, 0), "Ужин", 510); + public static final Meal adminMeal1 = new Meal(ADMIN_MEAL_ID, of(2020, Month.JANUARY, 31, 14, 0), "Админ ланч", 510); + public static final Meal adminMeal2 = new Meal(ADMIN_MEAL_ID + 1, of(2020, Month.JANUARY, 31, 21, 0), "Админ ужин", 1500); + + public static final List meals = Arrays.asList(meal7, meal6, meal5, meal4, meal3, meal2, meal1); + + public static Meal getNew() { + return new Meal(null, of(2020, Month.FEBRUARY, 1, 18, 0), "Созданный ужин", 300); + } + + public static Meal getUpdated() { + return new Meal(MEAL1_ID, meal1.getDateTime().plus(2, ChronoUnit.MINUTES), "Обновленный завтрак", 200); + } + + public static void assertMatch(Meal actual, Meal expected) { + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + public static void assertMatch(Iterable actual, Meal... expected) { + assertMatch(actual, Arrays.asList(expected)); + } + + public static void assertMatch(Iterable actual, Iterable expected) { + assertThat(actual).usingFieldByFieldElementComparator().isEqualTo(expected); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 645869cb..f56cef32 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -3,15 +3,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.MealTestData; +import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.util.MealsUtil; import ru.javawebinar.topjava.util.Util; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.time.LocalDateTime; -import java.time.Month; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -20,9 +20,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; -import static ru.javawebinar.topjava.UserTestData.USER_ID; - @Repository public class InMemoryMealRepository implements MealRepository { private static final Logger log = LoggerFactory.getLogger(InMemoryMealRepository.class); @@ -31,9 +28,9 @@ public class InMemoryMealRepository implements MealRepository { private final Map> usersMealsMap = new ConcurrentHashMap<>(); { - MealsUtil.meals.forEach(meal -> save(meal, USER_ID)); - save(new Meal(LocalDateTime.of(2015, Month.JUNE, 1, 14, 0), "Админ ланч", 510), ADMIN_ID); - save(new Meal(LocalDateTime.of(2015, Month.JUNE, 1, 21, 0), "Админ ужин", 1500), ADMIN_ID); + InMemoryBaseRepository userMeals = new InMemoryBaseRepository<>(); + MealTestData.meals.forEach(userMeals::put); + usersMealsMap.put(UserTestData.USER_ID, userMeals); } diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java new file mode 100644 index 00000000..26278060 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -0,0 +1,117 @@ +package ru.javawebinar.topjava.service; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.bridge.SLF4JBridgeHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.time.LocalDate; +import java.time.Month; + +import static org.junit.Assert.assertThrows; +import static ru.javawebinar.topjava.MealTestData.*; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.USER_ID; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringRunner.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +public class MealServiceTest { + + static { + SLF4JBridgeHandler.install(); + } + + @Autowired + private MealService service; + + @Test + public void delete() { + service.delete(MEAL1_ID, USER_ID); + assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, USER_ID)); + } + + @Test + public void deleteNotFound() { + assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND, USER_ID)); + } + + @Test + public void deleteNotOwn() { + assertThrows(NotFoundException.class, () -> service.delete(MEAL1_ID, ADMIN_ID)); + } + + @Test + public void create() { + Meal created = service.create(getNew(), USER_ID); + int newId = created.getId(); + Meal newMeal = getNew(); + newMeal.setId(newId); + assertMatch(created, newMeal); + assertMatch(service.get(newId, USER_ID), newMeal); + } + + @Test + public void duplicateDateTimeCreate() { + assertThrows(DataAccessException.class, () -> + service.create(new Meal(null, meal1.getDateTime(), "duplicate", 100), USER_ID)); + } + + + @Test + public void get() { + Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); + assertMatch(actual, adminMeal1); + } + + @Test + public void getNotFound() { + assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND, USER_ID)); + } + + @Test + public void getNotOwn() { + assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, ADMIN_ID)); + } + + @Test + public void update() { + Meal updated = getUpdated(); + service.update(updated, USER_ID); + assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); + } + + @Test + public void updateNotOwn() { + assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); + assertMatch(service.get(MEAL1_ID, USER_ID), meal1); + } + + @Test + public void getAll() { + assertMatch(service.getAll(USER_ID), meals); + } + + @Test + public void getBetweenInclusive() { + assertMatch(service.getBetweenInclusive( + LocalDate.of(2020, Month.JANUARY, 30), + LocalDate.of(2020, Month.JANUARY, 30), USER_ID), + meal3, meal2, meal1); + } + + @Test + public void getBetweenWithNullDates() { + assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); + } +} \ No newline at end of file From 565290bdef2f9f18fa32ede0d9887d14613cd0e3 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 09:58:06 +0300 Subject: [PATCH 030/144] 4_3_tests_refactoring.patch --- .../ru/javawebinar/topjava/UserTestData.java | 16 ++-------- .../ru/javawebinar/topjava/MealTestData.java | 15 ++-------- .../ru/javawebinar/topjava/TestMatcher.java | 29 +++++++++++++++++++ .../topjava/service/MealServiceTest.java | 16 +++++----- .../topjava/service/UserServiceTest.java | 12 ++++---- 5 files changed, 47 insertions(+), 41 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/TestMatcher.java diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/main/java/ru/javawebinar/topjava/UserTestData.java index 81494197..b562b234 100644 --- a/src/main/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/main/java/ru/javawebinar/topjava/UserTestData.java @@ -3,14 +3,14 @@ import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import java.util.Arrays; import java.util.Collections; import java.util.Date; -import static org.assertj.core.api.Assertions.assertThat; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { + public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "roles"); + public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; public static final int NOT_FOUND = 10; @@ -32,16 +32,4 @@ public static User getUpdated() { updated.setRoles(Collections.singletonList(Role.ADMIN)); return updated; } - - public static void assertMatch(User actual, User expected) { - assertThat(actual).usingRecursiveComparison().ignoringFields("registered", "roles").isEqualTo(expected); - } - - public static void assertMatch(Iterable actual, User... expected) { - assertMatch(actual, Arrays.asList(expected)); - } - - public static void assertMatch(Iterable actual, Iterable expected) { - assertThat(actual).usingElementComparatorIgnoringFields("registered", "roles").isEqualTo(expected); - } } diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index 60d18376..e256b937 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -8,10 +8,11 @@ import java.util.List; import static java.time.LocalDateTime.of; -import static org.assertj.core.api.Assertions.assertThat; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class MealTestData { + public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator(); + public static final int NOT_FOUND = 10; public static final int MEAL1_ID = START_SEQ + 2; public static final int ADMIN_MEAL_ID = START_SEQ + 9; @@ -35,16 +36,4 @@ public static Meal getNew() { public static Meal getUpdated() { return new Meal(MEAL1_ID, meal1.getDateTime().plus(2, ChronoUnit.MINUTES), "Обновленный завтрак", 200); } - - public static void assertMatch(Meal actual, Meal expected) { - assertThat(actual).usingRecursiveComparison().isEqualTo(expected); - } - - public static void assertMatch(Iterable actual, Meal... expected) { - assertMatch(actual, Arrays.asList(expected)); - } - - public static void assertMatch(Iterable actual, Iterable expected) { - assertThat(actual).usingFieldByFieldElementComparator().isEqualTo(expected); - } } diff --git a/src/test/java/ru/javawebinar/topjava/TestMatcher.java b/src/test/java/ru/javawebinar/topjava/TestMatcher.java new file mode 100644 index 00000000..efd4203b --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/TestMatcher.java @@ -0,0 +1,29 @@ +package ru.javawebinar.topjava; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestMatcher { + private final String[] fieldsToIgnore; + + private TestMatcher(String... fieldsToIgnore) { + this.fieldsToIgnore = fieldsToIgnore; + } + + public static TestMatcher usingIgnoringFieldsComparator(String... fieldsToIgnore) { + return new TestMatcher<>(fieldsToIgnore); + } + + public void assertMatch(T actual, T expected) { + assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); + } + + public void assertMatch(Iterable actual, T... expected) { + assertMatch(actual, Arrays.asList(expected)); + } + + public void assertMatch(Iterable actual, Iterable expected) { + assertThat(actual).usingElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 26278060..26d0b3d6 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -57,8 +57,8 @@ public void create() { int newId = created.getId(); Meal newMeal = getNew(); newMeal.setId(newId); - assertMatch(created, newMeal); - assertMatch(service.get(newId, USER_ID), newMeal); + MEAL_MATCHER.assertMatch(created, newMeal); + MEAL_MATCHER.assertMatch(service.get(newId, USER_ID), newMeal); } @Test @@ -71,7 +71,7 @@ public void duplicateDateTimeCreate() { @Test public void get() { Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); - assertMatch(actual, adminMeal1); + MEAL_MATCHER.assertMatch(actual, adminMeal1); } @Test @@ -88,23 +88,23 @@ public void getNotOwn() { public void update() { Meal updated = getUpdated(); service.update(updated, USER_ID); - assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); + MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); } @Test public void updateNotOwn() { assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); - assertMatch(service.get(MEAL1_ID, USER_ID), meal1); + MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } @Test public void getAll() { - assertMatch(service.getAll(USER_ID), meals); + MEAL_MATCHER.assertMatch(service.getAll(USER_ID), meals); } @Test public void getBetweenInclusive() { - assertMatch(service.getBetweenInclusive( + MEAL_MATCHER.assertMatch(service.getBetweenInclusive( LocalDate.of(2020, Month.JANUARY, 30), LocalDate.of(2020, Month.JANUARY, 30), USER_ID), meal3, meal2, meal1); @@ -112,6 +112,6 @@ public void getBetweenInclusive() { @Test public void getBetweenWithNullDates() { - assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); + MEAL_MATCHER.assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index ba3275eb..2234f291 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -42,8 +42,8 @@ public void create() { Integer newId = created.getId(); User newUser = getNew(); newUser.setId(newId); - assertMatch(created, newUser); - assertMatch(service.get(newId), newUser); + USER_MATCHER.assertMatch(created, newUser); + USER_MATCHER.assertMatch(service.get(newId), newUser); } @Test @@ -66,7 +66,7 @@ public void deletedNotFound() { @Test public void get() { User user = service.get(USER_ID); - assertMatch(user, UserTestData.user); + USER_MATCHER.assertMatch(user, UserTestData.user); } @Test @@ -77,19 +77,19 @@ public void getNotFound() { @Test public void getByEmail() { User user = service.getByEmail("admin@gmail.com"); - assertMatch(user, admin); + USER_MATCHER.assertMatch(user, admin); } @Test public void update() { User updated = getUpdated(); service.update(updated); - assertMatch(service.get(USER_ID), getUpdated()); + USER_MATCHER.assertMatch(service.get(USER_ID), getUpdated()); } @Test public void getAll() { List all = service.getAll(); - assertMatch(all, admin, user); + USER_MATCHER.assertMatch(all, admin, user); } } \ No newline at end of file From d611a7c62f342755185436b4c9385a710383f1b5 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:01:51 +0300 Subject: [PATCH 031/144] refactoring --- src/{main => test}/java/ru/javawebinar/topjava/UserTestData.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main => test}/java/ru/javawebinar/topjava/UserTestData.java (100%) diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java similarity index 100% rename from src/main/java/ru/javawebinar/topjava/UserTestData.java rename to src/test/java/ru/javawebinar/topjava/UserTestData.java From ee5ecc36034b815892d2ae141e6b1a639810ab8a Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:02:42 +0300 Subject: [PATCH 032/144] 4_4_HW3_fix_logging.patch --- src/main/resources/spring/spring-db.xml | 9 +++++++++ .../ru/javawebinar/topjava/service/MealServiceTest.java | 5 ----- .../ru/javawebinar/topjava/service/UserServiceTest.java | 7 ------- src/test/resources/logback-test.xml | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 8ec56c9e..283fee39 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -6,6 +6,15 @@ + + + + + diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 26d0b3d6..e4b5dd91 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -2,7 +2,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; @@ -28,10 +27,6 @@ @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class MealServiceTest { - static { - SLF4JBridgeHandler.install(); - } - @Autowired private MealService service; diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index 2234f291..f62db0c9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -2,7 +2,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; @@ -27,12 +26,6 @@ @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class UserServiceTest { - static { - // Only for postgres driver logging - // It uses java.util.logging and logged via jul-to-slf4j bridge - SLF4JBridgeHandler.install(); - } - @Autowired private UserService service; diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 428ceca2..da4eed7b 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -12,7 +12,7 @@ - + From d1fff4cb265bf2044689fcc84c19798401711049 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:03:37 +0300 Subject: [PATCH 033/144] 4_5_improve_code.patch --- .codacy.yml | 5 ++++ .travis.yml | 26 +++++++++++++++++++ .../topjava/model/AbstractBaseEntity.java | 7 +++++ .../topjava/service/MealService.java | 5 +++- .../topjava/service/UserService.java | 6 ++++- .../topjava/util/DateTimeUtil.java | 3 +++ .../javawebinar/topjava/util/MealsUtil.java | 3 +++ .../ru/javawebinar/topjava/util/Util.java | 3 +++ .../topjava/util/ValidationUtil.java | 4 ++- .../javawebinar/topjava/web/SecurityUtil.java | 3 +++ .../inmemory/InMemoryBaseRepository.java | 5 +++- .../inmemory/InMemoryMealRepository.java | 6 ++--- .../inmemory/InMemoryUserRepository.java | 2 ++ .../topjava/service/MealServiceTest.java | 2 +- .../topjava/service/UserServiceTest.java | 2 +- 15 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 .codacy.yml create mode 100644 .travis.yml diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 00000000..311a8f4e --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,5 @@ +--- +exclude_paths: + - 'src/main/webapp/**' + - '**.md' + - '**.sql' \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..8f44cef8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +# https://docs.travis-ci.com/user/languages/java/ +language: java +jdk: openjdk8 + +#https://dzone.com/articles/travis-ci-tutorial-java-projects +cache: + directories: + - $HOME/.m2 + +# https://docs.travis-ci.com/user/database-setup/#PostgreSQL +before_script: + - psql -c 'create database topjava' -U postgres + - psql -c 'create user "user"; grant all privileges on database topjava to "user"' -U postgres + +# https://docs.travis-ci.com/user/customizing-the-build#Building-Specific-Branches +branches: + only: + - master + +# https://stackoverflow.com/a/49852690/548473: +services: + - postgresql + +# https://docs.travis-ci.com/user/notifications#Configuring-email-notifications +#notifications: +# email: false \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 46e8d6e3..a8536054 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.model; +import org.springframework.util.Assert; + public abstract class AbstractBaseEntity { public static final int START_SEQ = 100000; @@ -20,6 +22,11 @@ public Integer getId() { return id; } + public int id() { + Assert.notNull(id, "Entity must have id"); + return id; + } + public boolean isNew() { return this.id == null; } diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index 7957a089..df874378 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -2,6 +2,7 @@ import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -38,10 +39,12 @@ public List getAll(int userId) { } public void update(Meal meal, int userId) { - checkNotFoundWithId(repository.save(meal, userId), meal.getId()); + Assert.notNull(meal, "meal must not be null"); + checkNotFoundWithId(repository.save(meal, userId), meal.id()); } public Meal create(Meal meal, int userId) { + Assert.notNull(meal, "meal must not be null"); return repository.save(meal, userId); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 8fbe8dc0..09ccee68 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.service; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; @@ -19,6 +20,7 @@ public UserService(UserRepository repository) { } public User create(User user) { + Assert.notNull(user, "user must not be null"); return repository.save(user); } @@ -31,6 +33,7 @@ public User get(int id) { } public User getByEmail(String email) { + Assert.notNull(email, "email must not be null"); return checkNotFound(repository.getByEmail(email), "email=" + email); } @@ -39,6 +42,7 @@ public List getAll() { } public void update(User user) { - checkNotFoundWithId(repository.save(user), user.getId()); + Assert.notNull(user, "user must not be null"); + checkNotFoundWithId(repository.save(user), user.id()); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index b63ecf50..a4665e2a 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -16,6 +16,9 @@ public class DateTimeUtil { private static final LocalDateTime MIN_DATE = LocalDateTime.of(1, 1, 1, 0, 0); private static final LocalDateTime MAX_DATE = LocalDateTime.of(3000, 1, 1, 0, 0); + private DateTimeUtil() { + } + public static LocalDateTime atStartOfDayOrMin(LocalDate localDate) { return localDate != null ? localDate.atStartOfDay() : MIN_DATE; } diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 2d604bed..c99874f6 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -14,6 +14,9 @@ public class MealsUtil { public static final int DEFAULT_CALORIES_PER_DAY = 2000; + private MealsUtil() { + } + public static List getTos(Collection meals, int caloriesPerDay) { return filterByPredicate(meals, caloriesPerDay, meal -> true); } diff --git a/src/main/java/ru/javawebinar/topjava/util/Util.java b/src/main/java/ru/javawebinar/topjava/util/Util.java index a17a6927..0860f5c6 100644 --- a/src/main/java/ru/javawebinar/topjava/util/Util.java +++ b/src/main/java/ru/javawebinar/topjava/util/Util.java @@ -3,6 +3,9 @@ import org.springframework.lang.Nullable; public class Util { + private Util() { + } + public static > boolean isBetweenHalfOpen(T value, @Nullable T start, @Nullable T end) { return (start == null || value.compareTo(start) >= 0) && (end == null || value.compareTo(end) < 0); } diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 971eb9c0..0f35f22a 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -5,6 +5,8 @@ import ru.javawebinar.topjava.util.exception.NotFoundException; public class ValidationUtil { + private ValidationUtil() { + } public static T checkNotFoundWithId(T object, int id) { checkNotFoundWithId(object != null, id); @@ -36,7 +38,7 @@ public static void assureIdConsistent(AbstractBaseEntity entity, int id) { // conservative when you reply, but accept liberally (http://stackoverflow.com/a/32728226/548473) if (entity.isNew()) { entity.setId(id); - } else if (entity.getId() != id) { + } else if (entity.id() != id) { throw new IllegalArgumentException(entity + " must be with id=" + id); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java index 58821754..4bad5863 100644 --- a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java +++ b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java @@ -8,6 +8,9 @@ public class SecurityUtil { private static int id = AbstractBaseEntity.START_SEQ; + private SecurityUtil() { + } + public static int authUserId() { return id; } diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java index 41e70701..a7cd8a47 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryBaseRepository.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -16,6 +17,7 @@ public class InMemoryBaseRepository { final Map map = new ConcurrentHashMap<>(); public T save(T entity) { + Objects.requireNonNull(entity, "Entity must not be null"); if (entity.isNew()) { entity.setId(counter.getAndIncrement()); map.put(entity.getId(), entity); @@ -37,6 +39,7 @@ Collection getCollection() { } void put(T entity) { - map.put(entity.getId(), entity); + Objects.requireNonNull(entity, "Entity must not be null"); + map.put(entity.id(), entity); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index f56cef32..022dba0b 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -12,10 +12,7 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -36,6 +33,7 @@ public class InMemoryMealRepository implements MealRepository { @Override public Meal save(Meal meal, int userId) { + Objects.requireNonNull(meal, "meal must not be null"); InMemoryBaseRepository meals = usersMealsMap.computeIfAbsent(userId, uid -> new InMemoryBaseRepository<>()); return meals.save(meal); } diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index d79817af..3f415931 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -7,6 +7,7 @@ import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import static ru.javawebinar.topjava.UserTestData.admin; @@ -32,6 +33,7 @@ public List getAll() { @Override public User getByEmail(String email) { + Objects.requireNonNull(email, "email must not be null"); return getCollection().stream() .filter(u -> email.equals(u.getEmail())) .findFirst() diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index e4b5dd91..595d2d85 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -49,7 +49,7 @@ public void deleteNotOwn() { @Test public void create() { Meal created = service.create(getNew(), USER_ID); - int newId = created.getId(); + int newId = created.id(); Meal newMeal = getNew(); newMeal.setId(newId); MEAL_MATCHER.assertMatch(created, newMeal); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index f62db0c9..a8b386e8 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -32,7 +32,7 @@ public class UserServiceTest { @Test public void create() { User created = service.create(getNew()); - Integer newId = created.getId(); + int newId = created.id(); User newUser = getNew(); newUser.setId(newId); USER_MATCHER.assertMatch(created, newUser); From 84a938fba4f2a6b8ea03778a02bd8ee9c45562b9 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:07:37 +0300 Subject: [PATCH 034/144] 4_6_init_and_populate_db.patch --- src/main/resources/db/postgres.properties | 3 ++- src/main/resources/spring/spring-db.xml | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 7b2f704a..e5aeb857 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -4,4 +4,5 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user -database.password=user +database.password=password +database.init=true \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 283fee39..72a3f617 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,8 +1,10 @@ + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd + http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> @@ -15,6 +17,11 @@ + + + + + From 027c86524696b53a38d049e1012184a3ec4b5553 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:09:03 +0300 Subject: [PATCH 035/144] 4_7_create_inmemory_test_ctx.patch --- src/main/resources/spring/spring-app.xml | 1 - src/main/resources/spring/spring-db.xml | 2 ++ src/test/java/ru/javawebinar/topjava/SpringMain.java | 2 +- .../web/user/InMemoryAdminRestControllerSpringTest.java | 2 +- .../topjava/web/user/InMemoryAdminRestControllerTest.java | 2 +- src/test/resources/spring/inmemory.xml | 7 +++++++ 6 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/spring/inmemory.xml diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 4c05aea4..4c17228b 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -9,7 +9,6 @@ --> - diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 72a3f617..8e33d32f 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -17,6 +17,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index b869d1c6..e41f1ae1 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -17,7 +17,7 @@ public class SpringMain { public static void main(String[] args) { // java 7 automatic resource management (ARM) - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml")) { + try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml")) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email@mail.ru", "password", Role.ADMIN)); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java index afdffdfa..2a15928d 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -13,7 +13,7 @@ import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -@ContextConfiguration("classpath:spring/spring-app.xml") +@ContextConfiguration({"classpath:spring/spring-app.xml", "classpath:spring/inmemory.xml"}) @RunWith(SpringRunner.class) public class InMemoryAdminRestControllerSpringTest { diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java index 33c83774..7cc2a833 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -22,7 +22,7 @@ public class InMemoryAdminRestControllerTest { @BeforeClass public static void beforeClass() { - appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml"); log.info("\n{}\n", Arrays.toString(appCtx.getBeanDefinitionNames())); controller = appCtx.getBean(AdminRestController.class); repository = appCtx.getBean(InMemoryUserRepository.class); diff --git a/src/test/resources/spring/inmemory.xml b/src/test/resources/spring/inmemory.xml new file mode 100644 index 00000000..c6a2710c --- /dev/null +++ b/src/test/resources/spring/inmemory.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file From 746e51af528fb30425e44604cd7fee54f9a78da2 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:10:04 +0300 Subject: [PATCH 036/144] 4_8_add_jpa.patch --- pom.xml | 27 +++++++- .../topjava/model/AbstractBaseEntity.java | 10 ++- .../topjava/model/AbstractNamedEntity.java | 13 +++- .../ru/javawebinar/topjava/model/Meal.java | 13 ++++ .../ru/javawebinar/topjava/model/User.java | 25 ++++++++ .../repository/jpa/JpaMealRepository.java | 37 +++++++++++ .../repository/jpa/JpaUserRepository.java | 61 +++++++++++++++++++ src/main/resources/db/postgres.properties | 6 +- src/main/resources/spring/spring-db.xml | 36 +++++++++-- 9 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java diff --git a/pom.xml b/pom.xml index 1ce2043b..ad18f8fc 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,11 @@ 4.13.2 3.19.0 + + + 5.4.28.Final + 7.0.1.Final + 3.0.1-b12 @@ -84,7 +89,7 @@ org.springframework - spring-jdbc + spring-orm ${spring.version} @@ -95,6 +100,26 @@ ${postgresql.version} + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} + + + + + org.glassfish + javax.el + ${javax-el.version} + provided + + javax.servlet diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index a8536054..d9343a07 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -2,12 +2,20 @@ import org.springframework.util.Assert; +import javax.persistence.*; + +@MappedSuperclass +// http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access +@Access(AccessType.FIELD) public abstract class AbstractBaseEntity { public static final int START_SEQ = 100000; + @Id + @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = START_SEQ) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") protected Integer id; - public AbstractBaseEntity() { + protected AbstractBaseEntity() { } protected AbstractBaseEntity(Integer id) { diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java index 5f0fde2b..47a758c6 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java @@ -1,10 +1,21 @@ package ru.javawebinar.topjava.model; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import javax.persistence.Column; +import javax.persistence.MappedSuperclass; + + +@MappedSuperclass public abstract class AbstractNamedEntity extends AbstractBaseEntity { + @NotBlank + @Size(min = 2, max = 100) + @Column(name = "name", nullable = false) protected String name; - public AbstractNamedEntity() { + protected AbstractNamedEntity() { } protected AbstractNamedEntity(Integer id, String name) { diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 1c1e67b1..788ae8f6 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.model; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -11,6 +13,9 @@ public class Meal extends AbstractBaseEntity { private int calories; + @ManyToOne(fetch = FetchType.LAZY) + private User user; + public Meal() { } @@ -57,6 +62,14 @@ public void setCalories(int calories) { this.calories = calories; } + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + @Override public String toString() { return "Meal{" + diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 446d86ff..12184b0b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,7 +1,13 @@ package ru.javawebinar.topjava.model; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; +import javax.persistence.*; import java.util.Collection; import java.util.Date; import java.util.EnumSet; @@ -9,18 +15,37 @@ import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; +@Entity +@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) public class User extends AbstractNamedEntity { + @Column(name = "email", nullable = false, unique = true) + @Email + @NotBlank + @Size(max = 100) private String email; + @Column(name = "password", nullable = false) + @NotBlank + @Size(min = 5, max = 100) private String password; + @Column(name = "enabled", nullable = false, columnDefinition = "bool default true") private boolean enabled = true; + @Column(name = "registered", nullable = false, columnDefinition = "timestamp default now()") + @NotNull private Date registered = new Date(); + @Enumerated(EnumType.STRING) + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), + uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "role"}, name = "user_roles_unique_idx")}) + @Column(name = "role") + @ElementCollection(fetch = FetchType.EAGER) private Set roles; + @Column(name = "calories_per_day", nullable = false, columnDefinition = "int default 2000") + @Range(min = 10, max = 10000) private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; public User() { diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java new file mode 100644 index 00000000..9cc19a4d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java @@ -0,0 +1,37 @@ +package ru.javawebinar.topjava.repository.jpa; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public class JpaMealRepository implements MealRepository { + + @Override + public Meal save(Meal meal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public Meal get(int id, int userId) { + return null; + } + + @Override + public List getAll(int userId) { + return null; + } + + @Override + public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java new file mode 100644 index 00000000..68ae67d0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java @@ -0,0 +1,61 @@ +package ru.javawebinar.topjava.repository.jpa; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.util.List; + +@Repository +public class JpaUserRepository implements UserRepository { + +/* + @Autowired + private SessionFactory sessionFactory; + + private Session openSession() { + return sessionFactory.getCurrentSession(); + } +*/ + + @PersistenceContext + private EntityManager em; + + @Override + public User save(User user) { + if (user.isNew()) { + em.persist(user); + return user; + } else { + return em.merge(user); + } + } + + @Override + public User get(int id) { + return em.find(User.class, id); + } + + @Override + public boolean delete(int id) { + +/* User ref = em.getReference(User.class, id); + em.remove(ref); +*/ + Query query = em.createQuery("DELETE FROM User u WHERE u.id=:id"); + return query.setParameter("id", id).executeUpdate() != 0; + } + + @Override + public User getByEmail(String email) { + return null; + } + + @Override + public List getAll() { + return null; + } +} diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index e5aeb857..040ea60c 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -5,4 +5,8 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user database.password=password -database.init=true \ No newline at end of file + +database.init=true +jpa.showSql=true +hibernate.format_sql=true +hibernate.use_sql_comments=true \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 8e33d32f..959e712a 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,5 +1,6 @@ - + @@ -33,11 +34,34 @@ - - - + + + + + + + + + + - - + + + + + + \ No newline at end of file From ac830419f7b5155e814a5fba140a6135116db232 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 10:11:29 +0300 Subject: [PATCH 037/144] 4_9_add_named_query_and_transaction.patch --- .../ru/javawebinar/topjava/model/User.java | 9 +++++++++ .../repository/jpa/JpaUserRepository.java | 19 +++++++++++++++---- src/main/resources/spring/spring-db.xml | 10 +++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 12184b0b..4699802e 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -15,10 +15,19 @@ import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; +@NamedQueries({ + @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), + @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), + @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email"), +}) @Entity @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) public class User extends AbstractNamedEntity { + public static final String DELETE = "User.delete"; + public static final String BY_EMAIL = "User.getByEmail"; + public static final String ALL_SORTED = "User.getAllSorted"; + @Column(name = "email", nullable = false, unique = true) @Email @NotBlank diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java index 68ae67d0..9ade1335 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java @@ -1,15 +1,17 @@ package ru.javawebinar.topjava.repository.jpa; +import org.springframework.dao.support.DataAccessUtils; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import javax.persistence.Query; import java.util.List; @Repository +@Transactional(readOnly = true) public class JpaUserRepository implements UserRepository { /* @@ -25,6 +27,7 @@ private Session openSession() { private EntityManager em; @Override + @Transactional public User save(User user) { if (user.isNew()) { em.persist(user); @@ -40,22 +43,30 @@ public User get(int id) { } @Override + @Transactional public boolean delete(int id) { /* User ref = em.getReference(User.class, id); em.remove(ref); -*/ + Query query = em.createQuery("DELETE FROM User u WHERE u.id=:id"); return query.setParameter("id", id).executeUpdate() != 0; +*/ + return em.createNamedQuery(User.DELETE) + .setParameter("id", id) + .executeUpdate() != 0; } @Override public User getByEmail(String email) { - return null; + List users = em.createNamedQuery(User.BY_EMAIL, User.class) + .setParameter(1, email) + .getResultList(); + return DataAccessUtils.singleResult(users); } @Override public List getAll() { - return null; + return em.createNamedQuery(User.ALL_SORTED, User.class).getResultList(); } } diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 959e712a..fc45d2a4 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -3,9 +3,11 @@ xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" + xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd - http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> + http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> @@ -54,6 +56,12 @@ + + + + + diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties new file mode 100644 index 00000000..c7944e25 --- /dev/null +++ b/src/main/resources/db/hsqldb.properties @@ -0,0 +1,12 @@ +#database.url=jdbc:hsqldb:file:D:/temp/topjava + +database.url=jdbc:hsqldb:mem:topjava +database.username=sa +database.password= +database.driverClassName=org.hsqldb.jdbcDriver + +database.init=true +jdbc.initLocation=classpath:db/initDB_hsql.sql +jpa.showSql=true +hibernate.format_sql=true +hibernate.use_sql_comments=true \ No newline at end of file diff --git a/src/main/resources/db/initDB_hsql.sql b/src/main/resources/db/initDB_hsql.sql new file mode 100644 index 00000000..37f2da1b --- /dev/null +++ b/src/main/resources/db/initDB_hsql.sql @@ -0,0 +1,39 @@ +DROP TABLE user_roles IF EXISTS; +DROP TABLE meals IF EXISTS; +DROP TABLE users IF EXISTS; +DROP SEQUENCE global_seq IF EXISTS; + +CREATE SEQUENCE GLOBAL_SEQ AS INTEGER START WITH 100000; + +CREATE TABLE users +( + id INTEGER GENERATED BY DEFAULT AS SEQUENCE GLOBAL_SEQ PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + registered TIMESTAMP DEFAULT now() NOT NULL, + enabled BOOLEAN DEFAULT TRUE NOT NULL, + calories_per_day INTEGER DEFAULT 2000 NOT NULL +); +CREATE UNIQUE INDEX users_unique_email_idx + ON USERS (email); + +CREATE TABLE user_roles +( + user_id INTEGER NOT NULL, + role VARCHAR(255), + CONSTRAINT user_roles_idx UNIQUE (user_id, role), + FOREIGN KEY (user_id) REFERENCES USERS (id) ON DELETE CASCADE +); + +CREATE TABLE meals +( + id INTEGER GENERATED BY DEFAULT AS SEQUENCE GLOBAL_SEQ PRIMARY KEY, + date_time TIMESTAMP NOT NULL, + description VARCHAR(255) NOT NULL, + calories INT NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id) REFERENCES USERS (id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX meals_unique_user_datetime_idx + ON meals (user_id, date_time) \ No newline at end of file diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 040ea60c..5ea981f0 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -5,8 +5,10 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user database.password=password +database.driverClassName=org.postgresql.Driver database.init=true +jdbc.initLocation=classpath:db/initDB.sql jpa.showSql=true hibernate.format_sql=true hibernate.use_sql_comments=true \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index fc45d2a4..970261d0 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -9,7 +9,10 @@ http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> + + + - + From 3421e982c481e569628639038159833d65b0e839 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 14:41:25 +0300 Subject: [PATCH 039/144] fix login --- src/main/resources/db/postgres.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 5ea981f0..0116582c 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -4,7 +4,7 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user -database.password=password +fixdatabase.password=user database.driverClassName=org.postgresql.Driver database.init=true From 9908412ec9114c3992b957a179a715e9d2be5888 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Feb 2021 16:34:55 +0300 Subject: [PATCH 040/144] fix2 --- .gitignore | 1 + src/main/resources/db/hsqldb.properties | 2 +- src/main/resources/db/postgres.properties | 2 +- .../inmemory/InMemoryMealRepository.java | 20 +++++++++---------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 73e6d9e3..8168ee34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea out target +patch *.iml log *.patch diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties index c7944e25..34885233 100644 --- a/src/main/resources/db/hsqldb.properties +++ b/src/main/resources/db/hsqldb.properties @@ -1,4 +1,4 @@ -#database.url=jdbc:hsqldb:file:D:/temp/topjava +#database.url=jdbc:hsqldb:file:C:/JavaProject/topjava database.url=jdbc:hsqldb:mem:topjava database.username=sa diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 0116582c..a7dd6584 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -4,7 +4,7 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user -fixdatabase.password=user +database.password=user database.driverClassName=org.postgresql.Driver database.init=true diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 022dba0b..3bbd9e9e 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -9,8 +9,8 @@ import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.Util; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; +//import javax.annotation.PostConstruct; +//import javax.annotation.PreDestroy; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -38,15 +38,15 @@ public Meal save(Meal meal, int userId) { return meals.save(meal); } - @PostConstruct - public void postConstruct() { - log.info("+++ PostConstruct"); - } +// @PostConstruct +// public void postConstruct() { +// log.info("+++ PostConstruct"); +// } - @PreDestroy - public void preDestroy() { - log.info("+++ PreDestroy"); - } +// @PreDestroy +// public void preDestroy() { +// log.info("+++ PreDestroy"); +// } @Override public boolean delete(int id, int userId) { From 328f5a90dfab29ebc71d001111104ce4376eda7b Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 08:17:13 +0300 Subject: [PATCH 041/144] 4_11_fix_validation.patch --- pom.xml | 2 +- .../ru/javawebinar/topjava/model/AbstractNamedEntity.java | 4 ++-- src/main/java/ru/javawebinar/topjava/model/User.java | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index aa719b64..31d8f727 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 5.4.28.Final - 7.0.1.Final + 6.2.0.Final 3.0.1-b12 diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java index 47a758c6..e0734c30 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.model; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; import javax.persistence.Column; import javax.persistence.MappedSuperclass; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 4699802e..20acabf2 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,9 +1,9 @@ package ru.javawebinar.topjava.model; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; From a34f645b49a3d665dff7e029b7916196613dbc70 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 08:45:36 +0300 Subject: [PATCH 042/144] 5_1_jdk_15.patch --- .travis.yml | 2 +- pom.xml | 8 +++++++- .../repository/jdbc/JdbcUserRepository.java | 7 ++++--- .../javawebinar/topjava/web/MealServlet.java | 18 ++++++++---------- .../ru/javawebinar/topjava/MealTestData.java | 3 +-- .../ru/javawebinar/topjava/TestMatcher.java | 4 ++-- .../inmemory/InMemoryMealRepository.java | 10 +++++----- 7 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f44cef8..2fda05fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ # https://docs.travis-ci.com/user/languages/java/ language: java -jdk: openjdk8 +jdk: openjdk15 #https://dzone.com/articles/travis-ci-tutorial-java-projects cache: diff --git a/pom.xml b/pom.xml index 31d8f727..0d3c1ec6 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ http://topjava.herokuapp.com/ - 1.8 + 15 UTF-8 UTF-8 @@ -81,6 +81,12 @@ runtime + + javax.annotation + javax.annotation-api + 1.3.2 + + org.springframework diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java index 412bfbeb..0d4baa50 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -41,9 +41,10 @@ public User save(User user) { if (user.isNew()) { Number newKey = insertUser.executeAndReturnKey(parameterSource); user.setId(newKey.intValue()); - } else if (namedParameterJdbcTemplate.update( - "UPDATE users SET name=:name, email=:email, password=:password, " + - "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", parameterSource) == 0) { + } else if (namedParameterJdbcTemplate.update(""" + UPDATE users SET name=:name, email=:email, password=:password, + registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id + """, parameterSource) == 0) { return null; } return user; diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index a8f60993..defeb1d7 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -58,32 +58,30 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t String action = request.getParameter("action"); switch (action == null ? "all" : action) { - case "delete": + case "delete" -> { int id = getId(request); mealController.delete(id); response.sendRedirect("meals"); - break; - case "create": - case "update": + } + case "create", "update" -> { final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : mealController.get(getId(request)); request.setAttribute("meal", meal); request.getRequestDispatcher("/mealForm.jsp").forward(request, response); - break; - case "filter": + } + case "filter" -> { LocalDate startDate = parseLocalDate(request.getParameter("startDate")); LocalDate endDate = parseLocalDate(request.getParameter("endDate")); LocalTime startTime = parseLocalTime(request.getParameter("startTime")); LocalTime endTime = parseLocalTime(request.getParameter("endTime")); request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); request.getRequestDispatcher("/meals.jsp").forward(request, response); - break; - case "all": - default: + } + default -> { request.setAttribute("meals", mealController.getAll()); request.getRequestDispatcher("/meals.jsp").forward(request, response); - break; + } } } diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index e256b937..3eca06fb 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -4,7 +4,6 @@ import java.time.Month; import java.time.temporal.ChronoUnit; -import java.util.Arrays; import java.util.List; import static java.time.LocalDateTime.of; @@ -27,7 +26,7 @@ public class MealTestData { public static final Meal adminMeal1 = new Meal(ADMIN_MEAL_ID, of(2020, Month.JANUARY, 31, 14, 0), "Админ ланч", 510); public static final Meal adminMeal2 = new Meal(ADMIN_MEAL_ID + 1, of(2020, Month.JANUARY, 31, 21, 0), "Админ ужин", 1500); - public static final List meals = Arrays.asList(meal7, meal6, meal5, meal4, meal3, meal2, meal1); + public static final List meals = List.of(meal7, meal6, meal5, meal4, meal3, meal2, meal1); public static Meal getNew() { return new Meal(null, of(2020, Month.FEBRUARY, 1, 18, 0), "Созданный ужин", 300); diff --git a/src/test/java/ru/javawebinar/topjava/TestMatcher.java b/src/test/java/ru/javawebinar/topjava/TestMatcher.java index efd4203b..131cdef8 100644 --- a/src/test/java/ru/javawebinar/topjava/TestMatcher.java +++ b/src/test/java/ru/javawebinar/topjava/TestMatcher.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava; -import java.util.Arrays; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -20,7 +20,7 @@ public void assertMatch(T actual, T expected) { } public void assertMatch(Iterable actual, T... expected) { - assertMatch(actual, Arrays.asList(expected)); + assertMatch(actual, List.of(expected)); } public void assertMatch(Iterable actual, Iterable expected) { diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 3bbd9e9e..5d5caee1 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -25,7 +25,7 @@ public class InMemoryMealRepository implements MealRepository { private final Map> usersMealsMap = new ConcurrentHashMap<>(); { - InMemoryBaseRepository userMeals = new InMemoryBaseRepository<>(); + var userMeals = new InMemoryBaseRepository(); MealTestData.meals.forEach(userMeals::put); usersMealsMap.put(UserTestData.USER_ID, userMeals); } @@ -34,7 +34,7 @@ public class InMemoryMealRepository implements MealRepository { @Override public Meal save(Meal meal, int userId) { Objects.requireNonNull(meal, "meal must not be null"); - InMemoryBaseRepository meals = usersMealsMap.computeIfAbsent(userId, uid -> new InMemoryBaseRepository<>()); + var meals = usersMealsMap.computeIfAbsent(userId, uid -> new InMemoryBaseRepository<>()); return meals.save(meal); } @@ -50,13 +50,13 @@ public Meal save(Meal meal, int userId) { @Override public boolean delete(int id, int userId) { - InMemoryBaseRepository meals = usersMealsMap.get(userId); + var meals = usersMealsMap.get(userId); return meals != null && meals.delete(id); } @Override public Meal get(int id, int userId) { - InMemoryBaseRepository meals = usersMealsMap.get(userId); + var meals = usersMealsMap.get(userId); return meals == null ? null : meals.get(id); } @@ -71,7 +71,7 @@ public List getAll(int userId) { } private List filterByPredicate(int userId, Predicate filter) { - InMemoryBaseRepository meals = usersMealsMap.get(userId); + var meals = usersMealsMap.get(userId); return meals == null ? Collections.emptyList() : meals.getCollection().stream() .filter(filter) From 23b3bb8e3e0044c17dd27ae83a7eed2a2d143f44 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 08:46:08 +0300 Subject: [PATCH 043/144] 5_2_HW4.patch --- .../ru/javawebinar/topjava/model/Meal.java | 33 ++++++++++++++++- .../repository/jpa/JpaMealRepository.java | 37 ++++++++++++++++--- .../ru/javawebinar/topjava/MealTestData.java | 2 +- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 788ae8f6..2451c945 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,19 +1,48 @@ package ru.javawebinar.topjava.model; -import javax.persistence.FetchType; -import javax.persistence.ManyToOne; +import org.hibernate.validator.constraints.Range; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +@NamedQueries({ + @NamedQuery(name = Meal.ALL_SORTED, query = "SELECT m FROM Meal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC"), + @NamedQuery(name = Meal.DELETE, query = "DELETE FROM Meal m WHERE m.id=:id AND m.user.id=:userId"), + @NamedQuery(name = Meal.GET_BETWEEN, query = """ + SELECT m FROM Meal m + WHERE m.user.id=:userId AND m.dateTime >= :startDateTime AND m.dateTime < :endDateTime ORDER BY m.dateTime DESC + """), +// @NamedQuery(name = Meal.UPDATE, query = "UPDATE Meal m SET m.dateTime = :datetime, m.calories= :calories," + +// "m.description=:desc where m.id=:id and m.user.id=:userId") +}) +@Entity +@Table(name = "meals", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "date_time"}, name = "meals_unique_user_datetime_idx")}) public class Meal extends AbstractBaseEntity { + public static final String ALL_SORTED = "Meal.getAll"; + public static final String DELETE = "Meal.delete"; + public static final String GET_BETWEEN = "Meal.getBetween"; + + @Column(name = "date_time", nullable = false) + @NotNull private LocalDateTime dateTime; + @Column(name = "description", nullable = false) + @NotBlank + @Size(min = 2, max = 120) private String description; + @Column(name = "calories", nullable = false) + @Range(min = 10, max = 5000) private int calories; @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + @NotNull private User user; public Meal() { diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java index 9cc19a4d..300a920a 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java @@ -1,37 +1,64 @@ package ru.javawebinar.topjava.repository.jpa; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.MealRepository; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; import java.time.LocalDateTime; import java.util.List; @Repository +@Transactional(readOnly = true) public class JpaMealRepository implements MealRepository { + @PersistenceContext + private EntityManager em; + @Override + @Transactional public Meal save(Meal meal, int userId) { - return null; + meal.setUser(em.getReference(User.class, userId)); + if (meal.isNew()) { + em.persist(meal); + return meal; + } else if (get(meal.id(), userId) == null) { + return null; + } + return em.merge(meal); } @Override + @Transactional public boolean delete(int id, int userId) { - return false; + return em.createNamedQuery(Meal.DELETE) + .setParameter("id", id) + .setParameter("userId", userId) + .executeUpdate() != 0; } @Override public Meal get(int id, int userId) { - return null; + Meal meal = em.find(Meal.class, id); + return meal != null && meal.getUser().getId() == userId ? meal : null; } @Override public List getAll(int userId) { - return null; + return em.createNamedQuery(Meal.ALL_SORTED, Meal.class) + .setParameter("userId", userId) + .getResultList(); } @Override public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - return null; + return em.createNamedQuery(Meal.GET_BETWEEN, Meal.class) + .setParameter("userId", userId) + .setParameter("startDateTime", startDateTime) + .setParameter("endDateTime", endDateTime) + .getResultList(); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index 3eca06fb..e19989f9 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -10,7 +10,7 @@ import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class MealTestData { - public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator(); + public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator("user"); public static final int NOT_FOUND = 10; public static final int MEAL1_ID = START_SEQ + 2; From cb9616bf2f59c9462f11ae725b98dfd176e6d881 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:00:53 +0300 Subject: [PATCH 044/144] 5_3_fix_hibernate_issue.patch --- .../ru/javawebinar/topjava/model/AbstractBaseEntity.java | 5 ++++- src/main/resources/spring/spring-db.xml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index d9343a07..1cd6897a 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.model; +import org.hibernate.Hibernate; import org.springframework.util.Assert; import javax.persistence.*; @@ -13,6 +14,8 @@ public abstract class AbstractBaseEntity { @Id @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = START_SEQ) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") +// See https://hibernate.atlassian.net/browse/HHH-3718 and https://hibernate.atlassian.net/browse/HHH-12034 +// Proxy initialization when accessing its identifier managed now by JPA_PROXY_COMPLIANCE setting protected Integer id; protected AbstractBaseEntity() { @@ -49,7 +52,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (o == null || !getClass().equals(Hibernate.getClass(o))) { return false; } AbstractBaseEntity that = (AbstractBaseEntity) o; diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 970261d0..ae30dc79 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -48,6 +48,7 @@ + From 87a057c43a797a216403a61407579a8313f4dfe2 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:02:09 +0300 Subject: [PATCH 045/144] 5_4_HW4_optional.patch --- src/main/resources/logback.xml | 2 +- .../topjava/service/MealServiceTest.java | 34 ++++++++++++++++++- src/test/resources/logback-test.xml | 16 +++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index c7bffc3a..12a4f63c 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -16,7 +16,7 @@ UTF-8 - %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n + %d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%class{50}.%M:%L) - %msg%n diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 595d2d85..438cf146 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -1,7 +1,13 @@ package ru.javawebinar.topjava.service; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Stopwatch; +import org.junit.runner.Description; import org.junit.runner.RunWith; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; @@ -13,8 +19,10 @@ import java.time.LocalDate; import java.time.Month; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertThrows; +import static org.slf4j.LoggerFactory.getLogger; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_ID; @@ -26,10 +34,33 @@ @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class MealServiceTest { + private static final Logger log = getLogger("result"); + + private static final StringBuilder results = new StringBuilder(); + + @Rule + // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev + public final Stopwatch stopwatch = new Stopwatch() { + @Override + protected void finished(long nanos, Description description) { + String result = String.format("\n%-25s %7d", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); + results.append(result); + log.info(result + " ms\n"); + } + }; @Autowired private MealService service; + @AfterClass + public static void printResult() { + log.info("\n---------------------------------" + + "\nTest Duration, ms" + + "\n---------------------------------" + + results + + "\n---------------------------------"); + } + @Test public void delete() { service.delete(MEAL1_ID, USER_ID); @@ -88,7 +119,8 @@ public void update() { @Test public void updateNotOwn() { - assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); + NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); + Assert.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index da4eed7b..80365547 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -7,12 +7,22 @@ UTF-8 - %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n + %d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%class{50}.%M:%L) - %msg%n - - + + + UTF-8 + %magenta(%msg%n) + + + + + + + + From 9786a63ed8bb142de473105ce8257548c79dd2df Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:19:22 +0300 Subject: [PATCH 046/144] 5_5_profiles_connection_pool.patch --- pom.xml | 57 ++++++++++++------- src/main/resources/db/hsqldb.properties | 1 - src/main/resources/spring/spring-db.xml | 55 ++++++++++-------- .../java/ru/javawebinar/topjava/Profiles.java | 15 +++++ .../topjava/service/MealServiceTest.java | 3 + .../topjava/service/UserServiceTest.java | 3 + 6 files changed, 90 insertions(+), 44 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/Profiles.java diff --git a/pom.xml b/pom.xml index 0d3c1ec6..fa9a5a2d 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ UTF-8 5.3.3 + 9.0.43 1.2.3 @@ -67,13 +68,6 @@ compile - - org.slf4j - jul-to-slf4j - ${slf4j.version} - runtime - - ch.qos.logback logback-classic @@ -99,19 +93,6 @@ ${spring.version} - - - org.postgresql - postgresql - ${postgresql.version} - - - org.hsqldb - hsqldb - 2.3.4 - - - org.hibernate @@ -168,6 +149,42 @@ + + hsqldb + + + org.hsqldb + hsqldb + 2.3.4 + + + + + + postgres + + + org.postgresql + postgresql + ${postgresql.version} + + + org.apache.tomcat + tomcat-jdbc + ${tomcat.version} + provided + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + runtime + + + + true + + diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties index 34885233..7f7d8838 100644 --- a/src/main/resources/db/hsqldb.properties +++ b/src/main/resources/db/hsqldb.properties @@ -3,7 +3,6 @@ database.url=jdbc:hsqldb:mem:topjava database.username=sa database.password= -database.driverClassName=org.hsqldb.jdbcDriver database.init=true jdbc.initLocation=classpath:db/initDB_hsql.sql diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index ae30dc79..c3cc57e6 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -9,20 +9,6 @@ http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> - - - - - - - - - - @@ -30,15 +16,6 @@ - - - - - - - - @@ -76,4 +53,36 @@ --> + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/Profiles.java b/src/test/java/ru/javawebinar/topjava/Profiles.java new file mode 100644 index 00000000..c438cbbe --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/Profiles.java @@ -0,0 +1,15 @@ +package ru.javawebinar.topjava; + +public class Profiles { + public static final String + JDBC = "jdbc", + JPA = "jpa"; + + public static final String REPOSITORY_IMPLEMENTATION = JPA; + + public static final String + POSTGRES_DB = "postgres", + HSQL_DB = "hsqldb"; + + public static final String ACTIVE_DB = POSTGRES_DB; +} diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 438cf146..88675044 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -9,11 +9,13 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -33,6 +35,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.ACTIVE_DB) public class MealServiceTest { private static final Logger log = getLogger("result"); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index a8b386e8..aea34c44 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -4,10 +4,12 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -24,6 +26,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.ACTIVE_DB) public class UserServiceTest { @Autowired From 2e20c90eec6aea926d109e24caa1cfdeda382eb7 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:37:48 +0300 Subject: [PATCH 047/144] 5_6_profile_resolver.patch --- src/main/resources/db/postgres.properties | 2 +- .../java/ru/javawebinar/topjava/Profiles.java | 23 ++++++++++++++++++- .../topjava/service/MealServiceTest.java | 4 ++-- .../topjava/service/UserServiceTest.java | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index a7dd6584..5ea981f0 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -4,7 +4,7 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user -database.password=user +database.password=password database.driverClassName=org.postgresql.Driver database.init=true diff --git a/src/test/java/ru/javawebinar/topjava/Profiles.java b/src/test/java/ru/javawebinar/topjava/Profiles.java index c438cbbe..be66a041 100644 --- a/src/test/java/ru/javawebinar/topjava/Profiles.java +++ b/src/test/java/ru/javawebinar/topjava/Profiles.java @@ -1,5 +1,9 @@ package ru.javawebinar.topjava; +import org.springframework.lang.NonNull; +import org.springframework.test.context.ActiveProfilesResolver; +import org.springframework.util.ClassUtils; + public class Profiles { public static final String JDBC = "jdbc", @@ -11,5 +15,22 @@ public class Profiles { POSTGRES_DB = "postgres", HSQL_DB = "hsqldb"; - public static final String ACTIVE_DB = POSTGRES_DB; + // Get DB profile depending of DB driver in classpath + public static String getActiveDbProfile() { + if (ClassUtils.isPresent("org.postgresql.Driver", null)) { + return POSTGRES_DB; + } else if (ClassUtils.isPresent("org.hsqldb.jdbcDriver", null)) { + return HSQL_DB; + } else { + throw new IllegalStateException("Could not find DB driver"); + } + } + + //http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver + public static class ActiveDbProfileResolver implements ActiveProfilesResolver { + @Override + public @NonNull String[] resolve(@NonNull Class aClass) { + return new String[]{getActiveDbProfile()}; + } + } } diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 88675044..32ae952a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -9,8 +9,8 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ActiveProfiles; import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; @@ -35,7 +35,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(Profiles.ACTIVE_DB) +@ActiveProfiles(resolver = Profiles.ActiveDbProfileResolver.class) public class MealServiceTest { private static final Logger log = getLogger("result"); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index aea34c44..ba809a7a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -26,7 +26,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(Profiles.ACTIVE_DB) +@ActiveProfiles(resolver = Profiles.ActiveDbProfileResolver.class) public class UserServiceTest { @Autowired From 7cfd8b4072d3f492618b59eaae036921e133e60c Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:57:40 +0300 Subject: [PATCH 048/144] 5_7_spring_data_jpa.patch --- pom.xml | 20 ++++++--- .../topjava/model/AbstractBaseEntity.java | 5 ++- .../datajpa/CrudMealRepository.java | 7 +++ .../datajpa/CrudUserRepository.java | 19 ++++++++ .../datajpa/DataJpaMealRepository.java | 43 ++++++++++++++++++ .../datajpa/DataJpaUserRepository.java | 44 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 16 ++++++- .../java/ru/javawebinar/topjava/Profiles.java | 5 ++- 8 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java diff --git a/pom.xml b/pom.xml index fa9a5a2d..1fe307c4 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,8 @@ UTF-8 UTF-8 - 5.3.3 + 5.3.4 + 2.4.5 9.0.43 @@ -85,12 +86,11 @@ org.springframework spring-context - ${spring.version} - org.springframework - spring-orm - ${spring.version} + org.springframework.data + spring-data-jpa + ${spring-data-jpa.version} @@ -137,7 +137,6 @@ org.springframework spring-test - ${spring.version} test @@ -188,5 +187,14 @@ + + + org.springframework + spring-framework-bom + ${spring.version} + pom + import + + diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 1cd6897a..2ab30f34 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.model; import org.hibernate.Hibernate; +import org.springframework.data.domain.Persistable; import org.springframework.util.Assert; import javax.persistence.*; @@ -8,7 +9,7 @@ @MappedSuperclass // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) -public abstract class AbstractBaseEntity { +public abstract class AbstractBaseEntity implements Persistable { public static final int START_SEQ = 100000; @Id @@ -29,6 +30,7 @@ public void setId(Integer id) { this.id = id; } + @Override public Integer getId() { return id; } @@ -38,6 +40,7 @@ public int id() { return id; } + @Override public boolean isNew() { return this.id == null; } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java new file mode 100644 index 00000000..a3659675 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -0,0 +1,7 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.javawebinar.topjava.model.Meal; + +public interface CrudMealRepository extends JpaRepository { +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java new file mode 100644 index 00000000..24c42a81 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -0,0 +1,19 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import ru.javawebinar.topjava.model.User; + +@Transactional(readOnly = true) +public interface CrudUserRepository extends JpaRepository { + @Transactional + @Modifying +// @Query(name = User.DELETE) + @Query("DELETE FROM User u WHERE u.id=:id") + int delete(@Param("id") int id); + + User getByEmail(String email); +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java new file mode 100644 index 00000000..d1b4c8ef --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java @@ -0,0 +1,43 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public class DataJpaMealRepository implements MealRepository { + + private final CrudMealRepository crudRepository; + + public DataJpaMealRepository(CrudMealRepository crudRepository) { + this.crudRepository = crudRepository; + } + + @Override + public Meal save(Meal meal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public Meal get(int id, int userId) { + return null; + } + + @Override + public List getAll(int userId) { + return null; + } + + @Override + public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java new file mode 100644 index 00000000..bc240d2d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java @@ -0,0 +1,44 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.List; + +@Repository +public class DataJpaUserRepository implements UserRepository { + private static final Sort SORT_NAME_EMAIL = Sort.by(Sort.Direction.ASC, "name", "email"); + + private final CrudUserRepository crudRepository; + + public DataJpaUserRepository(CrudUserRepository crudRepository) { + this.crudRepository = crudRepository; + } + + @Override + public User save(User user) { + return crudRepository.save(user); + } + + @Override + public boolean delete(int id) { + return crudRepository.delete(id) != 0; + } + + @Override + public User get(int id) { + return crudRepository.findById(id).orElse(null); + } + + @Override + public User getByEmail(String email) { + return crudRepository.getByEmail(email); + } + + @Override + public List getAll() { + return crudRepository.findAll(SORT_NAME_EMAIL); + } +} diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index c3cc57e6..4106dbf1 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -4,12 +4,17 @@ xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" + xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> - + + + + @@ -85,4 +90,11 @@ p:username="${database.username}" p:password="${database.password}"/> + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/Profiles.java b/src/test/java/ru/javawebinar/topjava/Profiles.java index be66a041..5389ff5e 100644 --- a/src/test/java/ru/javawebinar/topjava/Profiles.java +++ b/src/test/java/ru/javawebinar/topjava/Profiles.java @@ -7,9 +7,10 @@ public class Profiles { public static final String JDBC = "jdbc", - JPA = "jpa"; + JPA = "jpa", + DATAJPA = "datajpa"; - public static final String REPOSITORY_IMPLEMENTATION = JPA; + public static final String REPOSITORY_IMPLEMENTATION = DATAJPA; public static final String POSTGRES_DB = "postgres", From 6fad85e197f10b4b4e5a888ac7e3179b297c2069 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 4 Mar 2021 14:27:15 +0300 Subject: [PATCH 049/144] 5_8_spring_cache.patch --- pom.xml | 31 ++++++++++++++++++- .../topjava/service/UserService.java | 6 ++++ src/main/resources/cache/ehcache.xml | 25 +++++++++++++++ src/main/resources/spring/spring-app.xml | 1 + src/main/resources/spring/spring-cache.xml | 20 ++++++++++++ .../topjava/service/UserServiceTest.java | 10 ++++++ 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/cache/ehcache.xml create mode 100644 src/main/resources/spring/spring-cache.xml diff --git a/pom.xml b/pom.xml index 1fe307c4..098727d7 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,9 @@ 5.4.28.Final 6.2.0.Final 3.0.1-b12 + + + 3.9.2 @@ -85,7 +88,7 @@ org.springframework - spring-context + spring-context-support org.springframework.data @@ -113,6 +116,32 @@ provided + + + javax.cache + cache-api + 1.1.0 + + + org.ehcache + ehcache + runtime + ${ehcache.version} + + + org.glassfish.jaxb + jaxb-runtime + + + + + + + org.glassfish.jaxb + jaxb-runtime + 2.4.0-b180830.0438 + + javax.servlet diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 09ccee68..e0979b7e 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.service; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import ru.javawebinar.topjava.model.User; @@ -19,11 +21,13 @@ public UserService(UserRepository repository) { this.repository = repository; } + @CacheEvict(value = "users", allEntries = true) public User create(User user) { Assert.notNull(user, "user must not be null"); return repository.save(user); } + @CacheEvict(value = "users", allEntries = true) public void delete(int id) { checkNotFoundWithId(repository.delete(id), id); } @@ -37,10 +41,12 @@ public User getByEmail(String email) { return checkNotFound(repository.getByEmail(email), "email=" + email); } + @Cacheable("users") public List getAll() { return repository.getAll(); } + @CacheEvict(value = "users", allEntries = true) public void update(User user) { Assert.notNull(user, "user must not be null"); checkNotFoundWithId(repository.save(user), user.id()); diff --git a/src/main/resources/cache/ehcache.xml b/src/main/resources/cache/ehcache.xml new file mode 100644 index 00000000..05589f71 --- /dev/null +++ b/src/main/resources/cache/ehcache.xml @@ -0,0 +1,25 @@ + + + + + + + + + + 5 + + 5000 + + + + + + + 1 + + + + diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 4c17228b..d6c643e9 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -9,6 +9,7 @@ --> + diff --git a/src/main/resources/spring/spring-cache.xml b/src/main/resources/spring/spring-cache.xml new file mode 100644 index 00000000..73325fee --- /dev/null +++ b/src/main/resources/spring/spring-cache.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index ba809a7a..a7651c06 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -1,8 +1,10 @@ package ru.javawebinar.topjava.service; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.CacheManager; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; @@ -32,6 +34,14 @@ public class UserServiceTest { @Autowired private UserService service; + @Autowired + private CacheManager cacheManager; + + @Before + public void setup() { + cacheManager.getCache("users").clear(); + } + @Test public void create() { User created = service.create(getNew()); From 70cf0d63ca3f1fa3ca6ccdda16ac4c6ba5fd3ca1 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Fri, 5 Mar 2021 12:41:06 +0300 Subject: [PATCH 050/144] fix --- lib/javax.persistence.jar | Bin 129793 -> 164556 bytes .../datajpa/DataJpaUserRepository.java | 12 +++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/javax.persistence.jar b/lib/javax.persistence.jar index 21d80e0ed3b5e20aa787087247a006398cda7679..164ef3a99e2bf6cd07d7d1c7b5fea3a902770fb5 100644 GIT binary patch literal 164556 zcma%j19YU@)@^Ltwr$%^I(EgjyJI`)*mj2<+v(Wq*iOfMIp?a+|IUBktC6q9H)`x% zm6~%d?X?mmSx_(-ASfs(AV4U(tmQxz3@Qi^&>9#J5E>8=ki58>Fuk;b1f#sLg0zIV zsv3j5M6lYV{R$IWr{w6np<}90xjc%5P5_tEV4nK&RH<8*?g21?!s!aa)Vm+Yl#nD= zDBkvc$Hh>j0Y$^6GZV%;h;1{S3R)r+9Kk3F>X$BmqjcIfTE1y^+fF%ci{rdOkscs& zGU2`>(kD?@EI3(OXDjYL)U@R4ciwSg*G6{o<0Z9eL1&0y$FQPC8r$!j8Suaq%`8HN z`VHW8r51rG9{7@`md=jOFz}!*5qF9dT4|z2^0;-rDca>S_IKG`GCj-F3wLvv#Sx)B zCe>gb2*)Ob2ipl=Bn)(Z9*Xf)pXQnaN%|(|G%-^rxlQf-)=Oul&s*22I#1MR68kb^ zU5uDiyLC~Yk!V=TRBuF8DS)C2HB>HS*t7Z21bNsgoH2j8pDL;7@Dv zw_~uC3?*b(sgL)}=$a8(@kDQjs{W$r-K1DU}Q?}TAm>;rE&70lf(eVSksd#Bkwv9ke&X~9r=G~yZNLMy z&LiO*f4JX}!Okyu9(2UOE+tt=sNbcd^rxtRpn(1q)rXG${eb?s`?sk6v&`f}d;ebZ zaRc#jSsA$*c`*KGjp_ebgYxe+4ge=-OJ^5=oeAK-tjGP^^(Ic1E&wM>qyMrI?{7ES z0$hx2?M(qT|79n}-|lpFu>5V$A7=Nl?B5T>kGp>(WNYLGu=_8IiU04#f5OJ__mKS0 z&*J?5+5UgMA-&PRzrY`U;}2e*=w1bofPsMeJ}!cfH~j~9zyA`#CME!9X9g1+BWLGe zC3(eOAtYbeV44SiLP&nB@NhB4yX|q*aPovNaoX}xB>+}p*{}4L-^Je2AwPc+9OV)x z#f%Eg@=Wu*7(ey#>FfY)9?Xm7Cwvw_0HblXbV9Mv*S(`~VlW8AE=rrg(-Iffp&qUC zU@pM*UlC0xiQrjb>UvvmrC|dZQnt&=o=Kdz8CF_d>6b1+&4A9N^93b6S$MuDcS#w8CF#FnX0efqR}v)kpJh< zxzx8}S~P!s1hn-4qHE)fMfp+u)%b;b3LxdB9C=&tv8^g>yS*5;Xf< z6?16P_4zl{I4*oqiATbld}+A)P*AvFHJ ziZI5@;8d(ISP0;4^WkpbJ9_*FO0>RsF;sYw=+%VT){+Qr=8ho)I@^F0%F)A%zklPsW6h z#~EGv1ag4OWm(~9y$rJKQ0_T{DYbC?N>)P*<`iqh=Ule;*xwT!#doiAk4m-dnA;A` z?;X#1Y01w0lx6wEf|Yh%W60ygK~iKsQAFt67kSGyRss!f)nZs|&F$VnQLk-V(brU$ z4L~I<;on!|6e1AWaK?mVJ1y}pS#Jc3cn3Uje|OFM)lin4$HOVj|5PyT(RZ4xB#wxo-evJ0|9)Y^KLuCpXDi ziFzDPezoay8n_|Jp)!+mBx?7g1OGm-nS~S{y?Zz3&)I7`d$`B8+fF6Ub>dB2efw!( zA?W!tMEZ2lgf0VHy(>ACVu91rO%$$lK^@)TK)1RGhrv8LrfQk45Qou=DxVkc)`Uq$ zjX0pNR>V0z)L!JmLsE{YVbfsFtt1zoOCs!E4n)-jgf)}*J=poX!%?fq z%U==OFw`9p_z0Y2x55>*w{v!JGP1ODk^k_d|AbCO&mKh?i60XKqX{;cUZbbH zkr2azKK~F_URA_WB*0KpM#RUW=|ubcGJ}~G&gZX1;p{l_Ij_otEuZq){F2&67n51qZQ%YwemAn(m_a*G`N}fYrm0b8>APfk~fpl1?=<4tw3#1AR3z*pp_kFlQz-Z%%-g497?IPXuV#%AMumWtGLnJIobIWa7L!^ z0G>faQUnPBE1mvOIY~4n&G~kAgcg|^S<7^sVVHsXFcM2vr>w8)@>J$_t z1*)?oYzt%g+weH5K=cnR0}-J$n)Iaiuqkq~7)0Hk4T_34F(Y7ivFz*+I&&%#DMF%y zMN9@HfoH{*C~*e-dvG&oWIEWM%)xx&hccg#x!K)KaFTh zNed=NXu17t=986Yl6z&kWMDT#v?7ECBA^>2cujmPSuW3X&I?Io8O#ZOVN30n9Hn$Z zS`BZgdhkSU25K!8mc>_X4%7CglIN2ge3lU+s+PB0c~ib@L7-JXn8FV-fgcZ*N<6>r z0tys(A}b|1j{~#&i3|f6IrW~`b@M`D8u=8dbkfpyijW9Y2o`gfzxR6K)wi6^bo6sL zmqnx<=;ABB6X9yxX=MH?yS~uYb4k|A64a)l_c6GP)W{`sRey-nnB){QX!RK9ek)1B3ajhs>q3tnn4dKPiU&mD6sNpNWe@}=t*BWg}=EfW3txc%uJd7jF)C#_eUMQ{=v~NDSIoNvR zRLkH<)B7qyqBH${=1InDv0)Q_%WQO9x#`xaNFj9ivXbe6s>RMpcC3 z!Q?S{W;N`XZ8ZT>=?wykJBUH=%%IMlM4|0j6b7ZfRp_~6eN1${STyu<^Y~A6JDD~M zeqI2YGIqzBR-t}lLI06*afsVm(!qhvn{(mlLBW#uRT7W0_VQ>tlI0 zHZ&aZBL;dy&t;^%VMAc)53)xIrNzWmb6C5bqciQYm6+VtokrRgv-!(YJO;<+*w7! zE3w*uqb2c1t@VMn@6A3c=;?DA+#Moida}cayaj5Lx~S#5HR_~V$jX0F9>~1+dIlH0&rS{ z1|m@-PaYs=wIGBB!cl-n512E`*hd4=OxTA5khjIHkXZZt*ISdf??@)JzCM^&{LL3T zkYT@=&USi-VOA!I{K7o&*`a;RU{pheP%c3?o((YoF!VsIX`*EfKUs5oNqq#)r+$DE znIL3m8fJ0_^=n~awld!hm}dr+tYamb8|j`7E)nK9R(cbvnj4+247tcjm|9#+vwHmW z>U@CxBOd>5Ab&L-zRejhmJicm|L}g)|J`(C>@DroJRSZWlCKnJm5|!6pEomU* z6VbpO0;Q=&G3!7D!DVR?$Mdeq|`8T~7}8^PfG#nJDvjT_h3 zj|KWfUZ(__HZWp6rkuTr@A36rKsa~(tub@l_1-tvkr`R3ZjUE`lg}2j1xVF?MOQsV zmJfV;a1sl7s6D&&jO}5AEm)vhUH_Cxtmp3Axq8vy;b8ckuT`0R`KNUVW$`YJSJMWA zw$bL(nUj&2SHVwLn;N58_F9B8^o?@|Q@O{N(Xy-*^xw}` zjRc8`OQ}J^BX-~;2@OiCo?aCIWG%RJ2KBA4-CMlXNa;`L+NgUx)#2o+Q_8)dA&v{C+WeGh=(n;ra`6V+MhdI#ZG{gXIeVrXxdEB+?_wnf_ai|;Q+ z^!8sZ2!|}wlE*WZfRM~WEuenLh1ZHfBs9lJGp!NX$0*_10^I?}JBw{34ml*+=nfWB z3x<{%z44G-Yp^Em#fD{xB0t8V4^B+F^ce(vlh&@aq6|^5=xb#DeH2+sYZdUp0_KhCL?^GHO0WMwq4`~dq7+r_Oqr)AnU@4k%%QNdh7 z^k@of2;u>jAE!S)62c{4@6Vp#Q$5mO0)aw-V?gjwtvD^yzp(^T>+Gq@mhxfs-Ek;S z)RHEd+*NFgSFC50nzO@_?=}3gAweLm;Mb|@&D*PuNjliau}%3_pqFD(77hKV@f@#& ze%!V^SLe*b&QeipipW}%_>J=BS7&@o^mQ7b_Y!jRnvN_4>O7QIdqE8ENNYxQ*s3j;nwqYI?>n$=sqi@&t#9_~FZ*a3q+B z0v%&lxe^iB(pRBu*rH&@0+M;XvVEmwZ+O@6Ut>QAaA3P)^B)q3V2yPaO9=n)1-}FR zR|@!kP@IT

    }sW0r`KYK*iqP0$G7<`Tok+9@ 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 NkAX2N06Zg%{|~}rN^SrE literal 129793 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

    x$jzCx>QKI9Dc;_z4e z0Yjof!jtYPhbWx5hKGB$%LhfCBm1`-iGRzk7{pT4|G=z*;ASA7z{7XMS%+kLwXk%W>j7B@a_J!C@WeN1+*~V>{Bl#-HQiNtX^sL^z#~Mk&VLmX>zsDS*--Z5!6_ zmyRl7s(i+;QDxn)?`n6UYg4?s_nq&1alMX7Ku2!YsIXC8I_2ltv;oQhymtI+SybF} z&5%yV8Fnis+qSDJR969o0ePgX?>;nd8ilT!8EO$W(t2lKb^a@_Kv!Z*Qr`py`u0g3 zZbg(P;3`80t34!4MxMUfAtf5-l2pfc`~4o`Nn>PHWMZ{b^}Tq!IwLnS=D7Q@nP_AJ z8sVco2#(CK5g9n>xsoML==2oKhcCtLFy{?{xX#>Z&lk>Rso+VQP2bI;*p#A39_3ua z3zya53Tr%pUuzV&vlWDW1X|$xfqs}%)d9;}8Om^7+L!id9t6GxKHtOdzTXeqV9Jx! z3tmZxybfD?S*MPdg8Yyzxeny(r4u#+Pi{N;939nD8jYt&PnrFIsp6>KC+OLmrLq&S^b}zlDjCytmd7*0xc^f^MC9@ z=&NP_ano*sGCn+17@8=dpTx`IX{m-AX2O1xKfI3v*rmYo7&K&ngiW$r9mr_rx)sM_ z#Oz=4Vv`zRZpXu&Mmb(!)N6q=g+V9i!6~U(QvqD8>wnK=8H%^MaQx|0(^ETQWB?(j z)>@rFuE&URSsM)!vFUiB*5>)ainsa+K~=f=`~V@-tbQ7awxjYeWi7nicAh733(G?(In)J1z$v! zL)gO*mdA8wN2rh$t0s*R_F)t71Fc-W$DmA4%YZ5OBX?nfnfoN)+l~Aka750IO#Bo~ zZe&XByG|qJYr-bX^f>*UgsC_FPBhVcu zE?M4|@Ruv)k2hBFW{aTLN~uCB#iXo{tcREvLbp5b!I5W6lPQIXmT&ztMU$l&mh67# zAVy>wOf`Z@GYN~<9~>2ZQ)I%iXWE?%z?cj|Q{04$##f^$+D6A`k9Np#ZT=(obE*6&ga+De#a*1}r|Tm%hJ3*HK%S{$iBTjr6E zcRAkU5^&yPn!YG)RYb1Mp?J?nfnrr6wM5#lYRlUNv%b0z1qno-+KO5py~U(0-o|J7 zZG>rh9+nU6g?vloX{oe!XiIW&JI|RXQKd3)S|&LDu#KENkxJ{^hzwD-$szetxtXs;FE fq85Hs^+j9}Vd)!)Er7{XjDPYXacZ#z&d&6IINz)K diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java index bc240d2d..b7c4dd81 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.datajpa; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.User; @@ -11,11 +12,12 @@ public class DataJpaUserRepository implements UserRepository { private static final Sort SORT_NAME_EMAIL = Sort.by(Sort.Direction.ASC, "name", "email"); - private final CrudUserRepository crudRepository; + @Autowired + private CrudUserRepository crudRepository; - public DataJpaUserRepository(CrudUserRepository crudRepository) { - this.crudRepository = crudRepository; - } +// public DataJpaUserRepository(CrudUserRepository crudRepository) { +// this.crudRepository = crudRepository; +// } @Override public User save(User user) { @@ -41,4 +43,4 @@ public User getByEmail(String email) { public List getAll() { return crudRepository.findAll(SORT_NAME_EMAIL); } -} +} \ No newline at end of file From 7e21b7dcf59f6aebddcbadca05a40063cc2ca747 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Fri, 5 Mar 2021 13:21:48 +0300 Subject: [PATCH 051/144] fix --- lib/javax.annotation.jar | Bin 7713 -> 0 bytes lib/javax.ejb.jar | Bin 47581 -> 0 bytes lib/javax.jms.jar | Bin 25957 -> 0 bytes lib/javax.persistence.jar | Bin 164556 -> 0 bytes lib/javax.resource.jar | Bin 44511 -> 0 bytes lib/javax.servlet.jsp.jar | Bin 78836 -> 0 bytes lib/javax.transaction.jar | Bin 9714 -> 0 bytes pom.xml | 2 +- 8 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 lib/javax.annotation.jar delete mode 100644 lib/javax.ejb.jar delete mode 100644 lib/javax.jms.jar delete mode 100644 lib/javax.persistence.jar delete mode 100644 lib/javax.resource.jar delete mode 100644 lib/javax.servlet.jsp.jar delete mode 100644 lib/javax.transaction.jar diff --git a/lib/javax.annotation.jar b/lib/javax.annotation.jar deleted file mode 100644 index 52dca7f561c5d579ca764cb25b96d3f539cf242a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7713 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 ayGQqbfd2}7!o6gSEfc3Ot66$1-hTi%8LHX< diff --git a/lib/javax.ejb.jar b/lib/javax.ejb.jar deleted file mode 100644 index 4ebf5ecd4c9ab83540ef206f6c5a1906db008098..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47581 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

    }sW0r`KYK*iqP0$G7<`Tok+9@ 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 NkAX2N06Zg%{|~}rN^SrE diff --git a/lib/javax.resource.jar b/lib/javax.resource.jar deleted file mode 100644 index 696a2345878907025784d2e6e49c6e6b41d1cfac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44511 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 Rg4PB8C6U3vu!Mnr`hR(p1f~E0 diff --git a/lib/javax.servlet.jsp.jar b/lib/javax.servlet.jsp.jar deleted file mode 100644 index 9c0631cea0fd56031db19fc3edbb4db4bfbb923a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78836 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 5.4.28.Final 6.2.0.Final - 3.0.1-b12 + 3.0.1-b06 3.9.2 From 190ac3e70b339e1c5af115ae2d9b75673a87efdb Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Fri, 5 Mar 2021 15:54:34 +0300 Subject: [PATCH 052/144] fix --- .../topjava/service/MealServiceTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 32ae952a..5202e9cf 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -64,7 +64,7 @@ public static void printResult() { "\n---------------------------------"); } - @Test +// @Test public void delete() { service.delete(MEAL1_ID, USER_ID); assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, USER_ID)); @@ -75,12 +75,12 @@ public void deleteNotFound() { assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND, USER_ID)); } - @Test +// @Test public void deleteNotOwn() { assertThrows(NotFoundException.class, () -> service.delete(MEAL1_ID, ADMIN_ID)); } - @Test +// @Test public void create() { Meal created = service.create(getNew(), USER_ID); int newId = created.id(); @@ -90,14 +90,14 @@ public void create() { MEAL_MATCHER.assertMatch(service.get(newId, USER_ID), newMeal); } - @Test +// @Test public void duplicateDateTimeCreate() { assertThrows(DataAccessException.class, () -> service.create(new Meal(null, meal1.getDateTime(), "duplicate", 100), USER_ID)); } - @Test +// @Test public void get() { Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); MEAL_MATCHER.assertMatch(actual, adminMeal1); @@ -113,26 +113,26 @@ public void getNotOwn() { assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, ADMIN_ID)); } - @Test +// @Test public void update() { Meal updated = getUpdated(); service.update(updated, USER_ID); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); } - @Test +// @Test public void updateNotOwn() { NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); Assert.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } - @Test +// @Test public void getAll() { MEAL_MATCHER.assertMatch(service.getAll(USER_ID), meals); } - @Test +// @Test public void getBetweenInclusive() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive( LocalDate.of(2020, Month.JANUARY, 30), @@ -140,7 +140,7 @@ public void getBetweenInclusive() { meal3, meal2, meal1); } - @Test +// @Test public void getBetweenWithNullDates() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); } From ccb49aadbf2ede3d5555410ca1d5991a400a7d98 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:24:09 +0300 Subject: [PATCH 053/144] 6_0_fix.patch --- .../java/ru/javawebinar/topjava/Profiles.java | 10 ---------- src/main/webapp/mealForm.jsp | 2 +- src/main/webapp/meals.jsp | 2 +- .../topjava/ActiveDbProfileResolver.java | 13 +++++++++++++ .../topjava/service/MealServiceTest.java | 6 +++--- .../topjava/service/UserServiceTest.java | 4 ++-- .../user/InMemoryAdminRestControllerSpringTest.java | 2 +- .../web/user/InMemoryAdminRestControllerTest.java | 2 +- 8 files changed, 22 insertions(+), 19 deletions(-) rename src/{test => main}/java/ru/javawebinar/topjava/Profiles.java (63%) create mode 100644 src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java diff --git a/src/test/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java similarity index 63% rename from src/test/java/ru/javawebinar/topjava/Profiles.java rename to src/main/java/ru/javawebinar/topjava/Profiles.java index 5389ff5e..e4111a2a 100644 --- a/src/test/java/ru/javawebinar/topjava/Profiles.java +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -1,7 +1,5 @@ package ru.javawebinar.topjava; -import org.springframework.lang.NonNull; -import org.springframework.test.context.ActiveProfilesResolver; import org.springframework.util.ClassUtils; public class Profiles { @@ -26,12 +24,4 @@ public static String getActiveDbProfile() { throw new IllegalStateException("Could not find DB driver"); } } - - //http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver - public static class ActiveDbProfileResolver implements ActiveProfilesResolver { - @Override - public @NonNull String[] resolve(@NonNull Class aClass) { - return new String[]{getActiveDbProfile()}; - } - } } diff --git a/src/main/webapp/mealForm.jsp b/src/main/webapp/mealForm.jsp index e9c01a94..98a6f487 100644 --- a/src/main/webapp/mealForm.jsp +++ b/src/main/webapp/mealForm.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index e1ce51e2..b9ac19fd 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> diff --git a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java new file mode 100644 index 00000000..2d819f84 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava; + +import org.springframework.lang.NonNull; +import org.springframework.test.context.ActiveProfilesResolver; + +//http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver +public class ActiveDbProfileResolver implements ActiveProfilesResolver { + @Override + public @NonNull + String[] resolve(@NonNull Class aClass) { + return new String[]{Profiles.getActiveDbProfile()}; + } +} diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 5202e9cf..8907fc0d 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -15,7 +15,7 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; -import ru.javawebinar.topjava.Profiles; +import ru.javawebinar.topjava.ActiveDbProfileResolver; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -35,7 +35,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(resolver = Profiles.ActiveDbProfileResolver.class) +@ActiveProfiles(resolver = ActiveDbProfileResolver.class) public class MealServiceTest { private static final Logger log = getLogger("result"); @@ -122,7 +122,7 @@ public void update() { // @Test public void updateNotOwn() { - NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); + NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(getUpdated(), ADMIN_ID)); Assert.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index a7651c06..496dbe73 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -11,7 +11,7 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; -import ru.javawebinar.topjava.Profiles; +import ru.javawebinar.topjava.ActiveDbProfileResolver; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -28,7 +28,7 @@ }) @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(resolver = Profiles.ActiveDbProfileResolver.class) +@ActiveProfiles(resolver = ActiveDbProfileResolver.class) public class UserServiceTest { @Autowired diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java index 2a15928d..9102d210 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -24,7 +24,7 @@ public class InMemoryAdminRestControllerSpringTest { private InMemoryUserRepository repository; @Before - public void setUp() { + public void setup() { repository.init(); } diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java index 7cc2a833..14c60d96 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -34,7 +34,7 @@ public static void afterClass() { } @Before - public void setUp() { + public void setup() { // re-initialize repository.init(); } From f83af4e0abfb23e9c7b4ddcc3ac74d65890eb6ed Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:24:47 +0300 Subject: [PATCH 054/144] 6_01_HW5_data_jpa.patch --- .../datajpa/CrudMealRepository.java | 21 ++++++++++++++- .../datajpa/DataJpaMealRepository.java | 26 +++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java index a3659675..1f868274 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -1,7 +1,26 @@ package ru.javawebinar.topjava.repository.datajpa; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; +import java.time.LocalDateTime; +import java.util.List; + +@Transactional(readOnly = true) public interface CrudMealRepository extends JpaRepository { -} + + @Modifying + @Transactional + @Query("DELETE FROM Meal m WHERE m.id=:id AND m.user.id=:userId") + int delete(@Param("id") int id, @Param("userId") int userId); + + @Query("SELECT m FROM Meal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC") + List getAll(@Param("userId") int userId); + + @Query("SELECT m from Meal m WHERE m.user.id=:userId AND m.dateTime >= :startDate AND m.dateTime < :endDate ORDER BY m.dateTime DESC") + List getBetweenHalfOpen(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("userId") int userId); +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java index d1b4c8ef..a57c9742 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.repository.datajpa; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -10,34 +11,43 @@ @Repository public class DataJpaMealRepository implements MealRepository { - private final CrudMealRepository crudRepository; + private final CrudMealRepository crudMealRepository; + private final CrudUserRepository crudUserRepository; - public DataJpaMealRepository(CrudMealRepository crudRepository) { - this.crudRepository = crudRepository; + public DataJpaMealRepository(CrudMealRepository crudMealRepository, CrudUserRepository crudUserRepository) { + this.crudMealRepository = crudMealRepository; + this.crudUserRepository = crudUserRepository; } @Override + @Transactional public Meal save(Meal meal, int userId) { - return null; + if (!meal.isNew() && get(meal.getId(), userId) == null) { + return null; + } + meal.setUser(crudUserRepository.getOne(userId)); + return crudMealRepository.save(meal); } @Override public boolean delete(int id, int userId) { - return false; + return crudMealRepository.delete(id, userId) != 0; } @Override public Meal get(int id, int userId) { - return null; + return crudMealRepository.findById(id) + .filter(meal -> meal.getUser().getId() == userId) + .orElse(null); } @Override public List getAll(int userId) { - return null; + return crudMealRepository.getAll(userId); } @Override public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - return null; + return crudMealRepository.getBetweenHalfOpen(startDateTime, endDateTime, userId); } } From 23229b7b472e9500fcfb0bdb11997ee08a36cc4a Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:26:18 +0300 Subject: [PATCH 055/144] 6_02_HW5_profile_test.patch --- src/main/resources/spring/spring-db.xml | 84 +++++++++---------- ...Test.java => AbstractMealServiceTest.java} | 37 +------- .../topjava/service/AbstractServiceTest.java | 54 ++++++++++++ ...Test.java => AbstractUserServiceTest.java} | 16 +--- .../datajpa/DataJpaMealServiceTest.java | 10 +++ .../datajpa/DataJpaUserServiceTest.java | 10 +++ .../service/jdbc/JdbcMealServiceTest.java | 10 +++ .../service/jdbc/JdbcUserServiceTest.java | 10 +++ .../service/jpa/JpaMealServiceTest.java | 10 +++ .../service/jpa/JpaUserServiceTest.java | 10 +++ 10 files changed, 158 insertions(+), 93 deletions(-) rename src/test/java/ru/javawebinar/topjava/service/{MealServiceTest.java => AbstractMealServiceTest.java} (68%) create mode 100644 src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java rename src/test/java/ru/javawebinar/topjava/service/{UserServiceTest.java => AbstractUserServiceTest.java} (77%) create mode 100644 src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 4106dbf1..7a334ace 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -11,53 +11,12 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -92,9 +51,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java similarity index 68% rename from src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index 8907fc0d..cd2ef70c 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -1,56 +1,21 @@ package ru.javawebinar.topjava.service; -import org.junit.AfterClass; import org.junit.Assert; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.Stopwatch; -import org.junit.runner.Description; -import org.junit.runner.RunWith; -import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; -import org.springframework.test.context.junit4.SpringRunner; -import ru.javawebinar.topjava.ActiveDbProfileResolver; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.time.LocalDate; import java.time.Month; -import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertThrows; -import static org.slf4j.LoggerFactory.getLogger; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_ID; -@ContextConfiguration({ - "classpath:spring/spring-app.xml", - "classpath:spring/spring-db.xml" -}) -@RunWith(SpringRunner.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(resolver = ActiveDbProfileResolver.class) -public class MealServiceTest { - private static final Logger log = getLogger("result"); - - private static final StringBuilder results = new StringBuilder(); - - @Rule - // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev - public final Stopwatch stopwatch = new Stopwatch() { - @Override - protected void finished(long nanos, Description description) { - String result = String.format("\n%-25s %7d", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); - results.append(result); - log.info(result + " ms\n"); - } - }; +public abstract class AbstractMealServiceTest extends AbstractServiceTest { @Autowired private MealService service; diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java new file mode 100644 index 00000000..7859098b --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -0,0 +1,54 @@ +package ru.javawebinar.topjava.service; + +import org.junit.AfterClass; +import org.junit.Rule; +import org.junit.rules.Stopwatch; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.ActiveDbProfileResolver; + +import java.util.concurrent.TimeUnit; + +import static org.slf4j.LoggerFactory.getLogger; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringRunner.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(resolver = ActiveDbProfileResolver.class) +abstract public class AbstractServiceTest { + private static final Logger log = getLogger("result"); + + private static final StringBuilder results = new StringBuilder(); + + @Rule + // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev + public Stopwatch stopwatch = new Stopwatch() { + @Override + protected void finished(long nanos, Description description) { + String result = String.format("%-95s %7d", description.getDisplayName(), TimeUnit.NANOSECONDS.toMillis(nanos)); + results.append(result).append('\n'); + log.info(result + " ms\n"); + } + }; + + + // https://dzone.com/articles/applying-new-jdk-11-string-methods + private static final String DELIM = "-".repeat(103); + + @AfterClass + public static void printResult() { + log.info("\n" + DELIM + + "\nTest Duration, ms" + + "\n" + DELIM + "\n" + results + DELIM + "\n"); + results.setLength(0); + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java similarity index 77% rename from src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 496dbe73..5382a5d3 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -2,16 +2,9 @@ import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.dao.DataAccessException; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; -import org.springframework.test.context.junit4.SpringRunner; -import ru.javawebinar.topjava.ActiveDbProfileResolver; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -22,14 +15,7 @@ import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.UserTestData.*; -@ContextConfiguration({ - "classpath:spring/spring-app.xml", - "classpath:spring/spring-db.xml" -}) -@RunWith(SpringRunner.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(resolver = ActiveDbProfileResolver.class) -public class UserServiceTest { +public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired private UserService service; diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java new file mode 100644 index 00000000..e28cc6d6 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.datajpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.DATAJPA; + +@ActiveProfiles(DATAJPA) +public class DataJpaMealServiceTest extends AbstractMealServiceTest { +} diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java new file mode 100644 index 00000000..a0757e73 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.datajpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserServiceTest; + +import static ru.javawebinar.topjava.Profiles.DATAJPA; + +@ActiveProfiles(DATAJPA) +public class DataJpaUserServiceTest extends AbstractUserServiceTest { +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java new file mode 100644 index 00000000..9ff4ae61 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jdbc; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.JDBC; + +@ActiveProfiles(JDBC) +public class JdbcMealServiceTest extends AbstractMealServiceTest { +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java new file mode 100644 index 00000000..419f68ed --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jdbc; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserServiceTest; + +import static ru.javawebinar.topjava.Profiles.JDBC; + +@ActiveProfiles(JDBC) +public class JdbcUserServiceTest extends AbstractUserServiceTest { +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java new file mode 100644 index 00000000..70e7bf86 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.JPA; + +@ActiveProfiles(JPA) +public class JpaMealServiceTest extends AbstractMealServiceTest { +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java new file mode 100644 index 00000000..d1b3e469 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserServiceTest; + +import static ru.javawebinar.topjava.Profiles.JPA; + +@ActiveProfiles(JPA) +public class JpaUserServiceTest extends AbstractUserServiceTest { +} \ No newline at end of file From 09aa9c71ef262fae0d23e4d6371bd2e4026fd7fd Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:27:03 +0300 Subject: [PATCH 056/144] 6_03_extract_rules.patch --- .../ru/javawebinar/topjava/TimingRules.java | 42 +++++++++++++++++++ .../topjava/service/AbstractServiceTest.java | 37 +++------------- 2 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/TimingRules.java diff --git a/src/test/java/ru/javawebinar/topjava/TimingRules.java b/src/test/java/ru/javawebinar/topjava/TimingRules.java new file mode 100644 index 00000000..fdd3d877 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/TimingRules.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava; + +import org.junit.rules.ExternalResource; +import org.junit.rules.Stopwatch; +import org.junit.runner.Description; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; + +public class TimingRules { + private static final Logger log = LoggerFactory.getLogger("result"); + + private static final StringBuilder results = new StringBuilder(); + + // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev + public static final Stopwatch STOPWATCH = new Stopwatch() { + @Override + protected void finished(long nanos, Description description) { + String result = String.format("%-95s %7d", description.getDisplayName(), TimeUnit.NANOSECONDS.toMillis(nanos)); + results.append(result).append('\n'); + log.info(result + " ms\n"); + } + }; + + // https://dzone.com/articles/applying-new-jdk-11-string-methods + private static final String DELIM = "-".repeat(103); + + public static final ExternalResource SUMMARY = new ExternalResource() { + @Override + protected void before() throws Throwable { + results.setLength(0); + } + + @Override + protected void after() { + log.info("\n" + DELIM + + "\nTest Duration, ms" + + "\n" + DELIM + "\n" + results + DELIM + "\n"); + } + }; +} diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 7859098b..8bd916bc 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -1,21 +1,17 @@ package ru.javawebinar.topjava.service; -import org.junit.AfterClass; +import org.junit.ClassRule; import org.junit.Rule; +import org.junit.rules.ExternalResource; import org.junit.rules.Stopwatch; -import org.junit.runner.Description; import org.junit.runner.RunWith; -import org.slf4j.Logger; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; import ru.javawebinar.topjava.ActiveDbProfileResolver; - -import java.util.concurrent.TimeUnit; - -import static org.slf4j.LoggerFactory.getLogger; +import ru.javawebinar.topjava.TimingRules; @ContextConfiguration({ "classpath:spring/spring-app.xml", @@ -25,30 +21,9 @@ @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) @ActiveProfiles(resolver = ActiveDbProfileResolver.class) abstract public class AbstractServiceTest { - private static final Logger log = getLogger("result"); - - private static final StringBuilder results = new StringBuilder(); + @ClassRule + public static ExternalResource summary = TimingRules.SUMMARY; @Rule - // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev - public Stopwatch stopwatch = new Stopwatch() { - @Override - protected void finished(long nanos, Description description) { - String result = String.format("%-95s %7d", description.getDisplayName(), TimeUnit.NANOSECONDS.toMillis(nanos)); - results.append(result).append('\n'); - log.info(result + " ms\n"); - } - }; - - - // https://dzone.com/articles/applying-new-jdk-11-string-methods - private static final String DELIM = "-".repeat(103); - - @AfterClass - public static void printResult() { - log.info("\n" + DELIM + - "\nTest Duration, ms" + - "\n" + DELIM + "\n" + results + DELIM + "\n"); - results.setLength(0); - } + public Stopwatch stopwatch = TimingRules.STOPWATCH; } \ No newline at end of file From 7dcd998feeb90f2330c4bd083f25069af9c81fcd Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:42:48 +0300 Subject: [PATCH 057/144] 6_04_HW5_optional_fix_jdbc_profiles.patch --- .../repository/jdbc/JdbcMealRepository.java | 42 +++++++++++++++---- .../javawebinar/topjava/web/MealServlet.java | 9 ++-- .../ru/javawebinar/topjava/SpringMain.java | 9 ++-- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index fa26d566..f9ca4878 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.repository.jdbc; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; @@ -9,14 +9,15 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; -@Repository -public class JdbcMealRepository implements MealRepository { +public abstract class JdbcMealRepository implements MealRepository { private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); @@ -26,8 +27,7 @@ public class JdbcMealRepository implements MealRepository { private final SimpleJdbcInsert insertMeal; - @Autowired - public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.insertMeal = new SimpleJdbcInsert(jdbcTemplate) .withTableName("meals") .usingGeneratedKeyColumns("id"); @@ -36,13 +36,41 @@ public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; } + protected abstract T toDbDateTime(LocalDateTime ldt); + + @Repository + @Profile(Profiles.POSTGRES_DB) + public static class Java8JdbcMealRepository extends JdbcMealRepository { + public Java8JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + super(jdbcTemplate, namedParameterJdbcTemplate); + } + + @Override + protected LocalDateTime toDbDateTime(LocalDateTime ldt) { + return ldt; + } + } + + @Repository + @Profile(Profiles.HSQL_DB) + public static class TimestampJdbcMealRepository extends JdbcMealRepository { + public TimestampJdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + super(jdbcTemplate, namedParameterJdbcTemplate); + } + + @Override + protected Timestamp toDbDateTime(LocalDateTime ldt) { + return Timestamp.valueOf(ldt); + } + } + @Override public Meal save(Meal meal, int userId) { MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", meal.getId()) .addValue("description", meal.getDescription()) .addValue("calories", meal.getCalories()) - .addValue("date_time", meal.getDateTime()) + .addValue("date_time", toDbDateTime(meal.getDateTime())) .addValue("user_id", userId); if (meal.isNew()) { @@ -81,6 +109,6 @@ public List getAll(int userId) { public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { return jdbcTemplate.query( "SELECT * FROM meals WHERE user_id=? AND date_time >= ? AND date_time < ? ORDER BY date_time DESC", - ROW_MAPPER, userId, startDateTime, endDateTime); + ROW_MAPPER, userId, toDbDateTime(startDateTime), toDbDateTime(endDateTime)); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index defeb1d7..13f092e0 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,8 +1,8 @@ package ru.javawebinar.topjava.web; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.StringUtils; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.web.meal.MealRestController; @@ -22,12 +22,15 @@ public class MealServlet extends HttpServlet { - private ConfigurableApplicationContext springContext; + private ClassPathXmlApplicationContext springContext; private MealRestController mealController; @Override public void init() { - springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/spring-db.xml"); + springContext = new ClassPathXmlApplicationContext(new String[]{"spring/spring-app.xml", "spring/spring-db.xml"}, false); +// springContext.setConfigLocations("spring/spring-app.xml", "spring/spring-db.xml"); + springContext.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.REPOSITORY_IMPLEMENTATION); + springContext.refresh(); mealController = springContext.getBean(MealRestController.class); } diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index e41f1ae1..d809f025 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -1,7 +1,6 @@ package ru.javawebinar.topjava; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.context.support.GenericXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.MealTo; @@ -17,7 +16,11 @@ public class SpringMain { public static void main(String[] args) { // java 7 automatic resource management (ARM) - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml")) { + try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { + appCtx.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.REPOSITORY_IMPLEMENTATION); + appCtx.load("spring/spring-app.xml", "spring/spring-db.xml"); + appCtx.refresh(); + System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email@mail.ru", "password", Role.ADMIN)); From f04d5e2062faecefb42525e0e5f46b90b730d0d1 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:43:15 +0300 Subject: [PATCH 058/144] 6_05_update_hsqldb.patch --- pom.xml | 2 +- .../repository/jdbc/JdbcMealRepository.java | 40 +++---------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/pom.xml b/pom.xml index 46c45e0e..fb4635ad 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.hsqldb hsqldb - 2.3.4 + 2.5.1 diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index f9ca4878..1f3fe5ff 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.repository.jdbc; -import org.springframework.context.annotation.Profile; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; @@ -9,15 +8,14 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; -import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; -public abstract class JdbcMealRepository implements MealRepository { +@Repository +public class JdbcMealRepository implements MealRepository { private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); @@ -27,7 +25,7 @@ public abstract class JdbcMealRepository implements MealRepository { private final SimpleJdbcInsert insertMeal; - JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.insertMeal = new SimpleJdbcInsert(jdbcTemplate) .withTableName("meals") .usingGeneratedKeyColumns("id"); @@ -36,41 +34,13 @@ public abstract class JdbcMealRepository implements MealRepository { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; } - protected abstract T toDbDateTime(LocalDateTime ldt); - - @Repository - @Profile(Profiles.POSTGRES_DB) - public static class Java8JdbcMealRepository extends JdbcMealRepository { - public Java8JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { - super(jdbcTemplate, namedParameterJdbcTemplate); - } - - @Override - protected LocalDateTime toDbDateTime(LocalDateTime ldt) { - return ldt; - } - } - - @Repository - @Profile(Profiles.HSQL_DB) - public static class TimestampJdbcMealRepository extends JdbcMealRepository { - public TimestampJdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { - super(jdbcTemplate, namedParameterJdbcTemplate); - } - - @Override - protected Timestamp toDbDateTime(LocalDateTime ldt) { - return Timestamp.valueOf(ldt); - } - } - @Override public Meal save(Meal meal, int userId) { MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", meal.getId()) .addValue("description", meal.getDescription()) .addValue("calories", meal.getCalories()) - .addValue("date_time", toDbDateTime(meal.getDateTime())) + .addValue("date_time", meal.getDateTime()) .addValue("user_id", userId); if (meal.isNew()) { @@ -109,6 +79,6 @@ public List getAll(int userId) { public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { return jdbcTemplate.query( "SELECT * FROM meals WHERE user_id=? AND date_time >= ? AND date_time < ? ORDER BY date_time DESC", - ROW_MAPPER, userId, toDbDateTime(startDateTime), toDbDateTime(endDateTime)); + ROW_MAPPER, userId, startDateTime, endDateTime); } } From 19b5c02449eb3ae0cf9ad56f19f92df9d3957a5a Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:44:00 +0300 Subject: [PATCH 059/144] 6_06_HW5_optional_fetch_join.patch --- .../ru/javawebinar/topjava/model/User.java | 13 ++++++++---- .../topjava/repository/MealRepository.java | 4 ++++ .../topjava/repository/UserRepository.java | 4 ++++ .../datajpa/CrudMealRepository.java | 3 +++ .../datajpa/CrudUserRepository.java | 3 +++ .../datajpa/DataJpaMealRepository.java | 5 +++++ .../datajpa/DataJpaUserRepository.java | 5 +++++ .../topjava/service/MealService.java | 4 ++++ .../topjava/service/UserService.java | 4 ++++ .../ru/javawebinar/topjava/UserTestData.java | 2 +- .../service/AbstractMealServiceTest.java | 2 +- .../service/AbstractUserServiceTest.java | 2 +- .../datajpa/DataJpaMealServiceTest.java | 20 +++++++++++++++++++ .../datajpa/DataJpaUserServiceTest.java | 20 +++++++++++++++++++ 14 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 20acabf2..a7862af2 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -8,10 +8,7 @@ import org.springframework.util.CollectionUtils; import javax.persistence.*; -import java.util.Collection; -import java.util.Date; -import java.util.EnumSet; -import java.util.Set; +import java.util.*; import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; @@ -57,6 +54,10 @@ public class User extends AbstractNamedEntity { @Range(min = 10, max = 10000) private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") + @OrderBy("dateTime DESC") + private List meals; + public User() { } @@ -126,6 +127,10 @@ public void setRoles(Collection roles) { this.roles = CollectionUtils.isEmpty(roles) ? EnumSet.noneOf(Role.class) : EnumSet.copyOf(roles); } + public List getMeals() { + return meals; + } + @Override public String toString() { return "User{" + diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index 9461d5f9..1ad7f8d9 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -20,4 +20,8 @@ public interface MealRepository { // ORDERED dateTime desc List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId); + + default Meal getWithUser(int id, int userId) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java index 13836978..9fecbdda 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -18,4 +18,8 @@ public interface UserRepository { User getByEmail(String email); List getAll(); + + default User getWithMeals(int id) { + throw new UnsupportedOperationException(); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java index 1f868274..9aeef134 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -23,4 +23,7 @@ public interface CrudMealRepository extends JpaRepository { @Query("SELECT m from Meal m WHERE m.user.id=:userId AND m.dateTime >= :startDate AND m.dateTime < :endDate ORDER BY m.dateTime DESC") List getBetweenHalfOpen(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("userId") int userId); + + @Query("SELECT m FROM Meal m JOIN FETCH m.user WHERE m.id = ?1 and m.user.id = ?2") + Meal getWithUser(int id, int userId); } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java index 24c42a81..8955fc41 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -16,4 +16,7 @@ public interface CrudUserRepository extends JpaRepository { int delete(@Param("id") int id); User getByEmail(String email); + + @Query("SELECT u FROM User u LEFT JOIN FETCH u.meals WHERE u.id = ?1") + User getWithMeals(int id); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java index a57c9742..dc93b47e 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java @@ -50,4 +50,9 @@ public List getAll(int userId) { public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { return crudMealRepository.getBetweenHalfOpen(startDateTime, endDateTime, userId); } + + @Override + public Meal getWithUser(int id, int userId) { + return crudMealRepository.getWithUser(id, userId); + } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java index b7c4dd81..a6e4f459 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java @@ -43,4 +43,9 @@ public User getByEmail(String email) { public List getAll() { return crudRepository.findAll(SORT_NAME_EMAIL); } + + @Override + public User getWithMeals(int id) { + return crudRepository.getWithMeals(id); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index df874378..5e08c9e5 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -47,4 +47,8 @@ public Meal create(Meal meal, int userId) { Assert.notNull(meal, "meal must not be null"); return repository.save(meal, userId); } + + public Meal getWithUser(int id, int userId) { + return checkNotFoundWithId(repository.getWithUser(id, userId), id); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index e0979b7e..2fa24876 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -51,4 +51,8 @@ public void update(User user) { Assert.notNull(user, "user must not be null"); checkNotFoundWithId(repository.save(user), user.id()); } + + public User getWithMeals(int id) { + return checkNotFoundWithId(repository.getWithMeals(id), id); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index b562b234..979401e0 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -9,7 +9,7 @@ import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { - public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "roles"); + public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "roles", "meals"); public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index cd2ef70c..ee216648 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -18,7 +18,7 @@ public abstract class AbstractMealServiceTest extends AbstractServiceTest { @Autowired - private MealService service; + protected MealService service; @AfterClass public static void printResult() { diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 5382a5d3..6b642831 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -18,7 +18,7 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired - private UserService service; + protected UserService service; @Autowired private CacheManager cacheManager; diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java index e28cc6d6..61847184 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java @@ -1,10 +1,30 @@ package ru.javawebinar.topjava.service.datajpa; +import org.junit.Assert; +import org.junit.Test; import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.service.AbstractMealServiceTest; +import ru.javawebinar.topjava.util.exception.NotFoundException; +import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.Profiles.DATAJPA; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.admin; @ActiveProfiles(DATAJPA) public class DataJpaMealServiceTest extends AbstractMealServiceTest { + @Test + public void getWithUser() { + Meal adminMeal = service.getWithUser(ADMIN_MEAL_ID, ADMIN_ID); + MEAL_MATCHER.assertMatch(adminMeal, adminMeal1); + UserTestData.USER_MATCHER.assertMatch(adminMeal.getUser(), admin); + } + + @Test + public void getWithUserNotFound() { + Assert.assertThrows(NotFoundException.class, + () -> service.getWithUser(1, ADMIN_ID)); + } } diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java index a0757e73..32bcf5ef 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -1,10 +1,30 @@ package ru.javawebinar.topjava.service.datajpa; +import org.junit.Assert; +import org.junit.Test; import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.MealTestData; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.AbstractUserServiceTest; +import ru.javawebinar.topjava.util.exception.NotFoundException; import static ru.javawebinar.topjava.Profiles.DATAJPA; +import static ru.javawebinar.topjava.UserTestData.USER_ID; +import static ru.javawebinar.topjava.UserTestData.USER_MATCHER; @ActiveProfiles(DATAJPA) public class DataJpaUserServiceTest extends AbstractUserServiceTest { + @Test + public void getWithMeals() { + User user = service.getWithMeals(USER_ID); + USER_MATCHER.assertMatch(user, UserTestData.user); + MealTestData.MEAL_MATCHER.assertMatch(user.getMeals(), MealTestData.meals); + } + + @Test + public void getWithMealsNotFound() { + Assert.assertThrows(NotFoundException.class, + () -> service.getWithMeals(1)); + } } \ No newline at end of file From 6f19ff003475e45f14a8798e8e5ff6a3ef3a0699 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:44:47 +0300 Subject: [PATCH 060/144] 6_07_HW5_graph_batch_size.patch --- src/main/java/ru/javawebinar/topjava/model/User.java | 7 ++++++- .../topjava/repository/datajpa/CrudUserRepository.java | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index a7862af2..54dee345 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -4,6 +4,9 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; + +import org.hibernate.annotations.BatchSize; + import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; @@ -15,7 +18,7 @@ @NamedQueries({ @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), - @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email"), + @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u ORDER BY u.name, u.email"), }) @Entity @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) @@ -48,6 +51,8 @@ public class User extends AbstractNamedEntity { uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "role"}, name = "user_roles_unique_idx")}) @Column(name = "role") @ElementCollection(fetch = FetchType.EAGER) +// @Fetch(FetchMode.SUBSELECT) + @BatchSize(size = 200) private Set roles; @Column(name = "calories_per_day", nullable = false, columnDefinition = "int default 2000") diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java index 8955fc41..806884a9 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.datajpa; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -11,12 +12,12 @@ public interface CrudUserRepository extends JpaRepository { @Transactional @Modifying -// @Query(name = User.DELETE) @Query("DELETE FROM User u WHERE u.id=:id") int delete(@Param("id") int id); User getByEmail(String email); - @Query("SELECT u FROM User u LEFT JOIN FETCH u.meals WHERE u.id = ?1") + @EntityGraph(attributePaths = {"meals", "roles"}) + @Query("SELECT u FROM User u WHERE u.id=?1") User getWithMeals(int id); } From 8a72f37e735cf9f91957ebb05126d8e490b83943 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 09:06:15 +0300 Subject: [PATCH 061/144] 6_08_add_test_validation.patch --- .../javawebinar/topjava/util/ValidationUtil.java | 9 +++++++++ .../topjava/service/AbstractMealServiceTest.java | 10 ++++++++++ .../topjava/service/AbstractServiceTest.java | 15 +++++++++++++++ .../topjava/service/AbstractUserServiceTest.java | 13 +++++++++++++ 4 files changed, 47 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 0f35f22a..35d83de8 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.util; +import org.springframework.core.NestedExceptionUtils; +import org.springframework.lang.NonNull; import ru.javawebinar.topjava.model.AbstractBaseEntity; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -42,4 +44,11 @@ public static void assureIdConsistent(AbstractBaseEntity entity, int id) { throw new IllegalArgumentException(entity + " must be with id=" + id); } } + + // https://stackoverflow.com/a/65442410/548473 + @NonNull + public static Throwable getRootCause(@NonNull Throwable t) { + Throwable rootCause = NestedExceptionUtils.getRootCause(t); + return rootCause != null ? rootCause : t; + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index ee216648..2e02f614 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -7,9 +7,11 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; +import javax.validation.ConstraintViolationException; import java.time.LocalDate; import java.time.Month; +import static java.time.LocalDateTime.of; import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; @@ -109,4 +111,12 @@ public void getBetweenInclusive() { public void getBetweenWithNullDates() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); } + + @Test + public void createWithException() throws Exception { + validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID)); + validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, null, "Description", 300), USER_ID)); + validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID)); + validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 5001), USER_ID)); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 8bd916bc..c53a5b71 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -13,6 +13,9 @@ import ru.javawebinar.topjava.ActiveDbProfileResolver; import ru.javawebinar.topjava.TimingRules; +import static org.junit.Assert.assertThrows; +import static ru.javawebinar.topjava.util.ValidationUtil.getRootCause; + @ContextConfiguration({ "classpath:spring/spring-app.xml", "classpath:spring/spring-db.xml" @@ -21,9 +24,21 @@ @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) @ActiveProfiles(resolver = ActiveDbProfileResolver.class) abstract public class AbstractServiceTest { + @ClassRule public static ExternalResource summary = TimingRules.SUMMARY; @Rule public Stopwatch stopwatch = TimingRules.STOPWATCH; + + // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 + public void validateRootCause(Class rootExceptionClass, Runnable runnable) { + assertThrows(rootExceptionClass, () -> { + try { + runnable.run(); + } catch (Exception e) { + throw getRootCause(e); + } + }); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 6b642831..0f7a92a9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -8,9 +8,13 @@ import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.JpaUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; +import javax.validation.ConstraintViolationException; +import java.util.Date; import java.util.List; +import java.util.Set; import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.UserTestData.*; @@ -84,4 +88,13 @@ public void getAll() { List all = service.getAll(); USER_MATCHER.assertMatch(all, admin, user); } + + @Test + public void createWithException() throws Exception { + validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, " ", "mail@yandex.ru", "password", Role.USER))); + validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", " ", "password", Role.USER))); + validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", " ", Role.USER))); + validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", "password", 9, true, new Date(), Set.of()))); + validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", "password", 10001, true, new Date(), Set.of()))); + } } \ No newline at end of file From 6bce8be017591fe345315e516435a597ddc9697d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 09:07:23 +0300 Subject: [PATCH 062/144] 6_09_hibernate_cache.patch --- pom.xml | 5 +++++ .../ru/javawebinar/topjava/model/User.java | 4 ++++ .../topjava/repository/JpaUtil.java | 22 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 8 +++++++ .../service/AbstractUserServiceTest.java | 5 +++++ 5 files changed, 44 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java diff --git a/pom.xml b/pom.xml index fb4635ad..7d616cfb 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,11 @@ hibernate-validator ${hibernate-validator.version} + + org.hibernate + hibernate-jcache + ${hibernate.version} + diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 54dee345..f5d180e6 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -6,6 +6,8 @@ import javax.validation.constraints.Size; import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; @@ -15,6 +17,7 @@ import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @NamedQueries({ @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), @@ -46,6 +49,7 @@ public class User extends AbstractNamedEntity { @NotNull private Date registered = new Date(); + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Enumerated(EnumType.STRING) @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "role"}, name = "user_roles_unique_idx")}) diff --git a/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java new file mode 100644 index 00000000..d740b9f7 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java @@ -0,0 +1,22 @@ +package ru.javawebinar.topjava.repository; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +public class JpaUtil { + + @PersistenceContext + private EntityManager em; + + public void clear2ndLevelHibernateCache() { + Session s = (Session) em.getDelegate(); + SessionFactory sf = s.getSessionFactory(); +// sf.evict(User.class); +// sf.getCache().evictEntity(User.class, BaseEntity.START_SEQ); +// sf.getCache().evictEntityRegion(User.class); + sf.getCache().evictAllRegions(); + } +} diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 7a334ace..b601f31f 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -73,6 +73,12 @@ + + + + + + @@ -87,6 +93,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 0f7a92a9..97594a87 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -15,6 +15,7 @@ import java.util.Date; import java.util.List; import java.util.Set; +import ru.javawebinar.topjava.repository.JpaUtil; import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.UserTestData.*; @@ -27,9 +28,13 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired private CacheManager cacheManager; + @Autowired + protected JpaUtil jpaUtil; + @Before public void setup() { cacheManager.getCache("users").clear(); + jpaUtil.clear2ndLevelHibernateCache(); } @Test From 3ac0add34cdabdbc6b2b1835d6e366db61ddb8bd Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 09:34:59 +0300 Subject: [PATCH 063/144] 6_10_cascade_ddl.patch --- .../topjava/model/AbstractBaseEntity.java | 1 + .../java/ru/javawebinar/topjava/model/Meal.java | 3 +++ .../java/ru/javawebinar/topjava/model/User.java | 14 ++++++++++---- src/main/resources/spring/spring-db.xml | 6 ++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 2ab30f34..acb11612 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -14,6 +14,7 @@ public abstract class AbstractBaseEntity implements Persistable { @Id @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = START_SEQ) + // @Column(name = "id", unique = true, nullable = false, columnDefinition = "integer default nextval('global_seq')") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") // See https://hibernate.atlassian.net/browse/HHH-3718 and https://hibernate.atlassian.net/browse/HHH-12034 // Proxy initialization when accessing its identifier managed now by JPA_PROXY_COMPLIANCE setting diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 2451c945..c00282b1 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.model; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import org.hibernate.validator.constraints.Range; import javax.persistence.*; @@ -42,6 +44,7 @@ public class Meal extends AbstractBaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) @NotNull private User user; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index f5d180e6..6bd6d743 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,14 +1,18 @@ package ru.javawebinar.topjava.model; +import javax.persistence.Entity; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.OrderBy; +import javax.persistence.Table; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.hibernate.annotations.BatchSize; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.*; +import org.hibernate.annotations.Cache; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; @@ -57,13 +61,15 @@ public class User extends AbstractNamedEntity { @ElementCollection(fetch = FetchType.EAGER) // @Fetch(FetchMode.SUBSELECT) @BatchSize(size = 200) + @JoinColumn(name = "user_id") //https://stackoverflow.com/a/62848296/548473 + @OnDelete(action= OnDeleteAction.CASCADE) private Set roles; @Column(name = "calories_per_day", nullable = false, columnDefinition = "int default 2000") @Range(min = 10, max = 10000) private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; - @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) @OrderBy("dateTime DESC") private List meals; diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index b601f31f..29211c98 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -79,6 +79,12 @@ + From 55981039b0ef229d2b08b3c614e066fe68d522ee Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 09:55:24 +0300 Subject: [PATCH 064/144] 6_11_spring_web.patch --- pom.xml | 11 ++++++++--- .../ru/javawebinar/topjava/web/MealServlet.java | 16 +++------------- .../ru/javawebinar/topjava/web/UserServlet.java | 16 +++++++++++++++- src/main/webapp/WEB-INF/web.xml | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 7d616cfb..dfc95e4b 100644 --- a/pom.xml +++ b/pom.xml @@ -149,9 +149,9 @@ - javax.servlet - javax.servlet-api - 4.0.1 + org.apache.tomcat + tomcat-servlet-api + ${tomcat.version} provided @@ -161,6 +161,11 @@ 1.2 + + org.springframework + spring-web + + junit diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 13f092e0..fb667d35 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,8 +1,8 @@ package ru.javawebinar.topjava.web; -import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.StringUtils; -import ru.javawebinar.topjava.Profiles; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.web.meal.MealRestController; @@ -22,24 +22,14 @@ public class MealServlet extends HttpServlet { - private ClassPathXmlApplicationContext springContext; private MealRestController mealController; @Override public void init() { - springContext = new ClassPathXmlApplicationContext(new String[]{"spring/spring-app.xml", "spring/spring-db.xml"}, false); -// springContext.setConfigLocations("spring/spring-app.xml", "spring/spring-db.xml"); - springContext.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.REPOSITORY_IMPLEMENTATION); - springContext.refresh(); + WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); mealController = springContext.getBean(MealRestController.class); } - @Override - public void destroy() { - springContext.close(); - super.destroy(); - } - @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 22602340..909e6e49 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,7 +1,11 @@ package ru.javawebinar.topjava.web; import org.slf4j.Logger; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; +import ru.javawebinar.topjava.web.user.AdminRestController; +import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -13,6 +17,15 @@ public class UserServlet extends HttpServlet { private static final Logger log = getLogger(UserServlet.class); + private AdminRestController adminController; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); + adminController = springContext.getBean(AdminRestController.class); + } + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int userId = Integer.parseInt(request.getParameter("userId")); @@ -22,7 +35,8 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - log.debug("forward to users"); + log.debug("getAll"); + request.setAttribute("users", adminController.getAll()); request.getRequestDispatcher("/users.jsp").forward(request, response); } } diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index bd98d3bf..0a3127ac 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -6,6 +6,22 @@ Topjava + + spring.profiles.default + postgres,datajpa + + + + contextConfigLocation + + classpath:spring/spring-app.xml + classpath:spring/spring-db.xml + + + + + org.springframework.web.context.ContextLoaderListener + userServlet ru.javawebinar.topjava.web.UserServlet From 509441d876dc6a58cedc72d7dac81153f231bf1c Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 14:10:18 +0300 Subject: [PATCH 065/144] 6_12_jsp_jstl_i18n.patch --- src/main/resources/messages/app.properties | 12 ++++++ src/main/resources/messages/app_ru.properties | 12 ++++++ src/main/webapp/css/style.css | 8 ++++ src/main/webapp/fragments/bodyHeader.jsp | 7 +++ src/main/webapp/fragments/footer.jsp | 5 +++ src/main/webapp/fragments/headTag.jsp | 10 +++++ src/main/webapp/index.html | 18 -------- src/main/webapp/index.jsp | 23 ++++++++++ src/main/webapp/meals.jsp | 2 +- src/main/webapp/users.jsp | 43 ++++++++++++++++--- 10 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 src/main/resources/messages/app.properties create mode 100644 src/main/resources/messages/app_ru.properties create mode 100644 src/main/webapp/fragments/bodyHeader.jsp create mode 100644 src/main/webapp/fragments/footer.jsp create mode 100644 src/main/webapp/fragments/headTag.jsp delete mode 100644 src/main/webapp/index.html create mode 100644 src/main/webapp/index.jsp diff --git a/src/main/resources/messages/app.properties b/src/main/resources/messages/app.properties new file mode 100644 index 00000000..ccd4c66b --- /dev/null +++ b/src/main/resources/messages/app.properties @@ -0,0 +1,12 @@ +app.title=Calories management +app.home=Home +app.footer=Internship Spring 5/JPA Enterprise (Topjava) application +app.login=Login as +user.title=Users +user.name=Name +user.email=Email +user.roles=Roles +user.active=Active +user.registered=Registered +meal.title=Meals +common.select=Select \ No newline at end of file diff --git a/src/main/resources/messages/app_ru.properties b/src/main/resources/messages/app_ru.properties new file mode 100644 index 00000000..0db1b2c9 --- /dev/null +++ b/src/main/resources/messages/app_ru.properties @@ -0,0 +1,12 @@ +app.title= +app.home= +app.footer= Spring 5/JPA Enterprise (Topjava) +app.login= +user.title= +user.name= +user.email= +user.roles= +user.active= +user.registered= +meal.title= +common.select= \ No newline at end of file diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css index 26a14ce4..ef7ff212 100644 --- a/src/main/webapp/css/style.css +++ b/src/main/webapp/css/style.css @@ -22,3 +22,11 @@ tr[data-mealExcess="false"] { tr[data-mealExcess="true"] { color: red; } + +header, footer { + background: none repeat scroll 0 0 #A6C9E2; + color: #2E6E9E; + font-size: 20px; + padding: 5px 20px; + margin: 6px 0; +} diff --git a/src/main/webapp/fragments/bodyHeader.jsp b/src/main/webapp/fragments/bodyHeader.jsp new file mode 100644 index 00000000..345cc15a --- /dev/null +++ b/src/main/webapp/fragments/bodyHeader.jsp @@ -0,0 +1,7 @@ +<%@page contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

    + | | +
    \ No newline at end of file diff --git a/src/main/webapp/fragments/footer.jsp b/src/main/webapp/fragments/footer.jsp new file mode 100644 index 00000000..c59bff2e --- /dev/null +++ b/src/main/webapp/fragments/footer.jsp @@ -0,0 +1,5 @@ +<%@page contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + +
    +
    \ No newline at end of file diff --git a/src/main/webapp/fragments/headTag.jsp b/src/main/webapp/fragments/headTag.jsp new file mode 100644 index 00000000..c1532783 --- /dev/null +++ b/src/main/webapp/fragments/headTag.jsp @@ -0,0 +1,10 @@ +<%@page contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> + + + + + <fmt:message key="app.title"/> + + \ No newline at end of file diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html deleted file mode 100644 index ce44f5b0..00000000 --- a/src/main/webapp/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - Java Enterprise (Topjava) - - -

    Проект Java Enterprise (Topjava)

    -
    -
    - Meals of  - - -
    - - diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 00000000..654d7b23 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + + + + +
    +
    +
    + : + + +
    + + + \ No newline at end of file diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index b9ac19fd..702d9ef3 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -9,7 +9,7 @@
    -

    Home

    +

    Home


    Meals

    diff --git a/src/main/webapp/users.jsp b/src/main/webapp/users.jsp index 650c8dda..391fb2b5 100644 --- a/src/main/webapp/users.jsp +++ b/src/main/webapp/users.jsp @@ -1,11 +1,40 @@ <%@ page contentType="text/html;charset=UTF-8" %> - - - Users - +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + + -

    Home

    -
    -

    Users

    + + +
    +

    + + + + + + + + + + + + + + + + + + + + + +
    ${user.email}${user.roles}<%=user.isEnabled()%> +
    +
    + \ No newline at end of file From ee42f229f558b1748c7b92f84f2020f0889b9856 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 14:31:11 +0300 Subject: [PATCH 066/144] 6_13_tomcat_pool_jndi_cargo.patch --- pom.xml | 42 ++++++++++++++++++ src/main/resources/db/tomcat.properties | 5 +++ src/main/resources/spring/spring-db.xml | 9 +++- src/main/resources/tomcat/context.xml | 57 +++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/tomcat.properties create mode 100644 src/main/resources/tomcat/context.xml diff --git a/pom.xml b/pom.xml index dfc95e4b..475218a2 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,48 @@ -Dfile.encoding=UTF-8 + + + + + org.codehaus.cargo + cargo-maven3-plugin + 1.9.2 + + + tomcat9x + + UTF-8 + tomcat,datajpa + + + + org.postgresql + postgresql + + + + + + + src/main/resources/tomcat/context.xml + conf/Catalina/localhost/ + ${project.build.finalName}.xml + + + + + + ru.javawebinar + topjava + war + + ${project.build.finalName} + + + + + diff --git a/src/main/resources/db/tomcat.properties b/src/main/resources/db/tomcat.properties new file mode 100644 index 00000000..2e073681 --- /dev/null +++ b/src/main/resources/db/tomcat.properties @@ -0,0 +1,5 @@ +database.init=false +jdbc.initLocation=initDB.sql +jpa.showSql=true +hibernate.format_sql=true +hibernate.use_sql_comments=true \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 29211c98..2b62f074 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -5,11 +5,13 @@ xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" + xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd - http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> + http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd + http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> @@ -50,6 +52,11 @@ p:password="${database.password}"/> + + + + + diff --git a/src/main/resources/tomcat/context.xml b/src/main/resources/tomcat/context.xml new file mode 100644 index 00000000..9311d590 --- /dev/null +++ b/src/main/resources/tomcat/context.xml @@ -0,0 +1,57 @@ + + + + + + + + WEB-INF/web.xml + ${catalina.base}/conf/web.xml + + + + + + + + + From 19883f8a1d145db820fa2b8b67da007cda5b84ac Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 15:03:43 +0300 Subject: [PATCH 067/144] 6_14_spring_webmvc.patch --- pom.xml | 2 +- .../topjava/web/RootController.java | 34 +++++++++++++++ .../javawebinar/topjava/web/UserServlet.java | 42 ------------------- src/main/resources/logback.xml | 1 + src/main/resources/spring/spring-app.xml | 3 -- src/main/resources/spring/spring-mvc.xml | 18 ++++++++ .../jsp}/fragments/bodyHeader.jsp | 2 +- .../{ => WEB-INF/jsp}/fragments/footer.jsp | 0 .../{ => WEB-INF/jsp}/fragments/headTag.jsp | 2 +- src/main/webapp/{ => WEB-INF/jsp}/index.jsp | 0 .../webapp/{ => WEB-INF/jsp}/mealForm.jsp | 0 src/main/webapp/{ => WEB-INF/jsp}/meals.jsp | 0 src/main/webapp/{ => WEB-INF/jsp}/users.jsp | 0 src/main/webapp/WEB-INF/web.xml | 28 +++++-------- src/main/webapp/{ => resources}/css/style.css | 0 15 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/RootController.java delete mode 100644 src/main/java/ru/javawebinar/topjava/web/UserServlet.java create mode 100644 src/main/resources/spring/spring-mvc.xml rename src/main/webapp/{ => WEB-INF/jsp}/fragments/bodyHeader.jsp (66%) rename src/main/webapp/{ => WEB-INF/jsp}/fragments/footer.jsp (100%) rename src/main/webapp/{ => WEB-INF/jsp}/fragments/headTag.jsp (85%) rename src/main/webapp/{ => WEB-INF/jsp}/index.jsp (100%) rename src/main/webapp/{ => WEB-INF/jsp}/mealForm.jsp (100%) rename src/main/webapp/{ => WEB-INF/jsp}/meals.jsp (100%) rename src/main/webapp/{ => WEB-INF/jsp}/users.jsp (100%) rename src/main/webapp/{ => resources}/css/style.css (100%) diff --git a/pom.xml b/pom.xml index 475218a2..6d272b4f 100644 --- a/pom.xml +++ b/pom.xml @@ -205,7 +205,7 @@ org.springframework - spring-web + spring-webmvc diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java new file mode 100644 index 00000000..32726bc8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -0,0 +1,34 @@ +package ru.javawebinar.topjava.web; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import ru.javawebinar.topjava.service.UserService; + +import javax.servlet.http.HttpServletRequest; + +@Controller +public class RootController { + @Autowired + private UserService service; + + @GetMapping("/") + public String root() { + return "index"; + } + + @GetMapping("/users") + public String getUsers(Model model) { + model.addAttribute("users", service.getAll()); + return "users"; + } + + @PostMapping("/users") + public String setUser(HttpServletRequest request) { + int userId = Integer.parseInt(request.getParameter("userId")); + SecurityUtil.setAuthUserId(userId); + return "redirect:meals"; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java deleted file mode 100644 index 909e6e49..00000000 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.slf4j.Logger; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; -import ru.javawebinar.topjava.web.user.AdminRestController; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -import static org.slf4j.LoggerFactory.getLogger; - -public class UserServlet extends HttpServlet { - private static final Logger log = getLogger(UserServlet.class); - - private AdminRestController adminController; - - @Override - public void init(ServletConfig config) throws ServletException { - super.init(config); - WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); - adminController = springContext.getBean(AdminRestController.class); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - int userId = Integer.parseInt(request.getParameter("userId")); - SecurityUtil.setAuthUserId(userId); - response.sendRedirect("meals"); - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - log.debug("getAll"); - request.setAttribute("users", adminController.getAll()); - request.getRequestDispatcher("/users.jsp").forward(request, response); - } -} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 12a4f63c..809d4c9c 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -21,6 +21,7 @@ + diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index d6c643e9..d57b656a 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -12,7 +12,4 @@ - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml new file mode 100644 index 00000000..7c71d5af --- /dev/null +++ b/src/main/resources/spring/spring-mvc.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp similarity index 66% rename from src/main/webapp/fragments/bodyHeader.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 345cc15a..888df90f 100644 --- a/src/main/webapp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -3,5 +3,5 @@
    - | | + | |
    \ No newline at end of file diff --git a/src/main/webapp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp similarity index 100% rename from src/main/webapp/fragments/footer.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/footer.jsp diff --git a/src/main/webapp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp similarity index 85% rename from src/main/webapp/fragments/headTag.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index c1532783..da40d461 100644 --- a/src/main/webapp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -6,5 +6,5 @@ <fmt:message key="app.title"/> - + \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp similarity index 100% rename from src/main/webapp/index.jsp rename to src/main/webapp/WEB-INF/jsp/index.jsp diff --git a/src/main/webapp/mealForm.jsp b/src/main/webapp/WEB-INF/jsp/mealForm.jsp similarity index 100% rename from src/main/webapp/mealForm.jsp rename to src/main/webapp/WEB-INF/jsp/mealForm.jsp diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/WEB-INF/jsp/meals.jsp similarity index 100% rename from src/main/webapp/meals.jsp rename to src/main/webapp/WEB-INF/jsp/meals.jsp diff --git a/src/main/webapp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp similarity index 100% rename from src/main/webapp/users.jsp rename to src/main/webapp/WEB-INF/jsp/users.jsp diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 0a3127ac..d15c5d96 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -12,34 +12,28 @@ - contextConfigLocation + contextC onfigLocation classpath:spring/spring-app.xml classpath:spring/spring-db.xml + org.springframework.web.context.ContextLoaderListener - userServlet - ru.javawebinar.topjava.web.UserServlet - 0 + mvc-dispatcher + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + classpath:spring/spring-mvc.xml + + 1 - userServlet - /users + mvc-dispatcher + / - - - mealServlet - ru.javawebinar.topjava.web.MealServlet - 0 - - - mealServlet - /meals - - diff --git a/src/main/webapp/css/style.css b/src/main/webapp/resources/css/style.css similarity index 100% rename from src/main/webapp/css/style.css rename to src/main/webapp/resources/css/style.css From d68cc35ff0e49759cefad0327bdcd1635610e4f3 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:04:38 +0300 Subject: [PATCH 068/144] 6_15_spring_i18n.patch --- .../messages/app.properties | 0 .../messages/app_ru.properties | 0 src/main/resources/spring/spring-mvc.xml | 18 ++++++++++++++++++ .../WEB-INF/jsp/fragments/bodyHeader.jsp | 5 ++--- .../webapp/WEB-INF/jsp/fragments/footer.jsp | 5 ++--- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 5 ++--- src/main/webapp/WEB-INF/jsp/index.jsp | 8 +++----- src/main/webapp/WEB-INF/jsp/users.jsp | 15 +++++++-------- 8 files changed, 34 insertions(+), 22 deletions(-) rename {src/main/resources => config}/messages/app.properties (100%) rename {src/main/resources => config}/messages/app_ru.properties (100%) diff --git a/src/main/resources/messages/app.properties b/config/messages/app.properties similarity index 100% rename from src/main/resources/messages/app.properties rename to config/messages/app.properties diff --git a/src/main/resources/messages/app_ru.properties b/config/messages/app_ru.properties similarity index 100% rename from src/main/resources/messages/app_ru.properties rename to config/messages/app_ru.properties diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 7c71d5af..ce12ddcc 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -15,4 +15,22 @@ + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 888df90f..5b5efe57 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,7 +1,6 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -
    - | | + | |
    \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index c59bff2e..0935c441 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp @@ -1,5 +1,4 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index da40d461..6d77694e 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -1,10 +1,9 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> - - <fmt:message key="app.title"/> + <spring:message code="app.title"/> \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index 654d7b23..84719196 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -1,8 +1,6 @@ <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> @@ -11,11 +9,11 @@
    - : - +
    diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index 391fb2b5..dad5f1b1 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -1,8 +1,7 @@ <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> @@ -10,16 +9,16 @@
    -

    +

    - - - - - + + + + + From a86a17d3e0010881f678ee94c3fb032b8a8ea357 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:59:25 +0300 Subject: [PATCH 069/144] fix --- src/main/resources/spring/spring-mvc.xml | 2 +- .../topjava/service/AbstractMealServiceTest.java | 9 --------- .../topjava/service/AbstractUserServiceTest.java | 1 - 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index ce12ddcc..e3889a6a 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -10,7 +10,7 @@ - + Date: Thu, 25 Mar 2021 09:00:13 +0300 Subject: [PATCH 070/144] 7_0_fix.patch --- .gitignore | 2 -- src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java | 5 ++--- src/main/resources/spring/spring-mvc.xml | 2 +- .../ru/javawebinar/topjava/service/AbstractServiceTest.java | 4 ++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 8168ee34..124d2c69 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,3 @@ patch *.iml log *.patch - - diff --git a/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java index d740b9f7..f3e51ad5 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java +++ b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java @@ -14,9 +14,8 @@ public class JpaUtil { public void clear2ndLevelHibernateCache() { Session s = (Session) em.getDelegate(); SessionFactory sf = s.getSessionFactory(); -// sf.evict(User.class); -// sf.getCache().evictEntity(User.class, BaseEntity.START_SEQ); -// sf.getCache().evictEntityRegion(User.class); +// sf.getCache().evictEntityData(User.class, AbstractBaseEntity.START_SEQ); +// sf.getCache().evictEntityData(User.class); sf.getCache().evictAllRegions(); } } diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index e3889a6a..8df7c9a5 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -30,7 +30,7 @@ - + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index c53a5b71..06d99de9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -23,7 +23,7 @@ @RunWith(SpringRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) @ActiveProfiles(resolver = ActiveDbProfileResolver.class) -abstract public class AbstractServiceTest { +public abstract class AbstractServiceTest { @ClassRule public static ExternalResource summary = TimingRules.SUMMARY; @@ -32,7 +32,7 @@ abstract public class AbstractServiceTest { public Stopwatch stopwatch = TimingRules.STOPWATCH; // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 - public void validateRootCause(Class rootExceptionClass, Runnable runnable) { + protected void validateRootCause(Class rootExceptionClass, Runnable runnable) { assertThrows(rootExceptionClass, () -> { try { runnable.run(); From 56f12c102b340b6273bae0bf2d57f9dcfcfba66f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:01:02 +0300 Subject: [PATCH 071/144] 7_01_HW6_fix_tests.patch --- src/test/java/ru/javawebinar/topjava/SpringMain.java | 2 +- .../topjava/service/AbstractMealServiceTest.java | 2 ++ .../topjava/service/AbstractServiceTest.java | 11 +++++++++++ .../topjava/service/AbstractUserServiceTest.java | 10 ++++++++-- .../user/InMemoryAdminRestControllerSpringTest.java | 2 +- .../web/user/InMemoryAdminRestControllerTest.java | 6 ++++-- src/test/resources/spring/inmemory.xml | 2 ++ 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index d809f025..66d4b30d 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -18,7 +18,7 @@ public static void main(String[] args) { // java 7 automatic resource management (ARM) try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { appCtx.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.REPOSITORY_IMPLEMENTATION); - appCtx.load("spring/spring-app.xml", "spring/spring-db.xml"); + appCtx.load("spring/inmemory.xml"); appCtx.refresh(); System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index febcc5b9..7f09f29a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.service; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -105,6 +106,7 @@ public void getBetweenWithNullDates() { @Test public void createWithException() throws Exception { + Assume.assumeTrue("Validation not supported (JPA only)", isJpaBased()); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, null, "Description", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID)); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 06d99de9..7886ba5e 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -5,12 +5,15 @@ import org.junit.rules.ExternalResource; import org.junit.rules.Stopwatch; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; import ru.javawebinar.topjava.ActiveDbProfileResolver; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.TimingRules; import static org.junit.Assert.assertThrows; @@ -28,9 +31,17 @@ public abstract class AbstractServiceTest { @ClassRule public static ExternalResource summary = TimingRules.SUMMARY; + @Autowired + public Environment env; + @Rule public Stopwatch stopwatch = TimingRules.STOPWATCH; + public boolean isJpaBased() { +// return Arrays.stream(env.getActiveProfiles()).noneMatch(Profiles.JDBC::equals); + return env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.JPA, Profiles.DATAJPA)); + } + // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 protected void validateRootCause(Class rootExceptionClass, Runnable runnable) { assertThrows(rootExceptionClass, () -> { diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 92800e24..65c19207 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -1,20 +1,22 @@ package ru.javawebinar.topjava.service; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Lazy; import org.springframework.dao.DataAccessException; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.JpaUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.validation.ConstraintViolationException; import java.util.Date; import java.util.List; import java.util.Set; -import ru.javawebinar.topjava.repository.JpaUtil; import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.UserTestData.*; @@ -28,12 +30,15 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { private CacheManager cacheManager; @Autowired + @Lazy protected JpaUtil jpaUtil; @Before public void setup() { cacheManager.getCache("users").clear(); - jpaUtil.clear2ndLevelHibernateCache(); + if (isJpaBased()) { + jpaUtil.clear2ndLevelHibernateCache(); + } } @Test @@ -95,6 +100,7 @@ public void getAll() { @Test public void createWithException() throws Exception { + Assume.assumeTrue("Validation not supported (JPA only)", isJpaBased()); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, " ", "mail@yandex.ru", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", " ", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", " ", Role.USER))); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java index 9102d210..8f769cf5 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -13,7 +13,7 @@ import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -@ContextConfiguration({"classpath:spring/spring-app.xml", "classpath:spring/inmemory.xml"}) +@ContextConfiguration({"classpath:spring/inmemory.xml"}) @RunWith(SpringRunner.class) public class InMemoryAdminRestControllerSpringTest { diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java index 14c60d96..1a348525 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -22,7 +22,7 @@ public class InMemoryAdminRestControllerTest { @BeforeClass public static void beforeClass() { - appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml"); + appCtx = new ClassPathXmlApplicationContext("spring/inmemory.xml"); log.info("\n{}\n", Arrays.toString(appCtx.getBeanDefinitionNames())); controller = appCtx.getBean(AdminRestController.class); repository = appCtx.getBean(InMemoryUserRepository.class); @@ -30,7 +30,9 @@ public static void beforeClass() { @AfterClass public static void afterClass() { - appCtx.close(); +// May cause during JUnit "Cache is not alive (STATUS_SHUTDOWN)" as JUnit share Spring context for speed +// http://stackoverflow.com/questions/16281802/ehcache-shutdown-causing-an-exception-while-running-test-suite +// appCtx.close(); } @Before diff --git a/src/test/resources/spring/inmemory.xml b/src/test/resources/spring/inmemory.xml index c6a2710c..0c9d0502 100644 --- a/src/test/resources/spring/inmemory.xml +++ b/src/test/resources/spring/inmemory.xml @@ -4,4 +4,6 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + + \ No newline at end of file From 2baca5daafe40dbcb6af5c4684c761ed156a5838 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:01:31 +0300 Subject: [PATCH 072/144] 7_02_HW6_meals.patch --- config/messages/app.properties | 19 ++++- config/messages/app_ru.properties | 19 ++++- .../javawebinar/topjava/web/MealServlet.java | 85 ------------------- .../topjava/web/RootController.java | 16 +++- .../web/meal/AbstractMealController.java | 72 ++++++++++++++++ .../topjava/web/meal/JspMealController.java | 70 +++++++++++++++ .../topjava/web/meal/MealRestController.java | 71 +--------------- src/main/webapp/WEB-INF/jsp/mealForm.jsp | 25 +++--- src/main/webapp/WEB-INF/jsp/meals.jsp | 46 +++++----- 9 files changed, 228 insertions(+), 195 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/web/MealServlet.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java diff --git a/config/messages/app.properties b/config/messages/app.properties index ccd4c66b..6b4d2d15 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -2,11 +2,28 @@ app.title=Calories management app.home=Home app.footer=Internship Spring 5/JPA Enterprise (Topjava) application app.login=Login as + user.title=Users user.name=Name user.email=Email user.roles=Roles user.active=Active user.registered=Registered + meal.title=Meals -common.select=Select \ No newline at end of file +meal.edit=Edit meal +meal.add=Add meal +meal.filter=Filter +meal.startDate=From date (inclusive) +meal.endDate=To date (inclusive) +meal.startTime=From time (inclusive) +meal.endTime=To time (exclusive) +meal.description=Description +meal.dateTime=Date/Time +meal.calories=Calories + +common.select=Select +common.delete=Delete +common.update=Update +common.save=Save +common.cancel=Cancel \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 0db1b2c9..4150d65d 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -2,11 +2,28 @@ app.title= app.home= app.footer= Spring 5/JPA Enterprise (Topjava) app.login= + user.title= user.name= user.email= user.roles= user.active= user.registered= + meal.title= -common.select= \ No newline at end of file +meal.edit= +meal.add= +meal.filter= +meal.startDate= () +meal.endDate= () +meal.startTime= () +meal.endTime= () +meal.description= +meal.dateTime=/ +meal.calories= + +common.select= +common.delete= +common.update= +common.save= +common.cancel= \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java deleted file mode 100644 index fb667d35..00000000 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.springframework.util.StringUtils; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.web.meal.MealRestController; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.temporal.ChronoUnit; -import java.util.Objects; - -import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; -import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; - -public class MealServlet extends HttpServlet { - - private MealRestController mealController; - - @Override - public void init() { - WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); - mealController = springContext.getBean(MealRestController.class); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - request.setCharacterEncoding("UTF-8"); - Meal meal = new Meal( - LocalDateTime.parse(request.getParameter("dateTime")), - request.getParameter("description"), - Integer.parseInt(request.getParameter("calories"))); - - if (StringUtils.hasLength(request.getParameter("id"))) { - mealController.update(meal, getId(request)); - } else { - mealController.create(meal); - } - response.sendRedirect("meals"); - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String action = request.getParameter("action"); - - switch (action == null ? "all" : action) { - case "delete" -> { - int id = getId(request); - mealController.delete(id); - response.sendRedirect("meals"); - } - case "create", "update" -> { - final Meal meal = "create".equals(action) ? - new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - mealController.get(getId(request)); - request.setAttribute("meal", meal); - request.getRequestDispatcher("/mealForm.jsp").forward(request, response); - } - case "filter" -> { - LocalDate startDate = parseLocalDate(request.getParameter("startDate")); - LocalDate endDate = parseLocalDate(request.getParameter("endDate")); - LocalTime startTime = parseLocalTime(request.getParameter("startTime")); - LocalTime endTime = parseLocalTime(request.getParameter("endTime")); - request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); - request.getRequestDispatcher("/meals.jsp").forward(request, response); - } - default -> { - request.setAttribute("meals", mealController.getAll()); - request.getRequestDispatcher("/meals.jsp").forward(request, response); - } - } - } - - private int getId(HttpServletRequest request) { - String paramId = Objects.requireNonNull(request.getParameter("id")); - return Integer.parseInt(paramId); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 32726bc8..8081c8ba 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -5,14 +5,19 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import ru.javawebinar.topjava.service.MealService; import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.util.MealsUtil; import javax.servlet.http.HttpServletRequest; @Controller public class RootController { @Autowired - private UserService service; + private UserService userService; + + @Autowired + private MealService mealService; @GetMapping("/") public String root() { @@ -21,7 +26,7 @@ public String root() { @GetMapping("/users") public String getUsers(Model model) { - model.addAttribute("users", service.getAll()); + model.addAttribute("users", userService.getAll()); return "users"; } @@ -31,4 +36,11 @@ public String setUser(HttpServletRequest request) { SecurityUtil.setAuthUserId(userId); return "redirect:meals"; } + + @GetMapping("/meals") + public String getMeals(Model model) { + model.addAttribute("meals", + MealsUtil.getTos(mealService.getAll(SecurityUtil.authUserId()), SecurityUtil.authUserCaloriesPerDay())); + return "meals"; + } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java new file mode 100644 index 00000000..ec601c18 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java @@ -0,0 +1,72 @@ +package ru.javawebinar.topjava.web.meal; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.Nullable; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.to.MealTo; +import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.SecurityUtil; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; + +public abstract class AbstractMealController { + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Autowired + private MealService service; + + public Meal get(int id) { + int userId = SecurityUtil.authUserId(); + log.info("get meal {} for user {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = SecurityUtil.authUserId(); + log.info("delete meal {} for user {}", id, userId); + service.delete(id, userId); + } + + public List getAll() { + int userId = SecurityUtil.authUserId(); + log.info("getAll for user {}", userId); + return MealsUtil.getTos(service.getAll(userId), SecurityUtil.authUserCaloriesPerDay()); + } + + public Meal create(Meal meal) { + int userId = SecurityUtil.authUserId(); + log.info("create {} for user {}", meal, userId); + checkNew(meal); + return service.create(meal, userId); + } + + public void update(Meal meal, int id) { + int userId = SecurityUtil.authUserId(); + log.info("update {} for user {}", meal, userId); + assureIdConsistent(meal, id); + service.update(meal, userId); + } + + /** + *
      Filter separately + *
    1. by date
    2. + *
    3. by time for every date
    4. + *
    + */ + public List getBetween(@Nullable LocalDate startDate, @Nullable LocalTime startTime, + @Nullable LocalDate endDate, @Nullable LocalTime endTime) { + int userId = SecurityUtil.authUserId(); + log.info("getBetween dates({} - {}) time({} - {}) for user {}", startDate, endDate, startTime, endTime, userId); + + List mealsDateFiltered = service.getBetweenInclusive(startDate, endDate, userId); + return MealsUtil.getFilteredTos(mealsDateFiltered, SecurityUtil.authUserCaloriesPerDay(), startTime, endTime); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java new file mode 100644 index 00000000..7513dabe --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java @@ -0,0 +1,70 @@ +package ru.javawebinar.topjava.web.meal; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.javawebinar.topjava.model.Meal; + +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; + +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; + +@Controller +@RequestMapping("/meals") +public class JspMealController extends AbstractMealController { + + @GetMapping("/delete") + public String delete(HttpServletRequest request) { + super.delete(getId(request)); + return "redirect:meals"; + } + + @GetMapping("/update") + public String update(HttpServletRequest request, Model model) { + model.addAttribute("meal", super.get(getId(request))); + return "mealForm"; + } + + @GetMapping("/create") + public String create(Model model) { + model.addAttribute("meal", new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS), "", 1000)); + return "mealForm"; + } + + @PostMapping + public String updateOrCreate(HttpServletRequest request) { + Meal meal = new Meal(LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.parseInt(request.getParameter("calories"))); + + if (request.getParameter("id").isEmpty()) { + super.create(meal); + } else { + super.update(meal, getId(request)); + } + return "redirect:meals"; + } + + @GetMapping("/filter") + public String getBetween(HttpServletRequest request, Model model) { + LocalDate startDate = parseLocalDate(request.getParameter("startDate")); + LocalDate endDate = parseLocalDate(request.getParameter("endDate")); + LocalTime startTime = parseLocalTime(request.getParameter("startTime")); + LocalTime endTime = parseLocalTime(request.getParameter("endTime")); + model.addAttribute("meals", super.getBetween(startDate, startTime, endDate, endTime)); + return "meals"; + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.parseInt(paramId); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index bbfe35e3..c3daf685 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,76 +1,7 @@ package ru.javawebinar.topjava.web.meal; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.service.MealService; -import ru.javawebinar.topjava.to.MealTo; -import ru.javawebinar.topjava.util.MealsUtil; -import ru.javawebinar.topjava.web.SecurityUtil; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; -import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; @Controller -public class MealRestController { - private static final Logger log = LoggerFactory.getLogger(MealRestController.class); - - private final MealService service; - - public MealRestController(MealService service) { - this.service = service; - } - - public Meal get(int id) { - int userId = SecurityUtil.authUserId(); - log.info("get meal {} for user {}", id, userId); - return service.get(id, userId); - } - - public void delete(int id) { - int userId = SecurityUtil.authUserId(); - log.info("delete meal {} for user {}", id, userId); - service.delete(id, userId); - } - - public List getAll() { - int userId = SecurityUtil.authUserId(); - log.info("getAll for user {}", userId); - return MealsUtil.getTos(service.getAll(userId), SecurityUtil.authUserCaloriesPerDay()); - } - - public Meal create(Meal meal) { - int userId = SecurityUtil.authUserId(); - checkNew(meal); - log.info("create {} for user {}", meal, userId); - return service.create(meal, userId); - } - - public void update(Meal meal, int id) { - int userId = SecurityUtil.authUserId(); - assureIdConsistent(meal, id); - log.info("update {} for user {}", meal, userId); - service.update(meal, userId); - } - - /** - *
      Filter separately - *
    1. by date
    2. - *
    3. by time for every date
    4. - *
    - */ - public List getBetween(@Nullable LocalDate startDate, @Nullable LocalTime startTime, - @Nullable LocalDate endDate, @Nullable LocalTime endTime) { - int userId = SecurityUtil.authUserId(); - log.info("getBetween dates({} - {}) time({} - {}) for user {}", startDate, endDate, startTime, endTime, userId); - - List mealsDateFiltered = service.getBetweenInclusive(startDate, endDate, userId); - return MealsUtil.getFilteredTos(mealsDateFiltered, SecurityUtil.authUserCaloriesPerDay(), startTime, endTime); - } +public class MealRestController extends AbstractMealController { } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/mealForm.jsp b/src/main/webapp/WEB-INF/jsp/mealForm.jsp index 98a6f487..af6d7880 100644 --- a/src/main/webapp/WEB-INF/jsp/mealForm.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealForm.jsp @@ -1,34 +1,35 @@ <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> - - Meal - - + + +
    -

    Home

    -
    -

    ${param.action == 'create' ? 'Create meal' : 'Edit meal'}

    +<%-- `meal.new` cause javax.el.ELException - bug tomcat --%> +

    +
    -
    DateTime:
    +
    :
    -
    Description:
    +
    :
    -
    Calories:
    +
    :
    - - + +
    + diff --git a/src/main/webapp/WEB-INF/jsp/meals.jsp b/src/main/webapp/WEB-INF/jsp/meals.jsp index 702d9ef3..c96d7446 100644 --- a/src/main/webapp/WEB-INF/jsp/meals.jsp +++ b/src/main/webapp/WEB-INF/jsp/meals.jsp @@ -1,52 +1,49 @@ <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> - - Meals - - + + +
    -

    Home

    -
    -

    Meals

    -
    - +

    + +
    -
    From Date (inclusive):
    +
    :
    -
    To Date (inclusive):
    +
    :
    -
    From Time (inclusive):
    +
    :
    -
    To Time (exclusive):
    +
    :
    - + -
    - Add Meal -

    +
    + +
    - - - + + + - + - - + +
    DateDescriptionCalories
    <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> @@ -56,11 +53,12 @@ ${meal.description} ${meal.calories}UpdateDelete
    + \ No newline at end of file From 3c30cb624992bfb70cfce3205e392915643a666f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:01:57 +0300 Subject: [PATCH 073/144] 7_03_HW6_fix_relative_url_utf8.patch --- .../topjava/web/meal/JspMealController.java | 4 ++-- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 1 + src/main/webapp/WEB-INF/web.xml | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java index 7513dabe..abdfa0cf 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/JspMealController.java @@ -24,7 +24,7 @@ public class JspMealController extends AbstractMealController { @GetMapping("/delete") public String delete(HttpServletRequest request) { super.delete(getId(request)); - return "redirect:meals"; + return "redirect:/meals"; } @GetMapping("/update") @@ -50,7 +50,7 @@ public String updateOrCreate(HttpServletRequest request) { } else { super.update(meal, getId(request)); } - return "redirect:meals"; + return "redirect:/meals"; } @GetMapping("/filter") diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 6d77694e..0c77f108 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -5,5 +5,6 @@ <spring:message code="app.title"/> + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index d15c5d96..802e276e 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -36,4 +36,21 @@ mvc-dispatcher / + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + encodingFilter + /* + From 118517099236cdec0c8ea718218ff78e62fa0613 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:38:28 +0300 Subject: [PATCH 074/144] 7_04_HW6_optional_add_role.patch --- src/main/java/ru/javawebinar/topjava/model/User.java | 2 +- src/main/resources/db/populateDB.sql | 3 ++- src/test/java/ru/javawebinar/topjava/UserTestData.java | 4 ++-- .../topjava/service/AbstractUserServiceTest.java | 4 ++-- .../topjava/service/datajpa/DataJpaUserServiceTest.java | 8 ++++---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 6bd6d743..74b73788 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -24,7 +24,7 @@ @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @NamedQueries({ @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), - @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), + @NamedQuery(name = User.BY_EMAIL, query = "SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u ORDER BY u.name, u.email"), }) @Entity diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index a6c79fb9..1ff35fc5 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -9,7 +9,8 @@ VALUES ('User', 'user@yandex.ru', 'password'), INSERT INTO user_roles (role, user_id) VALUES ('USER', 100000), - ('ADMIN', 100001); + ('ADMIN', 100001), + ('USER', 100001); INSERT INTO meals (date_time, description, calories, user_id) VALUES ('2020-01-30 10:00:00', 'Завтрак', 500, 100000), diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 979401e0..c41c535a 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -9,14 +9,14 @@ import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { - public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "roles", "meals"); + public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "meals"); public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; public static final int NOT_FOUND = 10; public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", Role.USER); - public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN); + public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN, Role.USER); public static User getNew() { return new User(null, "New", "new@gmail.com", "newPass", 1555, false, new Date(), Collections.singleton(Role.USER)); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 65c19207..6d2a4542 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -70,8 +70,8 @@ public void deletedNotFound() { @Test public void get() { - User user = service.get(USER_ID); - USER_MATCHER.assertMatch(user, UserTestData.user); + User user = service.get(ADMIN_ID); + USER_MATCHER.assertMatch(user, UserTestData.admin); } @Test diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java index 32bcf5ef..06d30e4b 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -10,16 +10,16 @@ import ru.javawebinar.topjava.util.exception.NotFoundException; import static ru.javawebinar.topjava.Profiles.DATAJPA; -import static ru.javawebinar.topjava.UserTestData.USER_ID; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_MATCHER; @ActiveProfiles(DATAJPA) public class DataJpaUserServiceTest extends AbstractUserServiceTest { @Test public void getWithMeals() { - User user = service.getWithMeals(USER_ID); - USER_MATCHER.assertMatch(user, UserTestData.user); - MealTestData.MEAL_MATCHER.assertMatch(user.getMeals(), MealTestData.meals); + User admin = service.getWithMeals(ADMIN_ID); + USER_MATCHER.assertMatch(admin, UserTestData.admin); + MealTestData.MEAL_MATCHER.assertMatch(admin.getMeals(), MealTestData.adminMeal2, MealTestData.adminMeal1); } @Test From a86b789e5e7153941f1d65ec637f199428e4baf5 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:38:51 +0300 Subject: [PATCH 075/144] 7_05_fix_hint_graph.patch --- .../topjava/repository/datajpa/CrudUserRepository.java | 3 ++- .../javawebinar/topjava/repository/jpa/JpaUserRepository.java | 2 ++ src/main/resources/db/postgres.properties | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java index 806884a9..692e50c8 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -17,7 +17,8 @@ public interface CrudUserRepository extends JpaRepository { User getByEmail(String email); - @EntityGraph(attributePaths = {"meals", "roles"}) + // https://stackoverflow.com/a/46013654/548473 + @EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD) @Query("SELECT u FROM User u WHERE u.id=?1") User getWithMeals(int id); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java index 9ade1335..26c16f7d 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.jpa; +import org.hibernate.jpa.QueryHints; import org.springframework.dao.support.DataAccessUtils; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -61,6 +62,7 @@ public boolean delete(int id) { public User getByEmail(String email) { List users = em.createNamedQuery(User.BY_EMAIL, User.class) .setParameter(1, email) + .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false) .getResultList(); return DataAccessUtils.singleResult(users); } diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 5ea981f0..4264222d 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -11,4 +11,4 @@ database.init=true jdbc.initLocation=classpath:db/initDB.sql jpa.showSql=true hibernate.format_sql=true -hibernate.use_sql_comments=true \ No newline at end of file +hibernate.use_sql_comments=false \ No newline at end of file From e4a6441cb1be5477c0a0dcd08bb31e0dd7b58489 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:39:21 +0300 Subject: [PATCH 076/144] 7_06_HW6_optional_jdbc.patch --- .../repository/jdbc/JdbcMealRepository.java | 4 ++ .../repository/jdbc/JdbcUserRepository.java | 63 ++++++++++++++++--- src/main/resources/spring/spring-db.xml | 9 ++- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index 1f3fe5ff..1895fcac 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -8,6 +8,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -15,6 +16,7 @@ import java.util.List; @Repository +@Transactional(readOnly = true) public class JdbcMealRepository implements MealRepository { private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); @@ -35,6 +37,7 @@ public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate } @Override + @Transactional public Meal save(Meal meal, int userId) { MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", meal.getId()) @@ -58,6 +61,7 @@ public Meal save(Meal meal, int userId) { } @Override + @Transactional public boolean delete(int id, int userId) { return jdbcTemplate.update("DELETE FROM meals WHERE id=? AND user_id=?", id, userId) != 0; } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java index 0d4baa50..2a7bc7b3 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -8,12 +8,16 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; -import java.util.List; +import java.util.*; @Repository +@Transactional(readOnly = true) public class JdbcUserRepository implements UserRepository { private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); @@ -35,22 +39,32 @@ public JdbcUserRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate } @Override + @Transactional public User save(User user) { BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user); if (user.isNew()) { Number newKey = insertUser.executeAndReturnKey(parameterSource); user.setId(newKey.intValue()); - } else if (namedParameterJdbcTemplate.update(""" - UPDATE users SET name=:name, email=:email, password=:password, - registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id - """, parameterSource) == 0) { - return null; + insertRoles(user); + } else { + if (namedParameterJdbcTemplate.update(""" + UPDATE users SET name=:name, email=:email, password=:password, + registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id + """, parameterSource) == 0) { + return null; + } + // Simplest implementation. + // More complicated : get user roles from DB and compare them with user.roles (assume that roles are changed rarely). + // If roles are changed, calculate difference in java and delete/insert them. + deleteRoles(user); + insertRoles(user); } return user; } @Override + @Transactional public boolean delete(int id) { return jdbcTemplate.update("DELETE FROM users WHERE id=?", id) != 0; } @@ -58,18 +72,49 @@ public boolean delete(int id) { @Override public User get(int id) { List users = jdbcTemplate.query("SELECT * FROM users WHERE id=?", ROW_MAPPER, id); - return DataAccessUtils.singleResult(users); + return setRoles(DataAccessUtils.singleResult(users)); } @Override public User getByEmail(String email) { // return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); List users = jdbcTemplate.query("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); - return DataAccessUtils.singleResult(users); + return setRoles(DataAccessUtils.singleResult(users)); } @Override public List getAll() { - return jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + List users = jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + + Map> map = new HashMap<>(); + jdbcTemplate.query("SELECT * FROM user_roles", rs -> { + map.computeIfAbsent(rs.getInt("user_id"), userId -> EnumSet.noneOf(Role.class)) + .add(Role.valueOf(rs.getString("role"))); + }); + users.forEach(u -> u.setRoles(map.get(u.getId()))); + return users; + } + + private void insertRoles(User u) { + Set roles = u.getRoles(); + if (!CollectionUtils.isEmpty(roles)) { + jdbcTemplate.batchUpdate("INSERT INTO user_roles (user_id, role) VALUES (?, ?)", roles, roles.size(), + (ps, role) -> { + ps.setInt(1, u.id()); + ps.setString(2, role.name()); + }); + } + } + + private void deleteRoles(User u) { + jdbcTemplate.update("DELETE FROM user_roles WHERE user_id=?", u.getId()); + } + + private User setRoles(User u) { + if (u != null) { + List roles = jdbcTemplate.queryForList("SELECT role FROM user_roles WHERE user_id=?", Role.class, u.getId()); + u.setRoles(roles); + } + return u; } } diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 2b62f074..bf849688 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -20,6 +20,8 @@ + + @@ -66,6 +68,11 @@ + + + + @@ -101,8 +108,6 @@
    - - From 9a53e0d44fcfc5c6788092afa863845eff03f9d7 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:40:03 +0300 Subject: [PATCH 077/144] 7_07_HW6_optional_jdbc_validation.patch --- .../ru/javawebinar/topjava/model/Meal.java | 2 +- .../repository/jdbc/JdbcMealRepository.java | 3 +++ .../repository/jdbc/JdbcUserRepository.java | 3 +++ .../topjava/util/ValidationUtil.java | 21 +++++++++++++++++++ .../service/AbstractMealServiceTest.java | 2 -- .../service/AbstractUserServiceTest.java | 2 -- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index c00282b1..9033f673 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -45,7 +45,7 @@ public class Meal extends AbstractBaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) @OnDelete(action = OnDeleteAction.CASCADE) - @NotNull +// @NotNull private User user; public Meal() { diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index 1895fcac..82c80dd7 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -11,6 +11,7 @@ import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.ValidationUtil; import java.time.LocalDateTime; import java.util.List; @@ -39,6 +40,8 @@ public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate @Override @Transactional public Meal save(Meal meal, int userId) { + ValidationUtil.validate(meal); + MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", meal.getId()) .addValue("description", meal.getDescription()) diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java index 2a7bc7b3..dda61a5f 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -13,6 +13,7 @@ import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.ValidationUtil; import java.util.*; @@ -41,6 +42,8 @@ public JdbcUserRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate @Override @Transactional public User save(User user) { + ValidationUtil.validate(user); + BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user); if (user.isNew()) { diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 35d83de8..fa79c4b9 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -6,10 +6,31 @@ import ru.javawebinar.topjava.model.AbstractBaseEntity; import ru.javawebinar.topjava.util.exception.NotFoundException; +import javax.validation.*; +import java.util.Set; + public class ValidationUtil { + + private static final Validator validator; + + static { + // From Javadoc: implementations are thread-safe and instances are typically cached and reused. + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + // From Javadoc: implementations of this interface must be thread-safe + validator = factory.getValidator(); + } + private ValidationUtil() { } + public static void validate(T bean) { + // https://alexkosarev.name/2018/07/30/bean-validation-api/ + Set> violations = validator.validate(bean); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + public static T checkNotFoundWithId(T object, int id) { checkNotFoundWithId(object != null, id); return object; diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index 7f09f29a..febcc5b9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -1,7 +1,6 @@ package ru.javawebinar.topjava.service; import org.junit.Assert; -import org.junit.Assume; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -106,7 +105,6 @@ public void getBetweenWithNullDates() { @Test public void createWithException() throws Exception { - Assume.assumeTrue("Validation not supported (JPA only)", isJpaBased()); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, null, "Description", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID)); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 6d2a4542..fe8b8818 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.service; -import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -100,7 +99,6 @@ public void getAll() { @Test public void createWithException() throws Exception { - Assume.assumeTrue("Validation not supported (JPA only)", isJpaBased()); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, " ", "mail@yandex.ru", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", " ", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", " ", Role.USER))); From 190fb856429139964edc6a6ab360c55f7344a940 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 09:40:43 +0300 Subject: [PATCH 078/144] 7_08_HW06_optional2_disable_tests_cache.patch --- .../topjava/repository/JpaUtil.java | 21 --------------- src/main/resources/spring/spring-db.xml | 2 -- .../topjava/service/AbstractServiceTest.java | 11 -------- .../service/AbstractUserServiceTest.java | 19 ------------- src/test/resources/spring/spring-cache.xml | 27 +++++++++++++++++++ 5 files changed, 27 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java create mode 100644 src/test/resources/spring/spring-cache.xml diff --git a/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java deleted file mode 100644 index f3e51ad5..00000000 --- a/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.javawebinar.topjava.repository; - -import org.hibernate.Session; -import org.hibernate.SessionFactory; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -public class JpaUtil { - - @PersistenceContext - private EntityManager em; - - public void clear2ndLevelHibernateCache() { - Session s = (Session) em.getDelegate(); - SessionFactory sf = s.getSessionFactory(); -// sf.getCache().evictEntityData(User.class, AbstractBaseEntity.START_SEQ); -// sf.getCache().evictEntityData(User.class); - sf.getCache().evictAllRegions(); - } -} diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index bf849688..48afdb11 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -111,8 +111,6 @@ - - diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 7886ba5e..06d99de9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -5,15 +5,12 @@ import org.junit.rules.ExternalResource; import org.junit.rules.Stopwatch; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringRunner; import ru.javawebinar.topjava.ActiveDbProfileResolver; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.TimingRules; import static org.junit.Assert.assertThrows; @@ -31,17 +28,9 @@ public abstract class AbstractServiceTest { @ClassRule public static ExternalResource summary = TimingRules.SUMMARY; - @Autowired - public Environment env; - @Rule public Stopwatch stopwatch = TimingRules.STOPWATCH; - public boolean isJpaBased() { -// return Arrays.stream(env.getActiveProfiles()).noneMatch(Profiles.JDBC::equals); - return env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.JPA, Profiles.DATAJPA)); - } - // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 protected void validateRootCause(Class rootExceptionClass, Runnable runnable) { assertThrows(rootExceptionClass, () -> { diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index fe8b8818..0abb76fc 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -1,15 +1,11 @@ package ru.javawebinar.topjava.service; -import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.CacheManager; -import org.springframework.context.annotation.Lazy; import org.springframework.dao.DataAccessException; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.JpaUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.validation.ConstraintViolationException; @@ -25,21 +21,6 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired protected UserService service; - @Autowired - private CacheManager cacheManager; - - @Autowired - @Lazy - protected JpaUtil jpaUtil; - - @Before - public void setup() { - cacheManager.getCache("users").clear(); - if (isJpaBased()) { - jpaUtil.clear2ndLevelHibernateCache(); - } - } - @Test public void create() { User created = service.create(getNew()); diff --git a/src/test/resources/spring/spring-cache.xml b/src/test/resources/spring/spring-cache.xml new file mode 100644 index 00000000..ea51df90 --- /dev/null +++ b/src/test/resources/spring/spring-cache.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + false + + + + + + \ No newline at end of file From d257b37e510ee5bd1b3e85b10d1c99ab8d2930a8 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 10:33:36 +0300 Subject: [PATCH 079/144] 7_09_controller_test.patch --- pom.xml | 14 +++++ .../topjava/AllActiveProfileResolver.java | 13 +++++ .../topjava/web/AbstractControllerTest.java | 54 +++++++++++++++++++ .../topjava/web/RootControllerTest.java | 29 ++++++++++ 4 files changed, 110 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/AllActiveProfileResolver.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java diff --git a/pom.xml b/pom.xml index 6d272b4f..e769bfd7 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,7 @@ 4.13.2 3.19.0 + 1.3 5.4.28.Final @@ -214,7 +215,20 @@ junit ${junit.version} test + + + hamcrest-core + org.hamcrest + + + + org.hamcrest + hamcrest-all + ${hamcrest.version} + test + + org.springframework spring-test diff --git a/src/test/java/ru/javawebinar/topjava/AllActiveProfileResolver.java b/src/test/java/ru/javawebinar/topjava/AllActiveProfileResolver.java new file mode 100644 index 00000000..ae34d09f --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/AllActiveProfileResolver.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava; + +import org.springframework.lang.NonNull; +import org.springframework.test.context.ActiveProfilesResolver; + +//http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver +public class AllActiveProfileResolver implements ActiveProfilesResolver { + + @Override + public @NonNull String[] resolve(@NonNull Class aClass) { + return new String[]{Profiles.REPOSITORY_IMPLEMENTATION, Profiles.getActiveDbProfile()}; + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java new file mode 100644 index 00000000..3c4b902e --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -0,0 +1,54 @@ +package ru.javawebinar.topjava.web; + +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; +import ru.javawebinar.topjava.AllActiveProfileResolver; + +import javax.annotation.PostConstruct; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-mvc.xml", + "classpath:spring/spring-db.xml" +}) +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ActiveProfiles(resolver = AllActiveProfileResolver.class) +public abstract class AbstractControllerTest { + + private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); + + static { + CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); + CHARACTER_ENCODING_FILTER.setForceEncoding(true); + } + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + @PostConstruct + private void postConstruct() { + mockMvc = MockMvcBuilders + .webAppContextSetup(webApplicationContext) + .addFilter(CHARACTER_ENCODING_FILTER) + .build(); + } + + protected ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception { + return mockMvc.perform(builder); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java new file mode 100644 index 00000000..a9e94ef6 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -0,0 +1,29 @@ +package ru.javawebinar.topjava.web; + +import org.junit.Test; +import ru.javawebinar.topjava.UserTestData; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; + +public class RootControllerTest extends AbstractControllerTest { + + @Test + public void getUsers() throws Exception { + perform(get("/users")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(view().name("users")) + .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")) + .andExpect(model().attribute("users", hasSize(2))) + .andExpect(model().attribute("users", hasItem( + allOf( + hasProperty("id", is(START_SEQ)), + hasProperty("name", is(UserTestData.user.getName())) + ) + ))); + } +} \ No newline at end of file From d1f552b561252a482f66d6133fa427bcfae5453d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 11:38:17 +0300 Subject: [PATCH 080/144] 7_10_JUnit5.patch --- pom.xml | 15 +++---- .../javawebinar/topjava/TimingExtension.java | 36 ++++++++++++++++ .../ru/javawebinar/topjava/TimingRules.java | 42 ------------------- .../service/AbstractMealServiceTest.java | 36 ++++++++-------- .../topjava/service/AbstractServiceTest.java | 26 ++++-------- .../service/AbstractUserServiceTest.java | 24 +++++------ .../datajpa/DataJpaMealServiceTest.java | 12 +++--- .../datajpa/DataJpaUserServiceTest.java | 12 +++--- .../service/jdbc/JdbcMealServiceTest.java | 2 +- .../service/jdbc/JdbcUserServiceTest.java | 2 +- .../service/jpa/JpaMealServiceTest.java | 2 +- .../service/jpa/JpaUserServiceTest.java | 2 +- .../topjava/web/AbstractControllerTest.java | 11 ++--- .../topjava/web/RootControllerTest.java | 6 +-- ...InMemoryAdminRestControllerSpringTest.java | 27 ++++++------ .../user/InMemoryAdminRestControllerTest.java | 24 +++++------ 16 files changed, 126 insertions(+), 153 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/TimingExtension.java delete mode 100644 src/test/java/ru/javawebinar/topjava/TimingRules.java diff --git a/pom.xml b/pom.xml index e769bfd7..37d4067c 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 42.2.18 - 4.13.2 + 5.7.1 3.19.0 1.3 @@ -54,6 +54,7 @@ + org.apache.maven.plugins maven-surefire-plugin 2.22.2 @@ -211,16 +212,10 @@ - junit - junit - ${junit.version} + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} test - - - hamcrest-core - org.hamcrest - - org.hamcrest diff --git a/src/test/java/ru/javawebinar/topjava/TimingExtension.java b/src/test/java/ru/javawebinar/topjava/TimingExtension.java new file mode 100644 index 00000000..cee6ae92 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/TimingExtension.java @@ -0,0 +1,36 @@ +package ru.javawebinar.topjava; + +import org.junit.jupiter.api.extension.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StopWatch; + +public class TimingExtension implements + BeforeTestExecutionCallback, AfterTestExecutionCallback, BeforeAllCallback, AfterAllCallback { + + private static final Logger log = LoggerFactory.getLogger("result"); + + private StopWatch stopWatch; + + @Override + public void beforeAll(ExtensionContext extensionContext) { + stopWatch = new StopWatch("Execution time of " + extensionContext.getRequiredTestClass().getSimpleName()); + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) { + String testName = extensionContext.getDisplayName(); + log.info("\nStart " + testName); + stopWatch.start(testName); + } + + @Override + public void afterTestExecution(ExtensionContext extensionContext) { + stopWatch.stop(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) { + log.info('\n' + stopWatch.prettyPrint() + '\n'); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/TimingRules.java b/src/test/java/ru/javawebinar/topjava/TimingRules.java deleted file mode 100644 index fdd3d877..00000000 --- a/src/test/java/ru/javawebinar/topjava/TimingRules.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.javawebinar.topjava; - -import org.junit.rules.ExternalResource; -import org.junit.rules.Stopwatch; -import org.junit.runner.Description; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.TimeUnit; - -public class TimingRules { - private static final Logger log = LoggerFactory.getLogger("result"); - - private static final StringBuilder results = new StringBuilder(); - - // http://stackoverflow.com/questions/14892125/what-is-the-best-practice-to-determine-the-execution-time-of-the-bussiness-relev - public static final Stopwatch STOPWATCH = new Stopwatch() { - @Override - protected void finished(long nanos, Description description) { - String result = String.format("%-95s %7d", description.getDisplayName(), TimeUnit.NANOSECONDS.toMillis(nanos)); - results.append(result).append('\n'); - log.info(result + " ms\n"); - } - }; - - // https://dzone.com/articles/applying-new-jdk-11-string-methods - private static final String DELIM = "-".repeat(103); - - public static final ExternalResource SUMMARY = new ExternalResource() { - @Override - protected void before() throws Throwable { - results.setLength(0); - } - - @Override - protected void after() { - log.info("\n" + DELIM + - "\nTest Duration, ms" + - "\n" + DELIM + "\n" + results + DELIM + "\n"); - } - }; -} diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index febcc5b9..ad7fb8a9 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.service; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import ru.javawebinar.topjava.model.Meal; @@ -12,7 +12,7 @@ import java.time.Month; import static java.time.LocalDateTime.of; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_ID; @@ -23,23 +23,23 @@ public abstract class AbstractMealServiceTest extends AbstractServiceTest { protected MealService service; // @Test - public void delete() { + void delete() { service.delete(MEAL1_ID, USER_ID); assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, USER_ID)); } @Test - public void deleteNotFound() { + void deleteNotFound() { assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND, USER_ID)); } // @Test - public void deleteNotOwn() { + void deleteNotOwn() { assertThrows(NotFoundException.class, () -> service.delete(MEAL1_ID, ADMIN_ID)); } // @Test - public void create() { + void create() { Meal created = service.create(getNew(), USER_ID); int newId = created.id(); Meal newMeal = getNew(); @@ -49,49 +49,49 @@ public void create() { } // @Test - public void duplicateDateTimeCreate() { + void duplicateDateTimeCreate() { assertThrows(DataAccessException.class, () -> service.create(new Meal(null, meal1.getDateTime(), "duplicate", 100), USER_ID)); } // @Test - public void get() { + void get() { Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); MEAL_MATCHER.assertMatch(actual, adminMeal1); } @Test - public void getNotFound() { + void getNotFound() { assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND, USER_ID)); } @Test - public void getNotOwn() { + void getNotOwn() { assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, ADMIN_ID)); } // @Test - public void update() { + void update() { Meal updated = getUpdated(); service.update(updated, USER_ID); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); } // @Test - public void updateNotOwn() { + void updateNotOwn() { NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(getUpdated(), ADMIN_ID)); - Assert.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); + Assertions.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } // @Test - public void getAll() { + void getAll() { MEAL_MATCHER.assertMatch(service.getAll(USER_ID), meals); } // @Test - public void getBetweenInclusive() { + void getBetweenInclusive() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive( LocalDate.of(2020, Month.JANUARY, 30), LocalDate.of(2020, Month.JANUARY, 30), USER_ID), @@ -99,12 +99,12 @@ public void getBetweenInclusive() { } // @Test - public void getBetweenWithNullDates() { + void getBetweenWithNullDates() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); } @Test - public void createWithException() throws Exception { + void createWithException() throws Exception { validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, null, "Description", 300), USER_ID)); validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID)); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 06d99de9..06f72ef8 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -1,36 +1,26 @@ package ru.javawebinar.topjava.service; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.ExternalResource; -import org.junit.rules.Stopwatch; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import ru.javawebinar.topjava.ActiveDbProfileResolver; -import ru.javawebinar.topjava.TimingRules; +import ru.javawebinar.topjava.TimingExtension; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import static ru.javawebinar.topjava.util.ValidationUtil.getRootCause; -@ContextConfiguration({ +@SpringJUnitConfig(locations = { "classpath:spring/spring-app.xml", "classpath:spring/spring-db.xml" }) -@RunWith(SpringRunner.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +//@ExtendWith(SpringExtension.class) @ActiveProfiles(resolver = ActiveDbProfileResolver.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ExtendWith(TimingExtension.class) public abstract class AbstractServiceTest { - @ClassRule - public static ExternalResource summary = TimingRules.SUMMARY; - - @Rule - public Stopwatch stopwatch = TimingRules.STOPWATCH; - // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 protected void validateRootCause(Class rootExceptionClass, Runnable runnable) { assertThrows(rootExceptionClass, () -> { diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 0abb76fc..d2232c8a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.service; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import ru.javawebinar.topjava.UserTestData; @@ -13,7 +13,7 @@ import java.util.List; import java.util.Set; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import static ru.javawebinar.topjava.UserTestData.*; public abstract class AbstractUserServiceTest extends AbstractServiceTest { @@ -22,7 +22,7 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { protected UserService service; @Test - public void create() { + void create() { User created = service.create(getNew()); int newId = created.id(); User newUser = getNew(); @@ -32,54 +32,54 @@ public void create() { } @Test - public void duplicateMailCreate() { + void duplicateMailCreate() { assertThrows(DataAccessException.class, () -> service.create(new User(null, "Duplicate", "user@yandex.ru", "newPass", Role.USER))); } @Test - public void delete() { + void delete() { service.delete(USER_ID); assertThrows(NotFoundException.class, () -> service.get(USER_ID)); } @Test - public void deletedNotFound() { + void deletedNotFound() { assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND)); } @Test - public void get() { + void get() { User user = service.get(ADMIN_ID); USER_MATCHER.assertMatch(user, UserTestData.admin); } @Test - public void getNotFound() { + void getNotFound() { assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND)); } @Test - public void getByEmail() { + void getByEmail() { User user = service.getByEmail("admin@gmail.com"); USER_MATCHER.assertMatch(user, admin); } @Test - public void update() { + void update() { User updated = getUpdated(); service.update(updated); USER_MATCHER.assertMatch(service.get(USER_ID), getUpdated()); } @Test - public void getAll() { + void getAll() { List all = service.getAll(); USER_MATCHER.assertMatch(all, admin, user); } @Test - public void createWithException() throws Exception { + void createWithException() throws Exception { validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, " ", "mail@yandex.ru", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", " ", "password", Role.USER))); validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", " ", Role.USER))); diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java index 61847184..6c912cdc 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.service.datajpa; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.Meal; @@ -14,17 +14,17 @@ import static ru.javawebinar.topjava.UserTestData.admin; @ActiveProfiles(DATAJPA) -public class DataJpaMealServiceTest extends AbstractMealServiceTest { +class DataJpaMealServiceTest extends AbstractMealServiceTest { @Test - public void getWithUser() { + void getWithUser() { Meal adminMeal = service.getWithUser(ADMIN_MEAL_ID, ADMIN_ID); MEAL_MATCHER.assertMatch(adminMeal, adminMeal1); UserTestData.USER_MATCHER.assertMatch(adminMeal.getUser(), admin); } @Test - public void getWithUserNotFound() { - Assert.assertThrows(NotFoundException.class, + void getWithUserNotFound() { + Assertions.assertThrows(NotFoundException.class, () -> service.getWithUser(1, ADMIN_ID)); } } diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java index 06d30e4b..ac9afece 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.service.datajpa; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; import ru.javawebinar.topjava.MealTestData; import ru.javawebinar.topjava.UserTestData; @@ -14,17 +14,17 @@ import static ru.javawebinar.topjava.UserTestData.USER_MATCHER; @ActiveProfiles(DATAJPA) -public class DataJpaUserServiceTest extends AbstractUserServiceTest { +class DataJpaUserServiceTest extends AbstractUserServiceTest { @Test - public void getWithMeals() { + void getWithMeals() { User admin = service.getWithMeals(ADMIN_ID); USER_MATCHER.assertMatch(admin, UserTestData.admin); MealTestData.MEAL_MATCHER.assertMatch(admin.getMeals(), MealTestData.adminMeal2, MealTestData.adminMeal1); } @Test - public void getWithMealsNotFound() { - Assert.assertThrows(NotFoundException.class, + void getWithMealsNotFound() { + Assertions.assertThrows(NotFoundException.class, () -> service.getWithMeals(1)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java index 9ff4ae61..aef58826 100644 --- a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java @@ -6,5 +6,5 @@ import static ru.javawebinar.topjava.Profiles.JDBC; @ActiveProfiles(JDBC) -public class JdbcMealServiceTest extends AbstractMealServiceTest { +class JdbcMealServiceTest extends AbstractMealServiceTest { } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java index 419f68ed..62ca7668 100644 --- a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java @@ -6,5 +6,5 @@ import static ru.javawebinar.topjava.Profiles.JDBC; @ActiveProfiles(JDBC) -public class JdbcUserServiceTest extends AbstractUserServiceTest { +class JdbcUserServiceTest extends AbstractUserServiceTest { } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java index 70e7bf86..aaf5dcda 100644 --- a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java @@ -6,5 +6,5 @@ import static ru.javawebinar.topjava.Profiles.JPA; @ActiveProfiles(JPA) -public class JpaMealServiceTest extends AbstractMealServiceTest { +class JpaMealServiceTest extends AbstractMealServiceTest { } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java index d1b3e469..6d1cd915 100644 --- a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java @@ -6,5 +6,5 @@ import static ru.javawebinar.topjava.Profiles.JPA; @ActiveProfiles(JPA) -public class JpaUserServiceTest extends AbstractUserServiceTest { +class JpaUserServiceTest extends AbstractUserServiceTest { } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java index 3c4b902e..c7ce4cda 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -1,11 +1,8 @@ package ru.javawebinar.topjava.web; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -17,13 +14,13 @@ import javax.annotation.PostConstruct; -@ContextConfiguration({ +@SpringJUnitWebConfig(locations = { "classpath:spring/spring-app.xml", "classpath:spring/spring-mvc.xml", "classpath:spring/spring-db.xml" }) -@WebAppConfiguration -@RunWith(SpringJUnit4ClassRunner.class) +//@WebAppConfiguration +//@ExtendWith(SpringExtension.class) @Transactional @ActiveProfiles(resolver = AllActiveProfileResolver.class) public abstract class AbstractControllerTest { diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java index a9e94ef6..79f00a8e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.web; -import org.junit.Test; +import org.junit.jupiter.api.Test; import ru.javawebinar.topjava.UserTestData; import static org.hamcrest.Matchers.*; @@ -9,10 +9,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; -public class RootControllerTest extends AbstractControllerTest { +class RootControllerTest extends AbstractControllerTest { @Test - public void getUsers() throws Exception { + void getUsers() throws Exception { perform(get("/users")) .andDo(print()) .andExpect(status().isOk()) diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java index 8f769cf5..7568d0f5 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -1,21 +1,18 @@ package ru.javawebinar.topjava.web.user; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository; import ru.javawebinar.topjava.util.exception.NotFoundException; import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -@ContextConfiguration({"classpath:spring/inmemory.xml"}) -@RunWith(SpringRunner.class) -public class InMemoryAdminRestControllerSpringTest { +@SpringJUnitConfig(locations = {"classpath:spring/inmemory.xml"}) +class InMemoryAdminRestControllerSpringTest { @Autowired private AdminRestController controller; @@ -23,19 +20,19 @@ public class InMemoryAdminRestControllerSpringTest { @Autowired private InMemoryUserRepository repository; - @Before - public void setup() { + @BeforeEach + void setup() { repository.init(); } @Test - public void delete() { + void delete() { controller.delete(USER_ID); - Assert.assertNull(repository.get(USER_ID)); + Assertions.assertNull(repository.get(USER_ID)); } @Test - public void deleteNotFound() { - Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + void deleteNotFound() { + Assertions.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java index 1a348525..e6ebc59c 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.web.user; -import org.junit.*; +import org.junit.jupiter.api.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; @@ -13,42 +13,42 @@ import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -public class InMemoryAdminRestControllerTest { +class InMemoryAdminRestControllerTest { private static final Logger log = LoggerFactory.getLogger(InMemoryAdminRestControllerTest.class); private static ConfigurableApplicationContext appCtx; private static AdminRestController controller; private static InMemoryUserRepository repository; - @BeforeClass - public static void beforeClass() { + @BeforeAll + static void beforeClass() { appCtx = new ClassPathXmlApplicationContext("spring/inmemory.xml"); log.info("\n{}\n", Arrays.toString(appCtx.getBeanDefinitionNames())); controller = appCtx.getBean(AdminRestController.class); repository = appCtx.getBean(InMemoryUserRepository.class); } - @AfterClass - public static void afterClass() { + @AfterAll + static void afterClass() { // May cause during JUnit "Cache is not alive (STATUS_SHUTDOWN)" as JUnit share Spring context for speed // http://stackoverflow.com/questions/16281802/ehcache-shutdown-causing-an-exception-while-running-test-suite // appCtx.close(); } - @Before - public void setup() { + @BeforeEach + void setup() { // re-initialize repository.init(); } @Test - public void delete() { + void delete() { controller.delete(USER_ID); - Assert.assertNull(repository.get(USER_ID)); + Assertions.assertNull(repository.get(USER_ID)); } @Test - public void deleteNotFound() { - Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + void deleteNotFound() { + Assertions.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); } } \ No newline at end of file From 3cf4228408a415ba7cc201c4bbd99f3d2c717d72 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:07:19 +0300 Subject: [PATCH 081/144] 7_11_rest_controller.patch --- .../topjava/web/user/AdminRestController.java | 37 ++++++++++++++----- .../web/user/ProfileRestController.java | 15 ++++++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index b37a8ed6..007315eb 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -1,40 +1,59 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.stereotype.Controller; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; +import java.net.URI; import java.util.List; -@Controller +@RestController +@RequestMapping(value = AdminRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class AdminRestController extends AbstractUserController { + static final String REST_URL = "/rest/admin/users"; + @Override + @GetMapping public List getAll() { return super.getAll(); } @Override - public User get(int id) { + @GetMapping("/{id}") + public User get(@PathVariable int id) { return super.get(id); } - @Override - public User create(User user) { - return super.create(user); + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createWithLocation(@RequestBody User user) { + User created = super.create(user); + URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() + .path(REST_URL + "/{id}") + .buildAndExpand(created.getId()).toUri(); + return ResponseEntity.created(uriOfNewResource).body(created); } @Override - public void delete(int id) { + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable int id) { super.delete(id); } @Override - public void update(User user, int id) { + @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.NO_CONTENT) + public void update(@RequestBody User user, @PathVariable int id) { super.update(user, id); } @Override - public User getByMail(String email) { + @GetMapping("/by") + public User getByMail(@RequestParam String email) { return super.getByMail(email); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 7d3702c3..4887ff8b 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -1,22 +1,31 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.stereotype.Controller; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import static ru.javawebinar.topjava.web.SecurityUtil.authUserId; -@Controller +@RestController +@RequestMapping(value = ProfileRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class ProfileRestController extends AbstractUserController { + static final String REST_URL = "/rest/profile"; + @GetMapping public User get() { return super.get(authUserId()); } + @DeleteMapping + @ResponseStatus(HttpStatus.NO_CONTENT) public void delete() { super.delete(authUserId()); } - public void update(User user) { + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.NO_CONTENT) + public void update(@RequestBody User user) { super.update(user, authUserId()); } } \ No newline at end of file From ae9b79a942538ea3f2e267115fb3342683e7f038 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:24:01 +0300 Subject: [PATCH 082/144] 7_12_rest_test_jackson.patch --- pom.xml | 8 ++++++ .../topjava/model/AbstractBaseEntity.java | 5 ++++ .../ru/javawebinar/topjava/model/User.java | 4 ++- .../web/user/AdminRestControllerTest.java | 25 +++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java diff --git a/pom.xml b/pom.xml index 37d4067c..5ff9d9dd 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 5.3.4 2.4.5 + 2.12.2 9.0.43 @@ -210,6 +211,13 @@ spring-webmvc + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-json.version} + + org.junit.jupiter diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index acb11612..901798cb 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,14 +1,19 @@ package ru.javawebinar.topjava.model; +import com.fasterxml.jackson.annotation.JsonAutoDetect; import org.hibernate.Hibernate; import org.springframework.data.domain.Persistable; import org.springframework.util.Assert; import javax.persistence.*; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; + @MappedSuperclass // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) +@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) public abstract class AbstractBaseEntity implements Persistable { public static final int START_SEQ = 100000; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 74b73788..7b955990 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -15,6 +15,7 @@ import org.hibernate.annotations.Cache; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; import javax.persistence.*; import java.util.*; @@ -62,7 +63,7 @@ public class User extends AbstractNamedEntity { // @Fetch(FetchMode.SUBSELECT) @BatchSize(size = 200) @JoinColumn(name = "user_id") //https://stackoverflow.com/a/62848296/548473 - @OnDelete(action= OnDeleteAction.CASCADE) + @OnDelete(action = OnDeleteAction.CASCADE) private Set roles; @Column(name = "calories_per_day", nullable = false, columnDefinition = "int default 2000") @@ -71,6 +72,7 @@ public class User extends AbstractNamedEntity { @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) @OrderBy("dateTime DESC") + @JsonIgnore private List meals; public User() { diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java new file mode 100644 index 00000000..0ddd38ea --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -0,0 +1,25 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.javawebinar.topjava.web.AbstractControllerTest; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; + +class AdminRestControllerTest extends AbstractControllerTest { + + private static final String REST_URL = AdminRestController.REST_URL + '/'; + + @Test + void get() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_ID)) + .andExpect(status().isOk()) + .andDo(print()) + // https://jira.spring.io/browse/SPR-14472 + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)); + } +} \ No newline at end of file From ef0368dd843247d66ebdf239c7ce8fdb1748ea66 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:28:42 +0300 Subject: [PATCH 083/144] 7_13_jackson_object_mapper.patch --- pom.xml | 10 ++++++ .../topjava/model/AbstractBaseEntity.java | 6 +--- .../ru/javawebinar/topjava/model/User.java | 3 +- .../topjava/web/json/JacksonObjectMapper.java | 31 +++++++++++++++++++ src/main/resources/spring/spring-mvc.xml | 11 ++++++- 5 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java diff --git a/pom.xml b/pom.xml index 5ff9d9dd..f6143bc9 100644 --- a/pom.xml +++ b/pom.xml @@ -217,6 +217,16 @@ jackson-databind ${jackson-json.version} + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate5 + ${jackson-json.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-json.version} + diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 901798cb..7cdc077b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,19 +1,15 @@ package ru.javawebinar.topjava.model; -import com.fasterxml.jackson.annotation.JsonAutoDetect; import org.hibernate.Hibernate; import org.springframework.data.domain.Persistable; import org.springframework.util.Assert; import javax.persistence.*; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; - @MappedSuperclass // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) -@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) +//@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) public abstract class AbstractBaseEntity implements Persistable { public static final int START_SEQ = 100000; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 7b955990..f6c12ed5 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -15,7 +15,6 @@ import org.hibernate.annotations.Cache; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; -import com.fasterxml.jackson.annotation.JsonIgnore; import javax.persistence.*; import java.util.*; @@ -72,7 +71,7 @@ public class User extends AbstractNamedEntity { @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) @OrderBy("dateTime DESC") - @JsonIgnore +// @JsonIgnore private List meals; public User() { diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java new file mode 100644 index 00000000..d91b1007 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java @@ -0,0 +1,31 @@ +package ru.javawebinar.topjava.web.json; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +/** + *

    + * Handling Hibernate lazy-loading + * + * @link https://github.com/FasterXML/jackson + * @link https://github.com/FasterXML/jackson-datatype-hibernate + * @link https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers + */ +public class JacksonObjectMapper extends ObjectMapper { + + private JacksonObjectMapper() { + registerModule(new Hibernate5Module()); + + registerModule(new JavaTimeModule()); + configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + + setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); + setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + setSerializationInclusion(JsonInclude.Include.NON_NULL); + } +} \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 8df7c9a5..2d22edad 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -5,7 +5,16 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> - + + + + + + + + + + From 9253ac1c2616720eb1c58525193f4887ae3da220 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:39:45 +0300 Subject: [PATCH 084/144] 7_14_json_assert_tests.patch --- pom.xml | 7 +++ .../topjava/web/json/JacksonObjectMapper.java | 6 +++ .../topjava/web/json/JsonUtil.java | 47 +++++++++++++++++++ src/main/resources/spring/spring-mvc.xml | 7 ++- .../topjava/web/json/JsonUtilTest.java | 28 +++++++++++ .../web/user/AdminRestControllerTest.java | 5 +- 6 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java diff --git a/pom.xml b/pom.xml index f6143bc9..1652c9ad 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ 5.7.1 3.19.0 1.3 + 1.5.0 5.4.28.Final @@ -241,6 +242,12 @@ ${hamcrest.version} test + + org.skyscreamer + jsonassert + ${jsonassert.version} + test + org.springframework diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java index d91b1007..8237df93 100644 --- a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java +++ b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java @@ -18,6 +18,8 @@ */ public class JacksonObjectMapper extends ObjectMapper { + private static final ObjectMapper MAPPER = new JacksonObjectMapper(); + private JacksonObjectMapper() { registerModule(new Hibernate5Module()); @@ -28,4 +30,8 @@ private JacksonObjectMapper() { setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); setSerializationInclusion(JsonInclude.Include.NON_NULL); } + + public static ObjectMapper getMapper() { + return MAPPER; + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java new file mode 100644 index 00000000..b931bbe0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java @@ -0,0 +1,47 @@ +package ru.javawebinar.topjava.web.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectReader; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static ru.javawebinar.topjava.web.json.JacksonObjectMapper.getMapper; + +public class JsonUtil { + + public static List readValues(String json, Class clazz) { + ObjectReader reader = getMapper().readerFor(clazz); + try { + return reader.readValues(json).readAll(); + } catch (IOException e) { + throw new IllegalArgumentException("Invalid read array from JSON:\n'" + json + "'", e); + } + } + + public static T readValue(String json, Class clazz) { + try { + return getMapper().readValue(json, clazz); + } catch (IOException e) { + throw new IllegalArgumentException("Invalid read from JSON:\n'" + json + "'", e); + } + } + + public static String writeValue(T obj) { + try { + return getMapper().writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e); + } + } + + public static String writeIgnoreProps(T obj, String... ignoreProps) { + Map map = getMapper().convertValue(obj, new TypeReference>() {}); + for (String prop : ignoreProps) { + map.remove(prop); + } + return writeValue(map); + } +} \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 2d22edad..bafeab31 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -5,17 +5,16 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> + + - - - + - diff --git a/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java b/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java new file mode 100644 index 00000000..e1f2db35 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java @@ -0,0 +1,28 @@ +package ru.javawebinar.topjava.web.json; + +import org.junit.jupiter.api.Test; +import ru.javawebinar.topjava.MealTestData; +import ru.javawebinar.topjava.model.Meal; + +import java.util.List; + +import static ru.javawebinar.topjava.MealTestData.*; + +class JsonUtilTest { + + @Test + void readWriteValue() { + String json = JsonUtil.writeValue(adminMeal1); + System.out.println(json); + Meal meal = JsonUtil.readValue(json, Meal.class); + MEAL_MATCHER.assertMatch(meal, adminMeal1); + } + + @Test + void readWriteValues() { + String json = JsonUtil.writeValue(meals); + System.out.println(json); + List meals = JsonUtil.readValues(json, Meal.class); + MEAL_MATCHER.assertMatch(meals, MealTestData.meals); + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 0ddd38ea..0202bce2 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -9,6 +9,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.admin; +import static ru.javawebinar.topjava.web.json.JsonUtil.writeIgnoreProps; class AdminRestControllerTest extends AbstractControllerTest { @@ -20,6 +22,7 @@ void get() throws Exception { .andExpect(status().isOk()) .andDo(print()) // https://jira.spring.io/browse/SPR-14472 - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)); + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(content().json(writeIgnoreProps(admin, "registered"))); } } \ No newline at end of file From 8d2d5e59eafc383e650898043ceaaec83a2df8ef Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 13:03:06 +0300 Subject: [PATCH 085/144] 7_15_tests_refactoring.patch --- pom.xml | 11 +-- .../topjava/web/json/JsonUtil.java | 10 --- .../ru/javawebinar/topjava/MealTestData.java | 2 +- .../ru/javawebinar/topjava/TestMatcher.java | 27 ++++++-- .../java/ru/javawebinar/topjava/TestUtil.java | 26 +++++++ .../ru/javawebinar/topjava/UserTestData.java | 2 +- .../topjava/web/RootControllerTest.java | 23 ++++--- .../web/user/AdminRestControllerTest.java | 68 +++++++++++++++++-- .../web/user/ProfileRestControllerTest.java | 48 +++++++++++++ 9 files changed, 178 insertions(+), 39 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/TestUtil.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java diff --git a/pom.xml b/pom.xml index 1652c9ad..97019f91 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,7 @@ 5.7.1 3.19.0 - 1.3 - 1.5.0 + 2.2 5.4.28.Final @@ -238,16 +237,10 @@ org.hamcrest - hamcrest-all + hamcrest-core ${hamcrest.version} test - - org.skyscreamer - jsonassert - ${jsonassert.version} - test - org.springframework diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java index b931bbe0..fda04590 100644 --- a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java +++ b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java @@ -1,12 +1,10 @@ package ru.javawebinar.topjava.web.json; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectReader; import java.io.IOException; import java.util.List; -import java.util.Map; import static ru.javawebinar.topjava.web.json.JacksonObjectMapper.getMapper; @@ -36,12 +34,4 @@ public static String writeValue(T obj) { throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e); } } - - public static String writeIgnoreProps(T obj, String... ignoreProps) { - Map map = getMapper().convertValue(obj, new TypeReference>() {}); - for (String prop : ignoreProps) { - map.remove(prop); - } - return writeValue(map); - } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index e19989f9..457e2f72 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -10,7 +10,7 @@ import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class MealTestData { - public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator("user"); + public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator(Meal.class, "user"); public static final int NOT_FOUND = 10; public static final int MEAL1_ID = START_SEQ + 2; diff --git a/src/test/java/ru/javawebinar/topjava/TestMatcher.java b/src/test/java/ru/javawebinar/topjava/TestMatcher.java index 131cdef8..11e9aeb4 100644 --- a/src/test/java/ru/javawebinar/topjava/TestMatcher.java +++ b/src/test/java/ru/javawebinar/topjava/TestMatcher.java @@ -1,29 +1,48 @@ package ru.javawebinar.topjava; +import org.springframework.test.web.servlet.ResultMatcher; + import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static ru.javawebinar.topjava.TestUtil.readListFromJsonMvcResult; public class TestMatcher { + private final Class clazz; private final String[] fieldsToIgnore; - private TestMatcher(String... fieldsToIgnore) { + private TestMatcher(Class clazz, String... fieldsToIgnore) { + this.clazz = clazz; this.fieldsToIgnore = fieldsToIgnore; } - public static TestMatcher usingIgnoringFieldsComparator(String... fieldsToIgnore) { - return new TestMatcher<>(fieldsToIgnore); + public static TestMatcher usingIgnoringFieldsComparator(Class clazz, String... fieldsToIgnore) { + return new TestMatcher<>(clazz, fieldsToIgnore); } public void assertMatch(T actual, T expected) { assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); } - public void assertMatch(Iterable actual, T... expected) { + @SafeVarargs + public final void assertMatch(Iterable actual, T... expected) { assertMatch(actual, List.of(expected)); } public void assertMatch(Iterable actual, Iterable expected) { assertThat(actual).usingElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); } + + public ResultMatcher contentJson(T expected) { + return result -> assertMatch(TestUtil.readFromJsonMvcResult(result, clazz), expected); + } + + @SafeVarargs + public final ResultMatcher contentJson(T... expected) { + return contentJson(List.of(expected)); + } + + public ResultMatcher contentJson(Iterable expected) { + return result -> assertMatch(readListFromJsonMvcResult(result, clazz), expected); + } } diff --git a/src/test/java/ru/javawebinar/topjava/TestUtil.java b/src/test/java/ru/javawebinar/topjava/TestUtil.java new file mode 100644 index 00000000..3c1b6330 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/TestUtil.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava; + +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import ru.javawebinar.topjava.web.json.JsonUtil; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +public class TestUtil { + public static String getContent(MvcResult result) throws UnsupportedEncodingException { + return result.getResponse().getContentAsString(); + } + + public static T readFromJson(ResultActions action, Class clazz) throws UnsupportedEncodingException { + return JsonUtil.readValue(getContent(action.andReturn()), clazz); + } + + public static T readFromJsonMvcResult(MvcResult result, Class clazz) throws UnsupportedEncodingException { + return JsonUtil.readValue(getContent(result), clazz); + } + + public static List readListFromJsonMvcResult(MvcResult result, Class clazz) throws UnsupportedEncodingException { + return JsonUtil.readValues(getContent(result), clazz); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index c41c535a..60c55120 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -9,7 +9,7 @@ import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { - public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator("registered", "meals"); + public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator(User.class, "registered", "meals"); public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java index 79f00a8e..676fb6e6 100644 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -1,13 +1,15 @@ package ru.javawebinar.topjava.web; +import org.assertj.core.matcher.AssertionMatcher; import org.junit.jupiter.api.Test; -import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; + +import java.util.List; -import static org.hamcrest.Matchers.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; +import static ru.javawebinar.topjava.UserTestData.*; class RootControllerTest extends AbstractControllerTest { @@ -18,12 +20,13 @@ void getUsers() throws Exception { .andExpect(status().isOk()) .andExpect(view().name("users")) .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")) - .andExpect(model().attribute("users", hasSize(2))) - .andExpect(model().attribute("users", hasItem( - allOf( - hasProperty("id", is(START_SEQ)), - hasProperty("name", is(UserTestData.user.getName())) - ) - ))); + .andExpect(model().attribute("users", + new AssertionMatcher>() { + @Override + public void assertion(List actual) throws AssertionError { + USER_MATCHER.assertMatch(actual, admin, user); + } + } + )); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 0202bce2..e9f25a45 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -1,21 +1,31 @@ package ru.javawebinar.topjava.web.user; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.util.exception.NotFoundException; import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; -import static ru.javawebinar.topjava.UserTestData.admin; -import static ru.javawebinar.topjava.web.json.JsonUtil.writeIgnoreProps; +import static ru.javawebinar.topjava.TestUtil.readFromJson; +import static ru.javawebinar.topjava.UserTestData.*; class AdminRestControllerTest extends AbstractControllerTest { private static final String REST_URL = AdminRestController.REST_URL + '/'; + @Autowired + private UserService userService; + @Test void get() throws Exception { perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_ID)) @@ -23,6 +33,56 @@ void get() throws Exception { .andDo(print()) // https://jira.spring.io/browse/SPR-14472 .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(content().json(writeIgnoreProps(admin, "registered"))); + .andExpect(USER_MATCHER.contentJson(admin)); + } + + @Test + void getByEmail() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + "by?email=" + user.getEmail())) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(USER_MATCHER.contentJson(user)); + } + + @Test + void delete() throws Exception { + perform(MockMvcRequestBuilders.delete(REST_URL + USER_ID)) + .andDo(print()) + .andExpect(status().isNoContent()); + assertThrows(NotFoundException.class, () -> userService.get(USER_ID)); + } + + @Test + void update() throws Exception { + User updated = UserTestData.getUpdated(); + perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andExpect(status().isNoContent()); + + USER_MATCHER.assertMatch(userService.get(USER_ID), updated); + } + + @Test + void createWithLocation() throws Exception { + User newUser = UserTestData.getNew(); + ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(newUser))) + .andExpect(status().isCreated()); + + User created = readFromJson(action, User.class); + int newId = created.id(); + newUser.setId(newId); + USER_MATCHER.assertMatch(created, newUser); + USER_MATCHER.assertMatch(userService.get(newId), newUser); + } + + @Test + void getAll() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(USER_MATCHER.contentJson(admin, user)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java new file mode 100644 index 00000000..e6039ce6 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -0,0 +1,48 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.UserTestData.*; +import static ru.javawebinar.topjava.web.user.ProfileRestController.REST_URL; + +class ProfileRestControllerTest extends AbstractControllerTest { + + @Autowired + private UserService userService; + + @Test + void get() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(USER_MATCHER.contentJson(user)); + } + + @Test + void delete() throws Exception { + perform(MockMvcRequestBuilders.delete(REST_URL)) + .andExpect(status().isNoContent()); + USER_MATCHER.assertMatch(userService.getAll(), admin); + } + + @Test + void update() throws Exception { + User updated = getUpdated(); + perform(MockMvcRequestBuilders.put(REST_URL).contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andDo(print()) + .andExpect(status().isNoContent()); + + USER_MATCHER.assertMatch(userService.get(USER_ID), updated); + } +} \ No newline at end of file From f25216e5420e24a787eaaafac42a82aed094861d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 25 Mar 2021 13:14:33 +0300 Subject: [PATCH 086/144] 7_16_soapui_utf8_converter.patch --- config/Topjava-soapui-project.xml | 395 ++++++++++++++++++ .../web/user/ProfileRestController.java | 5 + src/main/resources/spring/spring-mvc.xml | 8 + 3 files changed, 408 insertions(+) create mode 100644 config/Topjava-soapui-project.xml diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml new file mode 100644 index 00000000..f9668d76 --- /dev/null +++ b/config/Topjava-soapui-project.xml @@ -0,0 +1,395 @@ + + + + + + + + http://localhost:8080 + + + + + + + + + text/html;charset=utf-8 + 500 401 + + html + + + application/json + 200 + + ns:Response + + + application/json;charset=UTF-8 + 200 + + Response + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/admin/users + + user@yandex.ru + password + No Authorization + Basic + No Authorization + + + + + + + + + + + application/json + + + + text/html;charset=utf-8 + 500 + + html + + + application/json + 201 + + user:Response + + + application/json;charset=UTF-8 + 201 + + user:Response + + + + <xml-fragment/> + + http://localhost:8080 + {"name": "New2", + "email": "new2@yandex.ru", + "password": "passwordNew", + "roles": ["USER"] + } + + http://localhost/topjava/rest/admin/users + + No Authorization + Basic + No Authorization + + + + + + + + + + + + + + + text/html;charset=utf-8 + 500 + + html + + + application/json + 200 + + ns:Response + + + application/json;charset=UTF-8 + 200 + + ns:Response + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/admin/users/100000 + + No Authorization + Basic + No Authorization + + + + + + + + + + + text/html;charset=utf-8 + 405 500 + + html + + + application/json + + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + <xml-fragment/> + + http://localhost:8080 + {"name": "UserUpdated", + "email": "user@yandex.ru", + "password": "passwordNew", + "roles": ["USER"] + } + + http://localhost/topjava/rest/admin/users/100000 + + Basic + Basic + Global HTTP Settings + + + + + + + + + + + + + + + text/html;charset=utf-8 + 500 + + html + + + application/json + 200 + + ns:Response + + + application/json;charset=UTF-8 + 200 + + prof:Response + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/profile + + Basic + Basic + Global HTTP Settings + + + + + + + + + + + application/json + + + + text/html;charset=utf-8 + 500 405 + + html + + + application/json + 201 + + user:Response + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + 200 + + data + + + + <xml-fragment/> + + http://localhost:8080 + {"name": "New777", + "email": "new777@yandex.ru", + "password": "passwordNew", + "roles": ["USER"] + } + + http://localhost/topjava/rest/profile + + No Authorization + Basic + No Authorization + + + + + + + + + + + + 200 + + data + + + text/html;charset=utf-8 + 500 + + html + + + application/json + + + + + 200 + + data + + + + 204 + + data + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/profile + + No Authorization + Basic + No Authorization + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 4887ff8b..14559e4c 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -28,4 +28,9 @@ public void delete() { public void update(@RequestBody User user) { super.update(user, authUserId()); } + + @GetMapping("/text") + public String testUTF() { + return "Русский текст"; + } } \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index bafeab31..44c70c5e 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -12,6 +12,14 @@ + + + + text/plain;charset=UTF-8 + text/html;charset=UTF-8 + + + From 016267cbe9c93de9b1f4bb77b39832ae4bc07f5d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:00:43 +0300 Subject: [PATCH 087/144] fix bug --- config/messages/app_ru.properties | 52 +++++++++++++++---------------- src/main/webapp/WEB-INF/web.xml | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 4150d65d..97f3e9f0 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -1,29 +1,29 @@ -app.title= -app.home= -app.footer= Spring 5/JPA Enterprise (Topjava) -app.login= +app.title=Подсчет калорий +app.home=Главная +app.footer=Приложение стажировки Spring 5/JPA Enterprise (Topjava) +app.login=Зайти как -user.title= -user.name= -user.email= -user.roles= -user.active= -user.registered= +user.title=Пользователи +user.name=Имя +user.email=Почта +user.roles=Роли +user.active=Активный +user.registered=Зарегистрирован -meal.title= -meal.edit= -meal.add= -meal.filter= -meal.startDate= () -meal.endDate= () -meal.startTime= () -meal.endTime= () -meal.description= -meal.dateTime=/ -meal.calories= +meal.title=Моя еда +meal.edit=Редактирование еды +meal.add=Добавление еды +meal.filter=Отфильтровать +meal.startDate=От даты (включая) +meal.endDate=До даты (включая) +meal.startTime=От времени (включая) +meal.endTime=До времени (исключая) +meal.description=Описание +meal.dateTime=Дата/Время +meal.calories=Калории -common.select= -common.delete= -common.update= -common.save= -common.cancel= \ No newline at end of file +common.select=Выбрать +common.delete=Удалить +common.update=Обновить +common.save=Сохранить +common.cancel=Отменить \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 802e276e..175ec350 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -12,7 +12,7 @@ - contextC onfigLocation + contextConfigLocation classpath:spring/spring-app.xml classpath:spring/spring-db.xml From 4f2bd300f02049e9a48f15d57d3cb2b2bd3c3a90 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:02:04 +0300 Subject: [PATCH 088/144] 8_01_HW07_controller_test.patch --- .../ru/javawebinar/topjava/to/MealTo.java | 18 +++++++++++++++++ src/main/resources/logback.xml | 3 ++- .../topjava/web/ResourceControllerTest.java | 20 +++++++++++++++++++ .../topjava/web/RootControllerTest.java | 12 +++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java diff --git a/src/main/java/ru/javawebinar/topjava/to/MealTo.java b/src/main/java/ru/javawebinar/topjava/to/MealTo.java index d14feae7..191d342e 100644 --- a/src/main/java/ru/javawebinar/topjava/to/MealTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealTo.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.to; import java.time.LocalDateTime; +import java.util.Objects; public class MealTo { private final Integer id; @@ -41,6 +42,23 @@ public boolean isExcess() { return excess; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MealTo mealTo = (MealTo) o; + return calories == mealTo.calories && + excess == mealTo.excess && + Objects.equals(id, mealTo.id) && + Objects.equals(dateTime, mealTo.dateTime) && + Objects.equals(description, mealTo.description); + } + + @Override + public int hashCode() { + return Objects.hash(id, dateTime, description, calories, excess); + } + @Override public String toString() { return "MealTo{" + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 809d4c9c..7f5b7b04 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -21,7 +21,8 @@ - + + diff --git a/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java new file mode 100644 index 00000000..24439966 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java @@ -0,0 +1,20 @@ +package ru.javawebinar.topjava.web; + +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ResourceControllerTest extends AbstractControllerTest { + + @Test + void resources() throws Exception { + perform(get("/resources/css/style.css")) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(MediaType.valueOf("text/css"))) + .andExpect(status().isOk()); + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java index 676fb6e6..d0cad54e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -9,7 +9,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.MealTestData.meals; import static ru.javawebinar.topjava.UserTestData.*; +import static ru.javawebinar.topjava.util.MealsUtil.getTos; class RootControllerTest extends AbstractControllerTest { @@ -29,4 +31,14 @@ public void assertion(List actual) throws AssertionError { } )); } + + @Test + void getMeals() throws Exception { + perform(get("/meals")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(view().name("meals")) + .andExpect(forwardedUrl("/WEB-INF/jsp/meals.jsp")) + .andExpect(model().attribute("meals", getTos(meals, SecurityUtil.authUserCaloriesPerDay()))); + } } \ No newline at end of file From 0e3dd0e94ca59e993a289224f3fd08a17a6f726f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:11:53 +0300 Subject: [PATCH 089/144] 8_02_HW07_rest_controller.patch --- .../ru/javawebinar/topjava/to/MealTo.java | 2 + .../javawebinar/topjava/util/MealsUtil.java | 2 +- .../topjava/web/meal/MealRestController.java | 61 ++++++++++++- .../ru/javawebinar/topjava/MealTestData.java | 2 + .../ru/javawebinar/topjava/TestMatcher.java | 27 ++++-- .../web/meal/MealRestControllerTest.java | 89 +++++++++++++++++++ 6 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java diff --git a/src/main/java/ru/javawebinar/topjava/to/MealTo.java b/src/main/java/ru/javawebinar/topjava/to/MealTo.java index 191d342e..d0f78638 100644 --- a/src/main/java/ru/javawebinar/topjava/to/MealTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealTo.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.to; +import java.beans.ConstructorProperties; import java.time.LocalDateTime; import java.util.Objects; @@ -14,6 +15,7 @@ public class MealTo { private final boolean excess; + @ConstructorProperties({"id", "dateTime", "description", "calories", "excess"}) public MealTo(Integer id, LocalDateTime dateTime, String description, int calories, boolean excess) { this.id = id; this.dateTime = dateTime; diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index c99874f6..65303d65 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -38,7 +38,7 @@ public static List filterByPredicate(Collection meals, int calorie .collect(Collectors.toList()); } - private static MealTo createTo(Meal meal, boolean excess) { + public static MealTo createTo(Meal meal, boolean excess) { return new MealTo(meal.getId(), meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index c3daf685..0a5dbbd5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,7 +1,64 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.stereotype.Controller; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.to.MealTo; -@Controller +import java.net.URI; +import java.time.LocalDateTime; +import java.util.List; + +@RestController +@RequestMapping(value = MealRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class MealRestController extends AbstractMealController { + static final String REST_URL = "/rest/profile/meals"; + + @Override + @GetMapping("/{id}") + public Meal get(@PathVariable int id) { + return super.get(id); + } + + @Override + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable int id) { + super.delete(id); + } + + @Override + @GetMapping + public List getAll() { + return super.getAll(); + } + + @Override + @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.NO_CONTENT) + public void update(@RequestBody Meal meal, @PathVariable int id) { + super.update(meal, id); + } + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createWithLocation(@RequestBody Meal meal) { + Meal created = super.create(meal); + + URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() + .path(REST_URL + "/{id}") + .buildAndExpand(created.getId()).toUri(); + + return ResponseEntity.created(uriOfNewResource).body(created); + } + + @GetMapping("/between") + public List getBetween( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDateTime, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDateTime) { + return super.getBetween(startDateTime.toLocalDate(), startDateTime.toLocalTime(), endDateTime.toLocalDate(), endDateTime.toLocalTime()); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index 457e2f72..9a7566d3 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava; import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.to.MealTo; import java.time.Month; import java.time.temporal.ChronoUnit; @@ -11,6 +12,7 @@ public class MealTestData { public static final TestMatcher MEAL_MATCHER = TestMatcher.usingIgnoringFieldsComparator(Meal.class, "user"); + public static TestMatcher MEAL_TO_MATCHER = TestMatcher.usingEqualsComparator(MealTo.class); public static final int NOT_FOUND = 10; public static final int MEAL1_ID = START_SEQ + 2; diff --git a/src/test/java/ru/javawebinar/topjava/TestMatcher.java b/src/test/java/ru/javawebinar/topjava/TestMatcher.java index 11e9aeb4..851181b7 100644 --- a/src/test/java/ru/javawebinar/topjava/TestMatcher.java +++ b/src/test/java/ru/javawebinar/topjava/TestMatcher.java @@ -3,25 +3,40 @@ import org.springframework.test.web.servlet.ResultMatcher; import java.util.List; +import java.util.function.BiConsumer; import static org.assertj.core.api.Assertions.assertThat; import static ru.javawebinar.topjava.TestUtil.readListFromJsonMvcResult; public class TestMatcher { private final Class clazz; - private final String[] fieldsToIgnore; + private final BiConsumer assertion; + private final BiConsumer, Iterable> iterableAssertion; - private TestMatcher(Class clazz, String... fieldsToIgnore) { + private TestMatcher(Class clazz, BiConsumer assertion, BiConsumer, Iterable> iterableAssertion) { this.clazz = clazz; - this.fieldsToIgnore = fieldsToIgnore; + this.assertion = assertion; + this.iterableAssertion = iterableAssertion; + } + + public static TestMatcher usingAssertions(Class clazz, BiConsumer assertion, BiConsumer, Iterable> iterableAssertion) { + return new TestMatcher<>(clazz, assertion, iterableAssertion); + } + + public static TestMatcher usingEqualsComparator(Class clazz) { + return usingAssertions(clazz, + (a, e) -> assertThat(a).isEqualTo(e), + (a, e) -> assertThat(a).isEqualTo(e)); } public static TestMatcher usingIgnoringFieldsComparator(Class clazz, String... fieldsToIgnore) { - return new TestMatcher<>(clazz, fieldsToIgnore); + return usingAssertions(clazz, + (a, e) -> assertThat(a).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(e), + (a, e) -> assertThat(a).usingElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(e)); } public void assertMatch(T actual, T expected) { - assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); + assertion.accept(actual, expected); } @SafeVarargs @@ -30,7 +45,7 @@ public final void assertMatch(Iterable actual, T... expected) { } public void assertMatch(Iterable actual, Iterable expected) { - assertThat(actual).usingElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); + iterableAssertion.accept(actual, expected); } public ResultMatcher contentJson(T expected) { diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java new file mode 100644 index 00000000..242c9373 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -0,0 +1,89 @@ +package ru.javawebinar.topjava.web.meal; + + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.util.exception.NotFoundException; +import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.MealTestData.*; +import static ru.javawebinar.topjava.TestUtil.readFromJson; +import static ru.javawebinar.topjava.UserTestData.USER_ID; +import static ru.javawebinar.topjava.UserTestData.user; +import static ru.javawebinar.topjava.util.MealsUtil.createTo; +import static ru.javawebinar.topjava.util.MealsUtil.getTos; + +class MealRestControllerTest extends AbstractControllerTest { + + private static final String REST_URL = MealRestController.REST_URL + '/'; + + @Autowired + private MealService mealService; + + @Test + void get() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + MEAL1_ID)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(MEAL_MATCHER.contentJson(meal1)); + } + + @Test + void delete() throws Exception { + perform(MockMvcRequestBuilders.delete(REST_URL + MEAL1_ID)) + .andExpect(status().isNoContent()); + assertThrows(NotFoundException.class, () -> mealService.get(MEAL1_ID, USER_ID)); + } + + @Test + void update() throws Exception { + Meal updated = getUpdated(); + perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andExpect(status().isNoContent()); + + MEAL_MATCHER.assertMatch(mealService.get(MEAL1_ID, USER_ID), updated); + } + + @Test + void createWithLocation() throws Exception { + Meal newMeal = getNew(); + ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(newMeal))); + + Meal created = readFromJson(action, Meal.class); + int newId = created.id(); + newMeal.setId(newId); + MEAL_MATCHER.assertMatch(created, newMeal); + MEAL_MATCHER.assertMatch(mealService.get(newId, USER_ID), newMeal); + } + + @Test + void getAll() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(MEAL_TO_MATCHER.contentJson(getTos(meals, user.getCaloriesPerDay()))); + } + + @Test + void getBetween() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + "between?startDateTime=2020-01-30T07:00&endDateTime=2020-01-31T11:00:00")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(MEAL_TO_MATCHER.contentJson(createTo(meal5, true), createTo(meal1, false))); + } +} \ No newline at end of file From cb7552a50f187a4fe5eef841f791bebb60a56a6d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:12:19 +0300 Subject: [PATCH 090/144] 8_03_HW07_formatters.patch --- .../web/converter/DateTimeFormatters.java | 39 +++++++++++++++++++ .../topjava/web/meal/MealRestController.java | 15 ++++--- src/main/resources/spring/spring-mvc.xml | 11 +++++- .../web/meal/MealRestControllerTest.java | 11 +++++- 4 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java diff --git a/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java b/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java new file mode 100644 index 00000000..bc440986 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java @@ -0,0 +1,39 @@ +package ru.javawebinar.topjava.web.converter; + +import org.springframework.format.Formatter; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; + +public class DateTimeFormatters { + public static class LocalDateFormatter implements Formatter { + + @Override + public LocalDate parse(String text, Locale locale) { + return parseLocalDate(text); + } + + @Override + public String print(LocalDate lt, Locale locale) { + return lt.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + } + + public static class LocalTimeFormatter implements Formatter { + + @Override + public LocalTime parse(String text, Locale locale) { + return parseLocalTime(text); + } + + @Override + public String print(LocalTime lt, Locale locale) { + return lt.format(DateTimeFormatter.ISO_LOCAL_TIME); + } + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index 0a5dbbd5..af1da813 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,16 +1,17 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.to.MealTo; import java.net.URI; -import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; import java.util.List; @RestController @@ -55,10 +56,12 @@ public ResponseEntity createWithLocation(@RequestBody Meal meal) { return ResponseEntity.created(uriOfNewResource).body(created); } - @GetMapping("/between") + @GetMapping("/filter") public List getBetween( - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDateTime, - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDateTime) { - return super.getBetween(startDateTime.toLocalDate(), startDateTime.toLocalTime(), endDateTime.toLocalDate(), endDateTime.toLocalTime()); + @RequestParam @Nullable LocalDate startDate, + @RequestParam @Nullable LocalTime startTime, + @RequestParam @Nullable LocalDate endDate, + @RequestParam @Nullable LocalTime endTime) { + return super.getBetween(startDate, startTime, endDate, endTime); } } \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 44c70c5e..3b43504a 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -7,7 +7,7 @@ - + @@ -23,6 +23,15 @@ + + + + + + + + + diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java index 242c9373..b006c758 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -81,9 +81,18 @@ void getAll() throws Exception { @Test void getBetween() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + "between?startDateTime=2020-01-30T07:00&endDateTime=2020-01-31T11:00:00")) + perform(MockMvcRequestBuilders.get(REST_URL + "filter") + .param("startDate", "2020-01-30").param("startTime", "07:00") + .param("endDate", "2020-01-31").param("endTime", "11:00")) .andExpect(status().isOk()) .andDo(print()) .andExpect(MEAL_TO_MATCHER.contentJson(createTo(meal5, true), createTo(meal1, false))); } + + @Test + void getBetweenAll() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + "filter?startDate=&endTime=")) + .andExpect(status().isOk()) + .andExpect(MEAL_TO_MATCHER.contentJson(getTos(meals, user.getCaloriesPerDay()))); + } } \ No newline at end of file From 9955a1da956bceb33b672c0e61134a667f32dec7 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:12:35 +0300 Subject: [PATCH 091/144] 8_04_HW07_soapui_curl.patch --- config/Topjava-soapui-project.xml | 214 ++++++++++++++++++++++++++++++ config/curl.md | 29 ++++ 2 files changed, 243 insertions(+) create mode 100644 config/curl.md diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml index f9668d76..f4b00d93 100644 --- a/config/Topjava-soapui-project.xml +++ b/config/Topjava-soapui-project.xml @@ -386,6 +386,220 @@ + + + + + + + + + 0 + + data + + + application/json;charset=UTF-8 + 200 + + Response + + + + <xml-fragment/> + + + http://localhost:8080 + + http://localhost/topjava/rest/profile/meals + + No Authorization + + + + + + + + + + + application/json + + + + application/json;charset=UTF-8 + 201 + + meal:Response + + + + <xml-fragment/> + + + http://localhost:8080 + { + "dateTime": "2020-02-01T10:00", + "description": "Новый завтрак", + "calories": 777 + } + + http://localhost/topjava/rest/profile/meals + + No Authorization + + + + + + + + + + + + + + + application/json;charset=UTF-8 + 200 + + ns:Response + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/profile/meals/100002 + + No Authorization + + + + + + + + + + + + + 200 + + data + + + application/json + + + + + 200 + + data + + + + 200 + + data + + + + <xml-fragment/> + + UTF-8 + http://localhost:8080 + { + "id": 100002, + "dateTime": "2020-01-30T10:00", + "description": "Обновленный завтрак", + "calories": 500 + } + + http://localhost/topjava/rest/profile/meals/100002 + + No Authorization + + + + + + + + + + + + + startDate + + QUERY + + + + + startTime + + QUERY + + + + + endDate + + QUERY + + + + + endTime + + QUERY + + + + + + + + + application/json;charset=UTF-8 + 200 + + Response + + + + <xml-fragment/> + + http://localhost:8080 + + http://localhost/topjava/rest/profile/meals/filter + + No Authorization + + + + + + + + + + + startDate + startTime + endDate + endTime + + + + diff --git a/config/curl.md b/config/curl.md new file mode 100644 index 00000000..c001e2bb --- /dev/null +++ b/config/curl.md @@ -0,0 +1,29 @@ +### curl samples (application deployed at application context `topjava`). +> For windows use `Git Bash` + +#### get All Users +`curl -s http://localhost:8080/topjava/rest/admin/users` + +#### get Users 100001 +`curl -s http://localhost:8080/topjava/rest/admin/users/100001` + +#### get All Meals +`curl -s http://localhost:8080/topjava/rest/profile/meals` + +#### get Meals 100003 +`curl -s http://localhost:8080/topjava/rest/profile/meals/100003` + +#### filter Meals +`curl -s "http://localhost:8080/topjava/rest/profile/meals/filter?startDate=2020-01-30&startTime=07:00:00&endDate=2020-01-31&endTime=11:00:00"` + +#### get Meals not found +`curl -s -v http://localhost:8080/topjava/rest/profile/meals/100008` + +#### delete Meals +`curl -s -X DELETE http://localhost:8080/topjava/rest/profile/meals/100002` + +#### create Meals +`curl -s -X POST -d '{"dateTime":"2020-02-01T12:00","description":"Created lunch","calories":300}' -H 'Content-Type:application/json;charset=UTF-8' http://localhost:8080/topjava/rest/profile/meals` + +#### update Meals +`curl -s -X PUT -d '{"dateTime":"2020-01-30T07:00", "description":"Updated breakfast", "calories":200}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003` \ No newline at end of file From 9d501289ef428b74e1628ab7d14bd13cddadddba Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:21:04 +0300 Subject: [PATCH 092/144] 8_05_HW8_with_meals.patch 8_06_HW8_test_with_meals.patch --- .../java/ru/javawebinar/topjava/model/Meal.java | 2 ++ .../java/ru/javawebinar/topjava/model/User.java | 7 ++++++- .../topjava/web/user/AbstractUserController.java | 5 +++++ .../topjava/web/user/AdminRestController.java | 5 +++++ .../topjava/web/user/ProfileRestController.java | 5 +++++ .../ru/javawebinar/topjava/UserTestData.java | 16 ++++++++++++++++ .../service/datajpa/DataJpaUserServiceTest.java | 6 ++---- .../topjava/web/AbstractControllerTest.java | 10 ++++++++++ .../web/user/AdminRestControllerTest.java | 10 ++++++++++ .../web/user/ProfileRestControllerTest.java | 10 ++++++++++ 10 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 9033f673..7c5a9c20 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.model; +import com.fasterxml.jackson.annotation.JsonBackReference; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import org.hibernate.validator.constraints.Range; @@ -45,6 +46,7 @@ public class Meal extends AbstractBaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) @OnDelete(action = OnDeleteAction.CASCADE) + @JsonBackReference // @NotNull private User user; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index f6c12ed5..34585fee 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -15,6 +15,7 @@ import org.hibernate.annotations.Cache; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; +import com.fasterxml.jackson.annotation.JsonManagedReference; import javax.persistence.*; import java.util.*; @@ -71,7 +72,7 @@ public class User extends AbstractNamedEntity { @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) @OrderBy("dateTime DESC") -// @JsonIgnore + @JsonManagedReference private List meals; public User() { @@ -147,6 +148,10 @@ public List getMeals() { return meals; } + public void setMeals(List meals) { + this.meals = meals; + } + @Override public String toString() { return "User{" + diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 0000f1c1..ccd46fdf 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -48,4 +48,9 @@ public User getByMail(String email) { log.info("getByEmail {}", email); return service.getByEmail(email); } + + public User getWithMeals(int id) { + log.info("getWithMeals {}", id); + return service.getWithMeals(id); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index 007315eb..7ede9855 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -56,4 +56,9 @@ public void update(@RequestBody User user, @PathVariable int id) { public User getByMail(@RequestParam String email) { return super.getByMail(email); } + + @GetMapping("/{id}/with-meals") + public User getWithMeals(@PathVariable int id) { + return super.getWithMeals(id); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 14559e4c..c6592668 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -33,4 +33,9 @@ public void update(@RequestBody User user) { public String testUTF() { return "Русский текст"; } + + @GetMapping("/with-meals") + public User getWithMeals() { + return super.getWithMeals(authUserId()); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 60c55120..27111396 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -5,11 +5,22 @@ import java.util.Collections; import java.util.Date; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { public static final TestMatcher USER_MATCHER = TestMatcher.usingIgnoringFieldsComparator(User.class, "registered", "meals"); + public static TestMatcher USER_WITH_MEALS_MATCHER = + TestMatcher.usingAssertions(User.class, +// No need use ignoringAllOverriddenEquals, see https://assertj.github.io/doc/#breaking-changes + (a, e) -> assertThat(a).usingRecursiveComparison() + .ignoringFields("registered", "meals.user").isEqualTo(e), + (a, e) -> { + throw new UnsupportedOperationException(); + }); public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; @@ -18,6 +29,11 @@ public class UserTestData { public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", Role.USER); public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN, Role.USER); + static { + user.setMeals(meals); + admin.setMeals(List.of(adminMeal2, adminMeal1)); + } + public static User getNew() { return new User(null, "New", "new@gmail.com", "newPass", 1555, false, new Date(), Collections.singleton(Role.USER)); } diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java index ac9afece..ea389889 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.MealTestData; import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.AbstractUserServiceTest; @@ -11,15 +10,14 @@ import static ru.javawebinar.topjava.Profiles.DATAJPA; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; -import static ru.javawebinar.topjava.UserTestData.USER_MATCHER; +import static ru.javawebinar.topjava.UserTestData.USER_WITH_MEALS_MATCHER; @ActiveProfiles(DATAJPA) class DataJpaUserServiceTest extends AbstractUserServiceTest { @Test void getWithMeals() { User admin = service.getWithMeals(ADMIN_ID); - USER_MATCHER.assertMatch(admin, UserTestData.admin); - MealTestData.MEAL_MATCHER.assertMatch(admin.getMeals(), MealTestData.adminMeal2, MealTestData.adminMeal1); + USER_WITH_MEALS_MATCHER.assertMatch(admin, UserTestData.admin); } @Test diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java index c7ce4cda..b7933d95 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.web; +import org.junit.jupiter.api.Assumptions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.web.servlet.MockMvc; @@ -11,6 +13,7 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.filter.CharacterEncodingFilter; import ru.javawebinar.topjava.AllActiveProfileResolver; +import ru.javawebinar.topjava.Profiles; import javax.annotation.PostConstruct; @@ -27,6 +30,9 @@ public abstract class AbstractControllerTest { private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); + @Autowired + public Environment env; + static { CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); CHARACTER_ENCODING_FILTER.setForceEncoding(true); @@ -37,6 +43,10 @@ public abstract class AbstractControllerTest { @Autowired private WebApplicationContext webApplicationContext; + public void assumeDataJpa() { + Assumptions.assumeTrue(env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.DATAJPA)), "DATA-JPA only"); + } + @PostConstruct private void postConstruct() { mockMvc = MockMvcBuilders diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index e9f25a45..f3269847 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -85,4 +85,14 @@ void getAll() throws Exception { .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(USER_MATCHER.contentJson(admin, user)); } + + @Test + void getWithMeals() throws Exception { + assumeDataJpa(); + perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_ID + "/with-meals")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(USER_WITH_MEALS_MATCHER.contentJson(admin)); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index e6039ce6..9866b2cd 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -45,4 +45,14 @@ void update() throws Exception { USER_MATCHER.assertMatch(userService.get(USER_ID), updated); } + + @Test + void getWithMeals() throws Exception { + assumeDataJpa(); + perform(MockMvcRequestBuilders.get(REST_URL + "/with-meals")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(USER_WITH_MEALS_MATCHER.contentJson(user)); + } } \ No newline at end of file From 69085e645345a99ca25e9be08877501a4d40c1af Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:24:28 +0300 Subject: [PATCH 093/144] 8_07_webjars.patch --- pom.xml | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pom.xml b/pom.xml index 97019f91..8b762f4f 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,13 @@ 3.9.2 + + + 4.6.0-1 + 3.6.0 + 2.5.20-1 + 3.1.4 + 1.10.24 @@ -211,6 +218,50 @@ spring-webmvc + + + org.webjars + jquery + ${webjars-jquery.version} + + + org.webjars + bootstrap + ${webjars-bootstrap.version} + + + org.webjars + jquery + + + + org.webjars + popper.js + + + + + org.webjars + datatables + ${webjars-datatables.version} + + + org.webjars + jquery + + + + + org.webjars + datetimepicker + ${webjars-datetimepicker.version} + + + org.webjars.bower + noty + ${webjars-noty.version} + + com.fasterxml.jackson.core From 997944fdb023dd149c958ca8f11771570d59954f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 14:55:08 +0300 Subject: [PATCH 094/144] 8_08_bootstrap4.patch --- config/messages/app.properties | 6 +- config/messages/app_ru.properties | 8 ++- src/main/resources/spring/spring-mvc.xml | 3 + .../WEB-INF/jsp/fragments/bodyHeader.jsp | 14 ++++- .../webapp/WEB-INF/jsp/fragments/footer.jsp | 8 ++- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 6 +- src/main/webapp/WEB-INF/jsp/index.jsp | 22 +++---- src/main/webapp/WEB-INF/jsp/users.jsp | 56 ++++++++++-------- src/main/webapp/resources/css/style.css | 25 ++++++-- .../webapp/resources/images/icon-meal.png | Bin 0 -> 1898 bytes 10 files changed, 97 insertions(+), 51 deletions(-) create mode 100644 src/main/webapp/resources/images/icon-meal.png diff --git a/config/messages/app.properties b/config/messages/app.properties index 6b4d2d15..65d9fe20 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -1,9 +1,10 @@ app.title=Calories management -app.home=Home -app.footer=Internship Spring 5/JPA Enterprise (Topjava) application +app.footer=Spring 5/JPA Enterprise (Topjava) internship application app.login=Login as user.title=Users +user.edit=Edit user +user.add=Add user user.name=Name user.email=Email user.roles=Roles @@ -22,6 +23,7 @@ meal.description=Description meal.dateTime=Date/Time meal.calories=Calories +common.add=Add common.select=Select common.delete=Delete common.update=Update diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 97f3e9f0..7210c082 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -1,9 +1,10 @@ app.title=Подсчет калорий -app.home=Главная app.footer=Приложение стажировки Spring 5/JPA Enterprise (Topjava) app.login=Зайти как user.title=Пользователи +user.edit=Редактировать пользователя +user.add=Добавить пользователя user.name=Имя user.email=Почта user.roles=Роли @@ -11,8 +12,8 @@ user.active=Активный user.registered=Зарегистрирован meal.title=Моя еда -meal.edit=Редактирование еды -meal.add=Добавление еды +meal.edit=Редактировать еду +meal.add=Добавить еду meal.filter=Отфильтровать meal.startDate=От даты (включая) meal.endDate=До даты (включая) @@ -22,6 +23,7 @@ meal.description=Описание meal.dateTime=Дата/Время meal.calories=Калории +common.add=Добавить common.select=Выбрать common.delete=Удалить common.update=Обновить diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 3b43504a..4b60d3a0 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -37,6 +37,9 @@ + + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 5b5efe57..bba77445 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,6 +1,14 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -

    - | | -
    \ No newline at end of file + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index 0935c441..cf1331fd 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp @@ -1,4 +1,8 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -
    -
    \ No newline at end of file +<%--https://getbootstrap.com/docs/4.0/examples/sticky-footer/--%> +
    +
    + +
    +
    \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 0c77f108..e89f67e9 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -6,5 +6,9 @@ <spring:message code="app.title"/> - + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index 84719196..cc610bd7 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -6,16 +6,18 @@ -
    -
    -
    - : - - -
    +
    +
    +
    + + + +
    +
    +
    \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index dad5f1b1..b9219c62 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -8,32 +8,40 @@ -
    -

    - - - - - - - - - - - - - +
    +
    +

    + +
    + - - - - - + + + + + + + - -
    ${user.email}${user.roles}<%=user.isEnabled()%> -
    -
    + + + +

    zFlv{r&Y6E%+(c*d0Nn_r+%V4oOXp?XJtDQnouM_-rTEIKx zy2?#}o^+Yi=s2SID4*poE~>Wy^>5XP)2sDQ0e!ZWP}7?IPEwG(=VsER zJh!dL+lN8D`9aqgtHn;ACx3F3W;;>Uw`)$gff48moWT}Mjz7pzSU8`dkL zjEt;zmXzGu#^Nb@f@_T!Aqi+=D_~s7GUvmJd?%h|<4<#PVTheRXAg$`{9Yob1P4>x z0cBYbq^t0Cg8GMls3yjj>HZ~wB-VjBHu+A|Aql6nCe<%h0zD{`he18Gg{Xr~B^!}V zAFqrcRXDlKDxD6#6+R1atW@nE8q9s_^ZfaXU@kl>^Moq zFmWQ5%HQi$BsNElhgog&M$W@b6K8@2=XKqxt|?a2BmFKd8RF zXb~*!WEKy}%_9#ZxU(QHbl0ThX5LVpsU?_oXsjEm<+11jX(r2A^TYc_2jwyp1RJv@ zl@XLHrOer}CZx;s!Iu*OE9ViU{^U4aAo+M1rc0YVk{-PC^y6g6q18awq=Z?q6;jkoSy!Yfmx0;t63yu3 zgefsmBxn~3@iL4@2(f^jN31+Io@>;cnK$-sF!aOPz$W5^QlFg!YF#P_Ox5hou=B{5 z!72Eyu?`BKULCSZ`lI$XpdzCCe;`Qz<&ghzp+6Ezs_ZL%HWe)AoFFU1(`Fh}SND=K zPMD4qQwE8}^ILA{2mn8Otb6e<{Opu$P$0h^E#pbc{5Q6#R{*W29yw#6V$n5B6>5p0oO8k7XAO07;ZqZX^q; ztH3TJ#oZdHV>=UXhG*_I@xh0E%I`rhX(xTt!?Njv?nRveM*IPI(N`}r6)@S5=?AmN ztaMkuB&uxU5g;g04`%suC zW&{AWy`svZ4%96?!!#*z=fY@Glo#AFB1a^uAo%Fo@I`ZX^dw=%RK534v5;uFWd>W> zd2|eA3}tk5j5%~}&mb>oaT_`-Zxe^t@2) zZWeyD@Z|4%>T(S~f;Qr<@`>)ft1~2@tBVO&KHjFdZCix`!vHcPR8MMj+G3lDx_cp> zTK@OsVaT6J4pT-JVFvrd-}b}!w)N}Gug8m6sjThkR?{jzKxQUQSta@c;igzqU6aQ+ zs@X#Zc?^&e+^-6dfNB(v`aLgLObSa<=l3{hO@MY6QV>yQYxR1D@|Y^N85-WC9pNGz z7;#)C&02nJSmBF@9b}9aU+%9_<1qY?KCCyORfu{crYVu<;`)iLZ6INvVrp4)x9d~cS#Pwbm?T{?M8Ur#CqioH^XEc)K@;FX0l_RD;;q!=A;^o=m*(F;kLdF z62X-yL02Jk)UmvNF8_OtXOs&_GduLNi?{5p{3&lQmZ6kMr&%q~J2lQydW92Eo`c>j zd!}fnG`DA0xA_j$cQwc<93y2(cKYIwhO(|J9V2bTDdSst<=W=>PNE@;%96_plS8kM z<$x^+W5j7=iDV<1>UFRP-5m;b(MQ=}J|^^lZdY>a11qESFlrzoISuv+AXta44{YB! zzPwWn4nzdZ0TKD>Ph+ItFZ5qiME;jE|La2k5s>3vnABf*tAX?d(0E#QQ$aM%S)34f zLrF<7k>C!YV-2bR!khD`+2y?*0D{O+IbZ#_5M~p@yT|8rll!vjO#JLa&5FLK8hRmMEoc1+r^edUHR>3g#y z)dTn4rZTNNd>nl%amHOG(2g#NHYE1Mw#%N^m$kEK8Um%;Q~s>q;%zXWymj+&64oTQ zn5osF*xl~-A9{Q4QE3Zj0KXsn+sN?YA0opqO32@mTwKJ9bPlrb&Mle;rq~I7{ag}$ zpfL%#u})KOfcYG8)p2mZDL*Q1J6{l~cVO_^`|XZCm@f6Qd`3?iOkYFdYgyw&^KLs7 zYhy@4zPFVI)pBe@BK?Gh?|;~hsKUsVp3uaRf|!M=ej4(Y`0iD97^d=|8z|fLGP3v#&zVWL8S%S2a4;JNP-B7}+Ok z>nAM;=?hi$J|oQxrS`$~l_7nb7D%WaU;vZ9H4)uEnCKTb!tqow#R5n>4 z-6ilweXNnNR(hZ9hhX7h58s2OnWb4O05{Gjuvg}7jB2!YI+9}Rp>Fv`F#$d=h+QOV zkR8#LYdwR2iuHg>v~0iLf(1@lf2qF&;uA{p@D5$9`(S|BfbT7>6PYcB^;{IDuuql_ zH;d}Z!foh>Bg>)NpWp0H#TSW(SB9vYY`u?rWoVM?TxvfoWt9T2^imZ2BKYGM>$5c~?D71P$eB>$KB6yY$2q9>>gDoV-Xl3Wqsge&xmWS>T_f{1ik!f&Qe?xg8_|iOzf6I_|z)m)w*8r4z#) zVGFkN6_q5v(O^5Lik`uWu7aM9-io0Dgvcw(FUl**3#(sOcMT+l9i~}33Hmh)^L}xb z`2jFq{@)t!uXme&8Bavt06>pnX!1hy`OAd0a<3ZNX|<)bwVZe7rDU0f1rs4}v7k8N zp-|wcvsUT`m;`lIqr-Op)n%(T2_lG71%n$pydD|;SY18*f_r>}-HGbsE8C(+XOzVv zw7gA&3G2f{;9Wj(S(4U;FOXvw5A14aJI034SzBRL{RXi!B6+(S??v!nQB(NE7t?TYt>Mzyqh9A4q!`$SMrbByDP zSm3Ok`>M@)KQiJ5>rcgwt&n{3rs$IW)iJlx)y+QkjX15Veu*`eEGVtDG`+<1DRa0_ ztw3D&t~5PT3oZ|j`$}}N=hLZU;wn!ftnc}(x?3!%iLqJKT0xEoX8m^1spSSuW^jU5 z&O3kbZjp=l-Q{~EUlTUjQ#*bBw$yh)OdE0&Pp2`{;pPR{9`~)WS)a{R^1A#^1}{x7 z-2kJHpc*}&bpRK104H?-Hx%?|PKJ1oXO~>6Q5iT7s6`{S z^Cg5q`CHsgL`b_YnO}vLw7sKvAbPl5@ZWw_E?vQPHhvyaGI6i zf@fLtb4?>KEr}3xm~fOo>eo3QtIHA^6%?2qudHw(hMjWxwmcW!vC0+m#(mk}zpvFW zrNnB?Rob&S+kGRDug0;*5$!nv8cL#_(wl5m||C^nM&ppvC(t)?Lh={8fv6D(4w z*q))N5J!n2=ZIaJAmBG2FR80?x;ME!o(M}~HEnIXe)J?~Y1sfK=)h{cNHGfxJ8J4U zs*r$dy49bzRW0#o=|NQV`f*1@=`k(gv|AjJimj^DdnId0^+L7Y|z8? zf*8wy@Pu`BwaxIEc)sG)xmp<=jx=uwRQJ~8ckp>g7lLWe)FH72CS)Q4;+r0;Sga>q z)P}240xOYZmo#ayImqrwTa0v*hfFxj<__OC_OOTaJBMz@SmbZfwFHRo$HD0{QTFjX zOqCG6|1gnomd&-rPLFa}F1_h|O0{5Y%{f6Kuj8TDMsR11Fc-#K#r;I}1!dIr2gC(x zXi;vHJr)(e~ld_Ri7v!G5|v($2xR z%erQkRJuCK-G_93Zo*ap|A|9$e9QzZ-wAZSJHP`Xkuo3B0Mt%O_l9mp0cak%Fp;ni zlswc|Xl@=@`8*L{hJ+~ZexTB~lRf?I7%7QqUzKH5L4&*39wxIk<-ZN5$TJIqJAgBT z3xdDXj%BQ*16Bbjy}@nJTY=n)!?0U%(!xV~s<=rbNWKQI9rY)e7sLVepN3_>cNCca z*~WhwH~flXaZ*iy;u${W5(NoM?U{%Db65^CXb&N(_Y8?jm!BBSfWXBp6#&^L$`nuO ztv1|w2>2YiHvlJO2AwGOxDh_)2%<@I{^t z!Cb2+kxAj~q5TX_0pVQTLtnRvPmMg5AhW;&XB3Wb3EHPs5&`TVG#>3k_EpE<-#Qr4 zcQRu5x_L&sIC^x8ZSg`~Cl@&+s+Xs_eJH-b6mViXidEOad6-nWeUuAw@+ULLpcr(Zp4o=N4Dbg!y7JeZxm&T zcm=F_4QZpOSE!o}^IHMySn-tBOMHm_#HlC@IGnpbb;EvtKy&}Y-Tg!K@joGg_}4q| z*DcNkw1!?5{rK<}ExrPHNCc1tB0ob$NES7+g1AIQ zUo^t2xdxq#%s1DGCM;rXcZQcfwOk*5{-Zer)Puf3UqDZ+JB}Wkm@4r@WnLd*FA>Lo zPIanMV^PZ}uf<-a5r$Gcj*nL*AP4sD>8?0UKWx7Z z{G>isE4_j~-u#x@UY#PbdVMIk_A^m-(b{S1+oHwM2q!HAf0E?l77QOR$%*o@Me89r zwf_=VKSRlr2f}o`SuOvm);x~QR=W+b<`)steg&b93~wDUA}AXAP|WsEZHkcHBBd_H^ z)qPDS*t8C$d?0=9-72{>?2ilRaPu4i$2S6{XfvAPfh-IJthvKx-2 zJsYl@Qjsn~q3gIh@@Wz>?uZ{h#^e|)(rkCZw9{WiKQAR&L(pfAuF#ig2?~WWA=f^QZRkUwcG?g#| zd=xR62s!0I^Le6jK}d-ToMWZ-cOs3=VV9Z^=bsQE0bCryQD&XFJj1d?tlN=Y3^VAR zMJ0PY<#i%9{^~huCh8sMf_Jamf>DG4Lf(Klt@61~y zAIKpV!~%(ar%YKU2A+b){7;bb3HB%>!pXGKse^C#$P39vm#qei+6RC+SZ#wh@R@Uj zHi!EGTKI61UENk@L_H=ow1^TGw))jQ>3npzFe0$b)Yci|o$w|( zUBakw*?U}A!The#wX|CBZDPpofoJi0!J@9!B_n**K%t%Ev*F!*lP&j;2+!Q_OWbNB z@**DT=cV6~oke@;dw?&erFTNKy=1u(j7_2g*$&+k_=y@Jkx{@LD#EZuD{dpUp@7}M z+Kt}1s?VY!D{NZdsNhfy&|KZt%#+*l9-ZI3j*+FH#B-|L0@Kom_th@QjJa7 zk57yl?Co#wZSQRFZy$UE3=oplj`s0F60Zh%EhxfgcIaCGS5W?MvnJy|Tv9m`3nP1T z9h={*6BacBSa(PE8|$8@BQ7G9S&jiz6|K)g!~Kr_0_YDQSB+&FTupDKQZ7qhY-sj$UWPZVXwzl64_{%sry z;iRvIqmEo$ZY}CoIkixPkL{r~3(QH!p~ZtEFV}+*)@RWlrB@K0iFp1`1z1|;NF~w> zUv?eNC2o8h)U?gSjEW}36B=`dX_##qOw4z~rZVPG->9Mompzm2S4i=el@Io>D zm+KZj*IJ1$L&~6nUyLd)n-pXJ!M(!N9&U&XFgW+0ZsYG&+&@JIIXfLY6TROG7Qd+F z7lr}9G2=ym=ujO5VV0B31CY;bFup8Lh3X`EpwbIpL-Gq>Lu=Bj$t6KcjXOdNMiXxh z!3|em6jHIXc}~5Xl241431d40!hILG!(uICkM6&<011UxaEMTtA?2q?mUui-Lk57I z9m0JYvD=;#JnTD^Phqb-YXU>5ZDY@5mnAiVtHu?@9rRs3D8y3r*Q=w!hP(BxV8nq^ zzCsa=+~yJ{qX%fM)_=f(QCKh{yS!U@g4^gUpo%%XTRq(CNJM7#R^RS(D`#A0WN#1` zOgPzZRiKedls+PL#yrbq*2Qc)wvi9D5Cvg}-`?~o;4jctSt_c>DCt+bm$#oLwg^^@ zHr+z#E(lg5$JRr*cr6ll3Fc8=a1NV)d)@!3etxMmegPHq{;MaJDu4g7d-ODjyDaJq zi;XIS5McL%9(Tjcm#!|FilSnUr|QOs;%6|4hEQX;@dh3V5kmIFX*EE6Z0Udy-VL;I zlkK``C*>r~)}rZzi_-&yEB%~aObcQ(L&icx{4^;uVEWvW=-bov%km0fLCQts2w}YVPtk1!cP1JPf&coM4eJZBg%qJwoBmR4ueKmr`2oTU*54K%e~tJ1)#dtCQvXVH1cKnlfJgYR7dQbvEq-kQ zOLaZ*2ii|4gyfN6v44JyOTRwG@niq%5zc3a0WX{7ZHT}I!pH^!%?9$G$j4v8Hx90` z^t(d>ZzxPBW|DB{=%=8b9&Q;(ahc>u2s5^fSV;L`ABn8;=t%tLYxMBSbS`V?j=#sGXb*61P__9B19>ET=>w)rPgUV+5c> zq|oh3_k(J-K@ZE8P8zZemRRdtuOo3gWt{v)2&Cv$2xOt0dYLh$!hXW7ch)z?gBvTavA0+#lQ7|WO= zR{-^ADf1FRN!ME)2@4+F%%IOQ@8=$C*^EQDkZVcAZ9xIVpbG_ry`+Dz4n|XK)`9^H z+xNG|{cEJ;Z^r%i5)fc@0?@YbU)}1Wyz%o4HfF+wP&a3HWeA=Cg$ELW$4s>Wj^~J>KYL5W7sxy zf=O^Mm?(bGIJahxVef(Iktw9wsk{TP+oxlR4VMB(1?a+|I*Xbd6AA>lG5&hx<@N0T>=KbJl3mg;eh~zpGahQqA15{x(Tk{9!Ds?Q6Z^5>0JF|z-M6o@8w8e&$EWAjDLyz;G)y~V! zpipH#kAR|_181Uv+oGXB#2g1d9{DMX344(tpNd_R#$4>1)XLIM84^m3@4Hp-41M z_uC`GuS$P%cIc)p?)~HS@UqAt;3q+Cr-@v-RA|mT41^vDgRT^5VvV`Y;=#o#{8PL+ z(V0!+t+%~gfmgogDSh4AC=rC;WU6)OYe1;i$kcnE3i^FR$1uXZpM|6^BrR2P@EVnY zrgHbRQOalz1|Jx{NMopaMKV#5=?xy=un^!7hdCjhi|@AHE!_`YDq}IQWY-K6Hljl6 z;MG-)3*9CSRTMTd`gk^e?CUMYuq@m!K-=x?1X_S}`2s=-n1}^dr2pn+)k}hMW1yM9O5|sCMvq`-(D-}pNT?nv1vP{ughD0PGs|7UG8t$eLiTz<|vZgCTrIe*K+vb+_2Da;k9 z2JI%4Lz9eU(EB?F6BKE@ zvdU}2#kY+vcc3m-R{nL0PrbhW$L-)uL!Lq3dl%8e0rH#@=m&|=$>YoQgP_^_bCst= zY_>PAdUjx+(+M#EwqE^PdlUR+Oag3I|7l@9dPs8dMSyVt?Cl`+)}$;Hq)LXqT!_7r zIyMT0Nzoc%REgv;d}xZ^5GT*(Oye4m6L~kf4TWq`*~8e!=TIYdcbBK15Ni-9z!3AM zrF{LfW2wCu1hXv1>IkSw;rH1jl`~I|QyiA8ppCKpazXiJDi0sco7Fn+QX7>VGVE3@ zE|2FJeJ5ckO8K%w@l5ZXC?T5)e{_ViNIrB|T+LaSICSMr67lL}WCfN>@t65p+ZEd- zzg=&Ef*2C)`NAB|+!|%4xfWSn1w%!pa-*%2C~Y!~(+=s>zEGbX*oHLtQ{r>HwJb?| z*R!&_pVvuPcm2oJ-PTdok+#)uY3{9OR9G_$S~!v*am2hIS0|v6?i*?AWlQaMF4(@X z4cF_LsNbJBq8eSZ{J{DEvuRa4MdT(H0?BriY0kPP#&zZjG$xAJIVr#r%29R?gg01m zL*6#&k4>0PRI(nze~)Oup$rUl!wRIq-A;Jbi$6BUBRLM`K$Zqdtc{Wgxtl@W;nfc# zN^1SUx^bVd#)o3MwNgkr%Ciy~m9Js>68haLh;utxOVADkZyDdM zuqKd9!;xM6=vmVH?9DT{3Rycqnx4y6++B4UyrqOFw9XxR5EItH*A=ldR=!&0VLLaZ zn|+(pX;E^nzB|e2qFW{G2sVo~GV1Hn^t{7VqKvcM`Y>{P=5%!@acBa-)?zix# z;4=i|^qiUGyEsrpV;HDl@)exGpM=}*^TWX)nfXyqQS9f!S6TKG_mfzD^tfkp2`$~<5p5BsO znQdcX^0RS_2|SM$%ax%8eyqYAI;Cq&ub8V@awzf4Z7FiBH|@*v(pgHf*mrx1(8;cb z{IT2Aoiymj`;%+DH)CgW+zk`#4ARTdnd0AIhryo65#-@@D5osJO*Nv9_nYUh+ivn# zf&!5ea|mI4XE{Z-BHu9>3v}!(PjO2VK*6#%6nt@vmx36{6h>?lV|;HSMM_#kDh?7t z4ekWOb?9;=sRg=s@UzEithAi!eBmty@un+~!xrWBN z&dWH#OsuB?3gbkImBy0#{d2E~V?N%M zOzrs%dVVqrt;LItEdZOA&)St8BE^+9G$Nd?$k4WbE8`SdYn3lW9uJpYn+S6BJQe9_lSiVud__ zc~3M2$~_DOuU&|Mxf!Ye5zW=X!J)6o-T8|Pctr>-5CqF*Z>^`R6Lty{Sg)kds7#30 zY%BW#b~-rl7TtFh{Up2c&?-u1U=jma(URtw?%OnMVJ)$#OSNwtP^InPBcpq}6}JtZ zo<1Va4Wq!hr$@`@#)}KPzi-IiTw4~)FxKyG>66I23>e}6-WHP{f6Tofik-&mgnmu8 zo=IHsg<+C;9672owU6@%QeG%zp*x}nQwY@}9MwU8l4R)C&*ewJMknc@ zU1AvwT8CI%;&xb^hj?4eEJR5iV_nFuXl`&mE^6)r5Sn$ouU!Gt4CuD>c0|c3>rz%b zar`IM^?nh8gaqF8kosOKq!lgQJ6igy;jVO3%4BlXqJH&ni*H;gRyUUv^br?-0J~6R-mt>XKCGsH*|vI=IDI4VB9q84tuPivs1}RvMTDTg=knez^HpFUA^`LxN-JJR0;gsP6 zZg|8uJjJzvAdTwgdV=UDug~v-+x6K=;sm+yp9LEDX$cSfqYWy!`EAkoX|~xR+n^YE za?lkzJ-IL9!i@U;QGL#&Z{otxE?_Ra@pMZDg)SoQcr3mGyR@lkTYJfpSG!xyZ%vT)ymY%qN5g#WNS?PsEW z6*@|tN#b$SpORliP?v>;nXYC#$UBrb=74Adgbh-6`j#&4K4akeOC6peInVA6oBGdA z#&iz9bhs}lfg)N0Gd9=0Aruf->rWo(shYN|J<#&Qr?#F};`!e@L(-4jl$CL{Tqb&g zw^B?}QQgau4ZmuOR)k3b!1`6^7!NXYC@VYVQXoura{;vdW-#w9`-^ z$610ynNzZuSWwG!yFt$kjm{ghAEu{p^(o-AdmqEo5U^GuLzr4zqgDuYhuI4Q_P5!Z zG0wmVsWvq#PO%STy_M+*wlv?I^M0a~jPKC^hF8W;!3P?K1}O_S^05nFX??8hb;)_} zeb=3h*Eb{`d2b#t+T|3~=6E#|9$E~Po378aBDY1Jfr37>jmTAre$oA%#YyThHMYY^ z3jGZW0cS&PGnk#L?fCQkqQ~mvs@IL)32lmJ=mc{6U z6fSVSnXlO0H3;u*3*ajHj}3IcatK-s6NuID5x$XmlSgZU^j*neq}>XYjEo)~2G739 z$rjT|)2dJ4%}r1@H$5K&BD!mltPOcdMd$ICn(vRF(4qT+OJjYJ1R3ZH_&M%QUwS<+7oMU4R@G#+7efLwVMck*8VJ75il<;S*sco@;KKBjc)s_Ed%R4= z&NgU!YdaUynz3H_=x z-^gJ_UQP6|d)BcY7sc=$b*x8qOiR&Ya?=x-lHQoZJJ}zK-Wl>o$B~G;-cSj%`CBK< zJPG4*N1;g~>GiO4viaXY)X{@*3=sIm@USow6TLa*qcGAGJ0PQW_BIpN~Yv~)EgZ?IUdBx&S_g!r=* zBt^TdRq4H{KYEx>D;hoz@l{t7pRNZ{!GJiqEFPZPoIu&eo}h-3QI3?w9Q`;EF==v+Vt~N6NKqbBn^5?tg zd?+Io=E)L|=*DpbveM7G@j-|2i$;hvIJgbf-W?6$dRp$Xl#Eji z#NA17k^sTY+xUPkleoQ!)(@K>hE+k#{Tf4vWRL3s)#4d1*9}W6$}I85NTZuG{9qOQ zL-$& zy|e<|pbFct;nZ@bFQYS(gc~3>-a#C+^=jy89_>Pb3{t+C>U^zErQi( zHqLBxijMXLtxL#BY-XF4W8Q$b2%4sO$Cq z%q4+#8)D2`k6I;p<`_8=KP_jruIu7!{(l)wpG8f%aum?M#;9w zf&PUesc#2k#+$vUR$<_xpUd8jSVuKva2c-MxmhasnYuNTw(ZirFH;Enx^HDMLbcZj zF)Qh>D0I0NyVK~V$+kTI4x?5!9&XsKOpNH!*cI9eCigRi_0q95LUA>}H;q_IU3g_U zUn5Nw9Yf=ssx7QBP)OKX%@w$C_%3^Wu5)nA_asm(&%%*Qay8#IFH~|dRWn(`H+jsn zzT05&a-JlZ#eAY1`>n%!#+!+0by;BC??4u}%GSRWAn%LQ*79jGzzwso8MKC2dvw${ zc|KcOnO`flWIM|ieN?X+)Ap`pJ=VH4HL*cwlk#rQj;AFs=af{8H(Z7>RaDJY@*fxv z6ABY8sTa>SCaLETfyRafVg!vV8!JWZ&PQL+K_`!~@+9{m1eWKZ*&;Bemmcl;ik>D2 z-NG#3+nIr)!>_DQBj-3j90Xw(Em$6l<^UFzjgr@&ql1Wfep;6s2IY!ce{Ha)E)+qn zx|$6tCD~W_VOGpo|+(0N?wfa<|oL95>sc0O3!&Z(9w6rnePOfwg@j zoaCELZIhjByA)K_QnwzgF0UN0-6`-$6HSaNto3h){Z>^=5}C_FY@vhJc}*HjYq6M| z-t{nAva7H|A493V*Y>y2sKEhl5vF)Qu#Wz-;=Ce;j$b=Ix;DP=-S;|_nc|&7I8UNV z0gCs#wBn>~lx#>rM=F0Ag?Eu^hNImb@s8 z0-bGcNy>GtDbu2qZM*$!d5Lr<^J|ThOc~N1F{&z{R#W70s74L4&8-vPISjVyTlx8Z zHel~{W{0WZCZFV~sda!|hgc7ga5~5>sfH%B0J^3N`}@FBFIw8x#)E;#eE{;sB1N#4 zb4?eT;DAama@@k!gNEoe9Qk&EHfZ_Y^;MG z@xk=nw(!h)n-e(OfPoP0N_*~E`n=@~b=24=M~=Dmb6e-YomWy3J6;(`Fc~SCWVEcK zJc%%wf4KuEWnRKYB^h>HS0!`cGRhUZpLJxWI#yfPFSCmPD!$pWe!nP09kmZn?p9w1 zkdX)-S)V`KV_N29V-+qbGg+1AMTIE;w$ort@zOB({EqCI2aaqq`H$6IsCarld*XpFrMHu#U(ZJI4 zx8|KiWX;0B<~wKDo`=@M<>32jYa1CelOL+s@YMxbbZv@@$3gq=p%@^1v|%KHO`@d+ z9X5G+U@SgDF&3-f5UrFB-0RBN6qjSr|Oj27G@?vh@jCM;zZ!cK!v$Jf_Gz#9<{ zp1RiNIV52(@+UjRj+_O1O{kXiE{eFzMv@oy2dAh;xxB9rgTW@LB?tUN9)}}P7{#8# zB15_}quux2@2l1F?|wX=?Yn}co4>b-1y>KEgXHf%7#ma~hh}y_4Z$P7QaQXW0_@FClrc z_`E2{^TVsdH^o%4_Vr@y3MScO^PPUxW;LQ#stWA0SWhdDHLzxih$j&(0Q*uMHjE?m z?@QkWIdjX|_726*De>mGuzHbIk&t>fG^k@#Q%}yR4CUJ0$Dqil)L$A$XSneCPe|et zbyO}hnX{T7NP|&m7IrqXP_?zWe}XC?9j&kM76sGQ2s>M0Y)M^nhEJ}2be{rythwSM z3Y3Ri95maL9Yi=Tg7bJ_Nzc@eq8wezGTRuuj3lCM%HmaJU7$;I^43e-wN!Ly9Gy>Y znJWVtFc%JAUD@#-Y8v*lep$jUgWKA{9Exhestb1xEoY&-YsZ9`r3C3xHys)T2WWWWvj>p?hVP zS%-TCcXPzanW85r-fGm{ddQ?4=dTZl7R5(#xTV_$W*MU`;OhI*c-`ZO$e6D+y>I{We;gT1j_P zwgQRCnNwj;SC%Er0rg|EXwTjSPeTB@?a!Rx^WZZwq??$ot(4<8mfg*T z$wHo2`J77lBtDB-Skwoi+*3AUNj0U`{L!CNhVJ>45Zu|8vm__Iu~Au9a-mE(m-Y!l z>r@&=cC&@R)CoU}ZE8l-P(Kr@+C00V)i|s=dT1MEH`EKDiI33lC=bT0({)H45kn2^ zS=BRkUYW33YeAiD#>8}(1n&zK@DeFj&Jym>i}SZ$L|`&hb4NkY&I(z{O+Y!**9Ipm zecRY`8d|U7Qqakce#FXr-aU=YEL@2d_nn>5pgWf{tCr*;1(ZxPu#H|U>Y%aAAUl*;ubu2!zT=A68)%061(TvFnTkFT*d~kN0#wFa{lmLs-I)FwT;KrR& zq927C{Hg@1vr=mpu-AT*MV^xx%xIgdJcKpZo8B;6s0Lm0v@>~s7ImQ(hehKNb>bXD zRVhfYRp61XDqd_~m~s_6P3Wt-Y3!w^xUAYSZ(rK1Y&&+;a?xb2nHa(hW<;7I$jh~d zx$6l%;1c!@h>SBwh=aj}B)uxZtq0^VkR+YE?bwdyB!(LX0SHpKaE0R+3{1 z>@xY2&bX?g^l+lhRHfhf2LvlZB}ep=x_2y=?6KtyTeCGmn9uir_%EzONZIHlz+9>Ai*ffj z4!=N;a#vpCOxwE-CnHji|7xSnNxO^HO(-?(pKCg`I~H5hVsD^J$-io)9=sULl#9m+ z(`Z&D5J(vjI>Q)XW0+8fnpzY_6CJEq`^WO17Oz23%M!`zZ>4^Iq~4qEZ))>Oi8R2} zRc+*Sbah$4r0nkr8?bw$D>0sW6rS&jo#v>vXe31V%6D*RLEIQe_mlsE2vf)-U~Z) z3_Wz^zT~sJk1RD{m)6H@cHXeGXfi`A?&W-sg6i8a@VVt9`2qyLrmEIEb-hl3d8Za+ zM?6QgcHVwDZ?_Ov022u5$&Y6(oW=({JKY7_cE9-i?Wp8ofc%0H9ROf&=^u|u6hg(g z{zG~He)ylocgBAg-z^iq#rLgdi|>n(ml2(0l164q&1cIJq)c^{>m91`ohf&%+9+X| zGezR*1*R2_2i>-N0!Rb`;VAgK?2-%aVg%Da0iwKfXIww$Qj(%BYw*>p?sktyuAd%UoSug*U=rh-#_zgXFy`c}R)D0DU+~0&)iA8`9zTDk+7&{66 zvgK(GI()aGrKd-@!JyF6FV3cq%CoV8AgOsm_(-z>7kg>+Z1U%cYoqP~RDOAt!C<)n z77z?o4bZ{%XYXBH)pX$BOFdSE5MiFvqb`3Y&eRdC+Vp#XaBG&)`0vT0CXQK$wWHL zPo>BF#U1TC!EMzuNlz{TnY#6SN%`k3e@DTymgyDa`3G`{{2AGp=gmgKS2(?O=(vj0 z=R4h%?3C;w*_FD`D}JFW)$cJtDi52j~}K{KnaHX5ztB^g40 z0VD~buh3S&73nB6IJJ3gG|GVu$rf=Wsd0F?N4jKa@yV6QpDe4b`EH42) z403OWVViiAKfgbdHPPMW^R-6{<9F?h?UQE9Eh@X(a($*PlaTVR@)A2PW_QhuG!~IDmLa^fm zq;H@Osf*YoZ1#2r_r+jMgJ&l^>Z!V)mLf_$p3B`s-AtoMjbxg-`2&$M^Xs(=d(fnY z;~`XwSe`sUBmn`FVA|=o*WiRUw(}1Ys-6jX2neM@zdk78M?g)00g>N&))(rzO+e!n>VyZkp#Ya1tJm2v9rx=LsO>( z+8u3?^bI;3fWxs_8}(s-%4-M+>?{CfkfJ^@w7Bj^!-T>4JNRIrCaBAI7$6f$;@;z8 z3;Xg_pHOZXg1>S8qz9&F`%PEt3ndIo`I;dBy!ZO5B5Tr^$@*fU*CP&4v}H`pJrdA&ts7 zz==SB-7Q^=NSh3t;Wf~pS$6@c<>{5QWOd^ zBNHYmg>%ufW|7>oq+Xt$rP&TrlbKB)@j$8wGh>dXkf44K5s;$&SXKlKUqrxtBa8IB zVLM1f$#M;E5$4Sg*|c3F%TRhZ@<+U+h9whf<3gLDS<<#cRz*!uhBM;ZjZv_y(CD9g z2Rn{NUcIDD+F{@a199-3eoeMT0zC>t8d9USKiA)&MoQQ=F$qHOV?rUknDljwk+c#=j2_CTz=$+!LAF z$h#qQ6TzhsFSJX;!gYnUJ|O^*-yhffRDVj01Yc)nh95G>he3O7aNIhD+3YOJ@B$X< zBo~GS&V*eeD-hl0mXzKct5tLVMz|2(j-RxtG6~iH)G$pr0>B0u;rUwJ5+9iU4}GW$ zN?6i8{~!Sv$s!Vt#ySg$94;O48(((UFiNpI$}g0HBC9bZ&|n+Gs3qS|S!t$Wl)JPY zq(C1L+JmN2KSVLm-vf!%D7Zc#QezSD40kg-n+h4;CXODmuOY#n@M69Rl)h{J!}DBE z$kG9kFdR(;o3NYW_QU;|f>F>VU?TI)ChSzrv6^Mcv++;X#l_B&9zP^(qbfvxc$UxV z(e3j!kDNljF`f3o5UGlQ(x3v~m_wVS!}_$3tQj7bvvNQX?F4=_?TPe@#L8K=43>pc-Mh1x3BkhO z@{hFFu5CX)POs-Uyz~-=_YyeqqcP0@N(tt;_C$wZ1@Sm!P%*=R4^pgS3ngUS31Lws zt_OCvIWHK;noJCX+^!Zu_BBGvMW{fpO?cX@3#tK}t&V||nS~5*U>a$CyUPq#;Eb|w{Wb`0ip8JO4>P0FIA-yB&tH* zoCn-#6Vmvxf6%_Vf~}h}N#*fpO&~v>%|ndLRR`w=y-zp5aM(mDdbdeLgjx&Ay_U_rt&7hD6ej-3wxBkvL>PdD1B`UmZ>E?^E8a9g1N{z zN>Bf8__UQ88&i$Cftz@(Xr;YcR$%t2C6zY7W3HvTGRyj6l5bX-0@ty~9Rj%-nVgFP z9uEb4_HZ}UfV1+v=xl+oHTw~SBxwHfCEcYiTe7;cvqWLg*{ZrrxYl7dnlWJs&3V!J znKXYf`*R!I192eyIjIAO-|D*56YriDu*dBrks7A@(>No#)p=}cJ@GI@-Ef%sXtYG{ zs|nzI<5cwMP=z5lz}vXr)ShZ}vD(t!$$RrS?Wn332WC#r1=8Ybwvxz**eQ*Vakvjj z(=)(#5mH?}2^I?X`9#y?w#djQfzYx0jGB7F7pmtCnsJ$^33atO7XbA5L%iapy4wtE z{6wK==$Nke_KV~5-Ip6+aJI4OC6i>0Z70~!9@A_`)*hiDA##hKyz&t3fLjU2$y%Tw zk>Y~UY=C#d;z#FdA)>p=6Zu+vXk6NTrr2h0ihVF~lqlDD7dX znC7*Rts#8pHhWILS|`fn^R!5^5RL9WXTPK1E~A~7Qh3PWP-)zCZq;2E_(&omYcd5FzgA3b z{!r)&0hg|}RtLEU7|ASSKd*)W)uV>@x-Qp8rIT5HP}L!4{4iRC)(JWQ@^b-xtzK=* zgtt;3s^s_K!@b?Xt#e2Gc}g~T>p^Rmjg&BMiV8HH0bbO6Hj3nvb<%=V+< zmf^oHM`?;tA{@YtbgTz;aVRN_Jg~VpD!TN|h(uA1H!x3R5ATr3Ie#+g5b>u@6eolkTe&%Z9DHULhfB{BpBE^f+fnl45u~TxZd6e;+Ni@s-ufkp}nGVt!6SL_KuW2OuK^+H9|1KA?a_rTRBRD`eTs^*#&V3n1flyI`7&DX zo2~?K)vk#uC0!x{)hNZ0U>O-_-Nowg_Y&%%63_drEn8Xmw3F6YWiKDTKspukc zYr>MH@!Zb}vA4o$$Hq_8q+FU;d&Y%bycK82r&2|b2j+&LBtB&$S&yE%4Qxw-5iL+LQOG%v!PW8R>?s zdY=W>UO~u+B9=kOpvm=4@86&}T!Tp9^mpq{zCyN2W#5ty_j0ggZAtU9xog1W z5=qnFdbihA?CEX=9$&RCUfg-1Ia4d; zZ1#qX_;G)%=l$Vt1X{fY#KIoiMIni9IU(_H6Ps4{Z@;fE04J5{lpFs6vDx}rQkxnX z>5$Z$&v8N72(-KFnRMvw>)n~I9WC%an}c69GzVXNL{`7jMOOyJo;)41OdzQ&9aX2t zndDknh(D?l4`}gZrOeya%Zpe<8sOwMY*NpYB12aPNlZ0Vs>oH4L*ew+knCM#i7Vtv zmB$x(+-o|Jps5ZwLLBYGQ!rPKEHPrHz_owpVfXUBx~I7xY|6L1(Ar+Imvp*3X4yVS zW_`e#Gx&@Hpx0uLw|DXkQgp93H-d8>U2d5=Fk;2af%^4%eQV*)`AOf(ofSXd>UYO* z4tlSCJb^`wUnty0trtntLeJV>vF5mbb;yM;CIxsDm_1y7LwJ~7$$~V8CuOr`@#ZS; z)(tG~x$K%$kzBad7Z*?06koU*5On*fxkOtByyxPCIlc5SN}je{l~!i9F5*_QlS@{X zGeyx(5lZT13s zVo#x`l$nZ>HIVItKrP|lWXKN=ZU;}_f5MP_eEJ~`UB*(fCK0Hn7W*sL661NJhXFt{ zMJyfonM6cgd4uJskz-7xA*P zbPK;&?5prjhiTmKSl;p8F!=DTOrj?f6>F$?1|}R`>fw(JfS=5H$Wf*_7v^QA0fz#` zbt#BaU4j)>4bq^vN(8HQ_%nklEr42A8@j)@tpyAgTe8=&V|a|fy>@_Q>;WhWmJay> zaU~5IT#PV%d}*-3H6z|wQN45kLqY}tHCO$<=h&0*H$HD46*xYTU4+XSA zr3qWInfB%jN&u1)CZptq+pZolqh&{0RJav&4Hd%?SZE)!3!1Dbr|&X@8cVU@ApmE1 zAnMIj61Q*GtTpX>mLIh;>&W&Z(=Q?y0n%o&wu=kDyV|Pl;9QbVq#Q`^f4W%=f%4U8 zYj=hcy{Q7Xw#AG{%8p}iC@rwfu@E@Gx!!+kySJmGP$5!F~TbsLgvE_KwpV;9e1~*)qo`eVvTM<`#z-w2YHy(c;ygc<^ zb?Ch0T)Sf6sk_$-)!qiM^MH|z*!WqpD3)M`I@b{@j)6~tpbOF>oPOybU(8e>q%9$& zm6;=BMRxuFr((*${BxUH#h zzifEyds*b}{88|lX>4N4(707M5a68h5+WyFD+r@3+6_8IPY1Km!oC{~;oEy|ReXT=Q6NYg`SMYywAZ9Nh?pDc5I7t?e`w z2Q}K_r%)k&?_5s_cyo6CK68D}^K=}b$hqWY5Txa}GO2`r>bwb;aq5o!$Uk;9@+RHs z!+dh>B>&~KaSuJ0<5kF9~(>YY`mP5%-S&FBe>kYzP$(OR!5aTl6fDd46 zE`86(PO3uV>>lzSk%a}hSDoYy8*`>I8EU|aF>zn@r+P@@m zcf)^(7h8yCvuUC$KMSf*^nxd!W>4NWs( znla)5mZN1-DMLn%wzioCXl~bSx<>1I zl$fRiOTVs55Hy?1#Hvfxm}_%_rfovZdX0~~1*N$>5^+mUuC?Y`+fedWfv2I;fmQzK z&qY6t6&C9_mw+(m^%&gGWP>?{bNNRbGiAk1`p`x~mDwmf9FWA;pQ%EQFfwJ;LcQI8 z^)Y^I2#SYd0szdQ0scex%RkfB2>)e2g}i~oH?zaZ#NqEyFOz943m^s*ux-B0BHc27 zRFH2;6RxZ5S&q0tLF+64+D>5?6R{Kv;^_KALfnD-0~a821`Pt` zg|3y*kJUxWy!FX=Z;M8Wr+D1FnyC+Z(Sfc{84K0BdNI+}&s&ynC%Iy7GC_j80~hf( z%5Q4=)PaPlp~|@zCP*i+KA-n&$s=`-Db9$!lg#`}od)`B6phDbu9%JmQ3f{K6zqdi zewNu$@$UNfWzED3D?xnDLZ~lj%3-luVuERa7Bq$Kd7v^Y8CmF(8!Mf*rSgusw~-mELebH;wtY@txp6{&Ezrr52h< zrcPE}9A21WmTF`L&D1tn#9@+NXh=;qat!XOI!vqWk?yRSgaI5{hm{5tdk(AbG${oI zoGmjfS_vlU$rl9$QD!Qna!W>^R#8b&vHa!NIRxXK=DYXMqFZ+w4OBJ&GcZdLa_wcJ zBY}cqO%gY%@uc4L(q)C81d zs49ub;z15Hp!vso;DQ4RqcKz1QiY_y-d^F40^|h}C$W>{ar`vRPHNyD5)o2%!gWU= z`cEiDE;6G6MD2poB~^%X=?gpz|Gv_}IKmRL1w-=TLq%xpl-i#TjzV)IhuX;4DNW2H z`69?8mR4dj`QV`(ldX{h8?IO37oso4rR^473Ga`=z#*SMD@eZH%h|S{@fsePCO%FF zQ(&grl_2X!;D_r?*tM20EvlAz(__MEQw@|9D&$o!4%L= zvWHkQ!4Xv)L?m9bgG1Jq216WBMzE))-F`sO9fYMV5@HWXLwJpa+Tpedj_sjFH>YL1 zH%)NQ-fZE{Hy;;ALzy#vpCT-@pw`hGV>FT&LBsG#c9*4CSA|Ks!mVjXT&Grr|MSbb zqX!dVdgrwkmiCn7$57EcP+vr%HwuvZ3BF=rNWDOJvw2DlOV839G@3WjK!6AF2WyMJ zi^M1@aONOD<$X$+C92sDp$-bc`AW6AWDeb)3}Bz3Ftt${ieA6kAPwM_QLH6%y3cRh zW&E3z8!(=uF2>DUL-lZRKBG(U=YE>1iG;(A*PCv>C!etm9vj4Fa;|Ef8IYPc3K#bR7xy+7i5QN{`{{uZtbJ3%MLEytH#F{cLAS{_ppBPE zgyC}`P>jM5SAC&#{XWA|dB3t>>DSDj{F;qgSK%_c0joIPS1KGnM_cv)i|a*md@{Pa zPmj+E*?_o#CyD|`2jQeXJHPgd#MfA4=zRR6k#^BV={9X*p>MMEjc+mAygN5Wz7VG*0NxCD)}(2qB|S$ zwr-4JN1BM?p5d}c(>P3UL0-trSA(%SkLaM>*it&}2eZ*W+J!_#sY%$>(KzNPj9DUt zIi}qB=dMn3JVMFiJd3e3l7_M{3x>?u7FTM%!FjQl81oMTQjfrV2{sxHrbQx*^^NaN zlS~wgyb#jg8BparCvacmW+)c}WIMgfd}4!IAn}$2JpTT!-JRfvFfqaFLEk0_PzqW;sPs1=$VH9F9Ru zIq~n(ju2Ipo9I|FO}bx9b~I{if@TUcTyJtGl^W;Pt0jl_{S*>1PfM!q)s!Bza_icP zPs4FfUvc;@4u>$x)}BPON4In;Hd1iAG@xGQ){K|CMwqxi6K2AkF#}vl_waa8mn$M| zxKjv?WT}r#o@)vE0;Y^z@e2x)>@@I?n0{eR)-sXq_~o8a^E55~~P z8;ZX$1f%ki)?60rP0^Kzji0!kQZF4kRT@*IC#i&+%$T1pWedpu#9})NmnL=^#{8wmjtf_H=N}^?{|9c;So~6L|}*VvUrXLr@^1^vrqTlc7p) zU}gAx$ck3Sduvgzbv{0ecsqmepjrX4%884UzwjNJROf)H`#VNr28CMBFKL7#P9WSN z*MV!`-~nWj2Y6}eR^ZF)Wd?@}agoo!H?r6FH@ST}m~4V3d;jS{#I92l+iN1}Ew8f&j2b#^#BiNWT(@Ufog zcnZ|Rr)6Th6ymq{bioCl&w-|Fx`r4`PNrlhZBlV|JM9+Kl*7n^9WA0}Du(tUExQLi z0!{(AJJ<${mJo-eI_PRNj(yJI0AG-zBLmew0*}8UdSZkb&LdH3g&gU4qLdhN*Mw3i z{CS=~{{rutZMt#zeP=Z1|23oiYmnYQWHcdL8z&Q2r@tq*sCW&z@A^DQ&cqJo$xF{L z(U(Ei^D&G-6bS?3&M?=Bl-^y#j*gQ0d{t$IMi&i)bX~Jbd+K1ru#j56l}#nM-sM)8Gke~;c(x-y z8F3ktptRApK%vXoAxBZ4&zYND2TBkXYnH~L3^?nlF&7-JuHihEzYP1?iX~jo^ssi! zih6c5(c@sh^PZ^AU=5?5fwo2sYn8t@AI^O5_EX7kW2Xqq9=g_0>J?sU;Ys-{E=sE} zstRV+xW!xjb;|86ipI1~Z}*f`zHQ>UB<3s4A$5e+`$=t!j^)zF=9<;aiRwqpmG^q33*_*<2p5uW+x+6m$pGC4yiitfeRfp!03Q?dw{R6JF zVIzEepb0UijQ&O6+_nLTR;N911~-0<=I*T%FytR@(Y@Sa(HaEDKSULOwXCa#{>gCq z{?D-UUs~4H{-5j&|NC728x=7s?k_50h)=$G{)65_K`6QZfG!M=N4%my36>#BL9sQ) zX!1o7amMW5sfh8i+^JIhbX;5K-dtnj-d;YQK*u|w+hWynH+_4)+XGj*fse{G%KC{} zAX9r3WYKoI))baX-@ICDyQhf2@>z8-$G;eGkl+RbAM>2LZJE>RZIU0&Oa6Z|IsSL|-M@S2{&QgeHJ$pu2lhXqok>btwzJql{lvLr7-K={#@W4v80`R`%)k8JDwZx?7nJJxr@>2SyIU#D-LOP5|;$H!m@=kPc)4FviV zt@=(wG7H)MzI@gwyZ;qNNd9RMF zCrnfsZmAYTBp}Qq3J0hB<61%#-3Spc4(&?3ZI2?Q9z)~@3n-V@&@9lbhToMh70E^u z(mWV|=fWjAb+|hjTx8$^yM*-U&Fen}=-7w|7F4%o`^{7fgSiQ#i~Qy-@lE2lQz} z9cKIqovlYi0R+Sb1QmFignenS!dEPTc=+Bep9dd%7jzGro%Qj}X!GBWrr9DB#|?t0 zPHhlJE`Np{=@aH}^L37v+0qx-8V>AAAd#sl#RnrBK0X@G{y4GeC5t2VKbzMHzeWF` zq$^rb_5N$&r67U!;ePLCmjA+Lk^83|{ja^%{$b($GXf}9PwJaaKQQ%dJH|XVI}a63 zBux|c8xIOPk03S_WoyZL8ge6Pqw!sn2a@L=@Kt`M^DN#F>Y`_biRtL`ar5IZ)TzI< zpZ;eJAwsKikpmTwfzTC~BEeH$_vt~jP1C5H*PM!u8yCItlu9y%T~A7YgWPJggSI(V z-8=guvOpyju9z(%-F{P2K4AeNb!6st_i@mz-+G57$xj5&P+Se0{=>C_# zTbr~uX+|{^ljLEb5d?otzechKk^;4UkVz8BNS$ckrur1KV_uo`%k&j%p^h0CybY#% zaqe39)2D&+BJ>m=8swB5guh2_Qtn3Sk*}RW=z|L*ps_^$J=i08 zM+{EuGC4>T4mbZgxbmH#i0x^o59$G=nomwQ`GduxJUsdwt;N{fd zEQOI)@HNZGh}!C&LVIB z|I)L(gRPy3gVVo5cVcvW&!5hRZaY8U-M;|rf_MZod3dC4ufzg-zy0S86KUdyRx%ys z*v%zJHII^(CP~@4h$S8KJFs;`ukH>(G+>DUan2HA;dIjsL<|D2P_{x8kw8~pfhRZCFZUvSRgFW?}eP5zrEdR?S28!PR0##~{f zR*>*8`(Hmr9mF|EE-2#E9Cv`9(L%TLox4^xQR%zM@xUd8WlE97x}Q6VA>T&zNSI1g3jK(JYIJ;}0i9IbT}V?v zDPN9#962#@FF|#wZODx0r`V~ePYh~>L8?<|Fjymp%;c-c+y`SOvF;d%v;mb>6Y|>C zC^anjgr9{5KA^+k)`fLv#7fxtT0K~RH`FI89I{fY-@v{nO7LAO|Im2mtVCu%CF6FG zjGc)Qo zuKjFk7`aJ7Jg0CpH~uw{vf?lFqX((S3FG$^!2GX^`d_nx{~<&DZ5sXq!~9KtZ+N_t5CSE*fS@Gcz8aiswPGV#NAXzhb`AWT+aCrFjC5>rG;y23 z*0E826(a}Wg@dTf?JkLj2Odb^7E$12=3XjPzQOF7JZhO_JF?ZVHi^nK~u_FN_ANo0!5RN#flkEJ-hyH#GNfA+%l6NIS=j$9 zl>cj=q<@O&-$c;Am*iftoqzL<3N%BOc^?w3X}qpxP+x=o#=guh4;aIe?GTMn@MZ+T zZwrN`Fo%O>WSKk~xugx;;PPJ9mg z5y3c~oo2F62p6e?om*{nk_Fu+hC&8>Wtv*0vQn~^1#TLDc$qV7Ac~rNR*PVy_~J*G zD-z}#nso24X~-gB**M4o>xQS&_`^bT!eqs35MulU3R;9-XvjXkc4C=!|2qYdqjkOG zJ1Fx1Yf%30-Tof|`A=RttteRZxM9wFf8spKzcd&X`S1ETu4a5%-(*k!wD!v7Af}Z|A!{2rvlRW@ME51bE zCW!?0U~u1p5;(*P`Y-W>?{RMaZLCtjAb+nk|Cu`VuciO*x5B>@sQ#Tkb)ac&yC#bC znF;g(uk;YH!9>vxc!{;-OhnEa+anvP-nmH#>yO%6$4cU|qN7+RzwXnWrlY7WSwM1B zSt>*jE&^wko#tlxC?ghQI}2i}jh(x1e?_o<1(*^S9@N2m`N7Bu?S0Dg&6~;q7ux~_ z^n8Qee$X$e3qo6(!*R~jdA9fYD&~3h1fgSzK*IY&AkhPbF}-fbvuCf~g#(hr`hL~> zlM=>QQ^ztHD1l*L3645kIh~=EA11ag)3O%{SlaO&ZSM{f84{Ze2MW{CdP^X8K30^a zc1tc_*f_Y)gh5sqvv zmJH)}Or^=930UDWz)myd+z6W-TgmWZC+AAe35I(L?1Z+|TCKbBcC7z`=}oUIr8J&I z{Ao3pVc@1OXc$WwqTP#u43TWr`n#uL*Tee(iXJ1RCh3Xrebib4Jzd99y~H1;5J-x) zIkVr^GS0UyvZ;<=GCgggSXjt(8b5l=!l!oTH;T7?mwR?zje;-~Hk*?CN@ebCT9+B> zq?YHLFs35ZHd0<{+kI)h zyLV8j))Dpo7!MT^f|_X*#(w$-1p&SEuHt$#F3Qc6Lxe^ez5KQuz6O>+6_@F+sezXy zmih7n9u}>P{N~n(jkMVlC0+xOc>JeNa&^OmQ-IozNKv7ZSSrIPLU3TgCoB2X{=gN4 zoEF5s%}T74FM#cl$)|R_{$_C7C!s_zi!A^%PnejW`mbiCC0kV^8+wgva7C~aGcMI3 za^U&BmPeqvDj~T@f35=}_@ooh(D96^0XYzthV`V%!o)*G3XyEOg)~z$%S0GYQ92{$ zW(Zs#cPqO8RO6}7-Z}iXR?k(XHkDdPX$GR7%!_5P&q}$f0HPb=(&ab!`CHeEmWmgj zwyzF%CV5wR3l*ZgYhQXHGZ{ki1xt)XeRhF0r8HeC(lGCkg@FAQac#CmiKC^QQlPdK z1ej1}QdO5N?;PZ4^dZ@rVV<)o%DTHylbgI;M57$O&kmDGZ$tuhjLlZ|QeJP&f~^TZ z1}*K!xP5gXzl*y!aE~9-o20P>rzsf8iA|OFQJts-iM?>|%I`o-DJT?=PJC|nyO^vk z!Ka~1_|DI;EqLw$yj3iz1EC}HA&HJ9>AeTbHuE;z=BhVczA``ZSX2EFGiKJc9e9n)1yz1ty!#kC#N+E34 zHf)(oESYd_sHLql!`m`pvaU;QuX)d3rZk77vqV3Pd4?IL0o8&B zTS{vv>Zo&3Z%fDRQJs2;Wk(t*$aqr6omKR-!nY2nLovNEv>T?0KgT8;v|1%Ky^=V*=UjWDT?v`seARXXl%w|>#z6?8AKG)d0y6(cb{4%kX z04c=AqQv^Zdevwn&BJ|nqQEQY-$0cS;R%JqXE*uOFLV8P9pf~=C`G@X&A;DP zlVRNgkuU<3X(sP(+Pq&QfJH1E?-YeQVN-vO=~ie0kAi4U8@aaF=NwPo?y^*c0E^UZ znCq5E)oC}#pg3BO+SE{4h4zxf%7oK9V|MzUlVt!bMVxLr*%Fz=_E90=wR&y4Zg(5l z5tYGe)+W@xEPr(A-o4XX-$_W9y*QmuD<5+IhMqC*G45{?p>%B^ zJC(D$p|n$}WhfY48*T*;)qX6ApNnvxrP={Xhbx_tYo;H!1X|gZ%5jP%cTihxS)AQ9 zHJPp;SOqTo!%Qkxza+DMiMLL}%RG%^=NNO~1*#F9N2)p>u7Bk@WKsTRuGzX7GNT(4 zt;J-X!fg#fPK7nvM=}XDwSD3yR8w@VNIHkBG}EbSJ>R^y6Xxc;8}Btg+j9G8T=3HL zWMhkmLzCY@(pulHBZ>KH*hvd1)ejRrwpq~lW61( zR?(J{9{gaBqp@A_cWl4*{#8nZ6{=4D1pDw|1W=JT0kk`xXOa~ zwvqYxsTvU+lLPgyw+N>GB|i$HaY>d#L^}^ zY^b~x$yH{{vXmPXs=^K%xn227{+Y1Aeg+8|vk_$xF6RYx95K(yz0FZvD*c{2{TOG?xZaR%+Cm>O*Wx;p&RqG8kk%qrT#GVPCs-3zN<`FNEAW|Goa0lt+umD zJU0rBCSLfPi-AoY4O%a+`^Rn&bAb=&0<;~(X^i5q5dGW5QtH(@>26*6&r!JB#r(+C zB-wO`H@)tmD&A)nt40}+4wKv%rJtKIDUynz8)%W4i?)7ONV+cLy=$=*K~g_KMl4Mv}eu{L~#tl@J<3RDgESh013MZdM-DWP!g(yd8{?VRw4>+BeHVR5Gh_ zrT-9#CHSg|CxK&axLTfyMXg$#O$u+G&v5XIKlXRw9Dj~~a1pPph6hwU%AEY)KY*IG+?QN&4>Tzo9lCy)= zMWzN>b0f)Xf5YJQ=9L>)aUB_FlEVC}CDP~xjSS((wuv*%OvSTrPDB?^V|YI%7-x=h z;~}%0LZqQiAzGU6+Le02InKIY+|%DB2AF#vmo|-*40$W3Rv}z(-L(g5AE8<5u!vz> zV+(3TFI?J4NmWTOUo8hu_^&O5CQL`;xkRQoh<%UJw*92gZbiVq`BV`5x(u&<#PN`k zP?a_q60qeJA^~sHqbzihFEs_Jq3zVgrX{yZ#-^Mvj#^MYAiD-CtLgTIR4BB^)@$KJ zhLBx6RGMb*PEO?fVA4_6h1t&v;p_qFt&||4RmNjoQ2P=v3NK;aQ;=WBsP%({u4_Z~ z3E16;Lnsa$lKx>$A34@83oRi*FZ(S{FNitnBP|#jw>R_v>V4GyK%c&Q7*#*CRy{u1S@pY+fllgj3r60Fbp$9E6%Ah1@Ix#zJlMilXAjme!^OpV(!=I%6 zW<~KouuIzM62o{f$9Muk-CL+|sl$VM=e)U_hIP=skYzA#E~Pu-Qaz3;}K9XpJ36DM98 zWP?e_++PFzjD`RWt5x8dRx~ZxLBx$~6Par&tJ;`@C@#?f;@CTE>Mmw3W)*l`(u!B$ ztd9_wCtH5StoKV4WJ;*wh`A@lT{`HmgTO8V)TB%)VDyspf( z4=oRUO_-1Xta*%A>lEHOGx)Wkx`rJn9-lfGjRfGSYho1!!$kCo#0+%Zd361V=|il8 zg*xG~8PxAET~0al8p9lWz=fw7@zkfv?z{-ihdU9IYH|8fVgnLNv)a3>a;26xAf4sG z3jem@7HG~(fomk;gA1wI#g<}att)y_&ZZ~O%B!>hyg*#*fAK7Ro(1;}T8W3($gdrV zip5adJFkT|yLT(AqwymOR zLy1lkvzIvi{XRICBSj?TCt7OGvS1rmy0Yu}b925vw-c|)L2GK4UfL30OQ?u6zZhxU)`kYUH>0I%hDsIWdMck`N=ZvduHfG&II@L^Nje#R$!-o5+&TGH%7`k>`tFCvH2I zCN&(`xtc8~j9s8dlw<#;30bcdiPa_OJoQZF{xy+5Y}U6AIBkmp6)QhbrJMZb@*+_> ziYK~O2A=w^uLN^>ins6g>Lm5lqP#BP-i*;7mtTOAHTcq=JydBoKwNGWbkPIR-obuV zHb4yEdcoiyKKK9>IRCq{`D^R<@3ns9j2#{SNgP4`mptTf;?{0on-f?BBj; zcXfFo%_x2~lB;cUHj;A_1!esH*=+O&Xe}QfaHv!#rqXekk?eD{Y=`4zFE^4r^{xiHw8IVJuG)iroEOs_Z!~ebXZua7nTCXnk(I)C;<=m_Qo*$>G^cgA zOvP<7x@u5Oabka3zlB)6XJW`M*UT)w_L=rh=mNU=rW*|v%Xd*XW7QoEsG($w7KfjS z@rsNLZilTSZZ}qxvDS*d&@7Ht2j8icj!0=VjAn3HB&{y$T7u8JAb7VCP0|?QFTq}; zLRQ8P3xhkCIBy6?OmVbQ8%D^ehw#f$Ue+VT*kb1;jtPd?HD2fSrA2 zVyB6EJ6-Wxd=j18oWz1A^*IhY7?Bi}p~eZSf{8Lyw7lvLNwFd4D*HU@e1m;59?F9F zEmK+Mgzf3$cdAmPH#IYrFAnvqZ~SAMb*RQDT$3Hg(us}NaKDTVSng*Q9H6<~{HGDz zUybcQjo|)dY>I|}$<4pHUy9bh49)AJwNkqj%PIu6cqgS9O}h`Qvl)bd9x1*KBRsrc zR=Z=;U@5MV=#3g96g@I%>WzH2#TX&P?7;da!+nx_ihauSI67Om^OIU&6`x8D{czz$ z(RH0Mqu9PgGQass)h_!shhQChDtV=9Tjhg`WxB!nWO$~uf9}o7@;BH@xTlYJpfd0d zQwL9uxWJP>Tth4#&mHE^VC_9IHQJlI(B?hU+GBuIRkqA}YN=Jgf`Gi$8*rK7!;GhK z5%OgtF2f9USZpLEHkZLD@|M(u&8eLSf?T%xzSdsd$Wh~jBld7C5egtumAy(3h&JjS z_LUU2P30}y&`L+Xh>_d#oX~Tu(J$xv+tO0SW&wEP$YSDS^Kx#hx6z zon|)3#h2>aW@2t-$I(G7-Y3l-hKZQQ$J&xuZn?<#R8X94Vk*$i z5wm1#yaQ{ZH|S*nBW9I+j~LW4H%sv0c}(nkW!cwT$n5+f+Jy7qjrGi0eI5-mheB$h zUhJVfd{v8(WNPeBVA>)YW<&YpWHOu*@L3e*b5apcs(TN049CFxhSZ|4;i57O*O6T2 z5yp;mJR&n(BF9HRE@orFGl6+>*HKEmxKD2EX05l;ewm!}x)dq~z~l)3(d6&}E#m(v zJs@Z6=p<#UZ}cxS3ykiO_{0w#^sclzW6_AKEOb4c?{6yWM@7@nR8fv5JNG$(VlK6g zQ}YZkO8MwPZZ9&egE~&|81L~yoUMC__o2#O$Sg9hg13_>6x4Hlz zxMWG+wadx;NKg{}sOC^R#cg^%AlkIFQNu4Z9sZf^@mQjuntMD{M z2h$u`XxD9iM%gXElANId@5j#Y4)H_PYp`#x|3Bd~MJ&!gj=_(6ROHC&4|tQXF#^CR zGB*&$B->me34pIq^`}})y%CKNuZ7z32Dft@fkeTrR=B>SbhI{+qtz@s-qXjgne~_6P z{P=Ul%LAx+03w$uY<6hm6F+hF?wvU@!jog8!YnyweEe|+!2BvCM_1M;iXEvg--)-m z;4NGB^5zQ-(`zH3qP!Ia+zX`!fH;BLfUJA(9e0O^uly<{StUD(v_1<$JcIDN8C}64 z9*US^!Ravjmr|OXd332}yCaG9nkzgaKzYp-W?@!!(Lk&xw^K6WTJ(MD_BYDj*>6 z$s1CWga88Hz`p{YT^5Ts{y-_x(JV z;aHY*gk$W|7JG8HShbH^w$H0ka^H7O7!Td8U8RG<+1 z@bRCa2Oqrv{mQv_wTINd0$<=idhUM&@6V3gB?H6{(AneHM?k4%?l+?*UsuyibrnH+ zjK8g^)tH1(uRGJecnE1v0|89jF}~2`v;7jiKSIxJt=I_D!U!)7+E}Z#i6yGk((4nd24z<$io*Ar8<;rn2~e+ z`$aZ#K9c<_?)@`*|Di~BFt^rsaFaB4ldv_n`IE*~?3e^Vc{S+$m}bMeQKuDNxaMeZ zYb9|Rxx8aWgkMW~*E}^&N`iKvJq-%4I~Y-xQZ3OhJ=MGO7RHqA_U-x!bPEXOBlzAa zOu0G|c?0!xQD4u|`nPkDF~s5T(b^kfQI~^RRWowtC=|L(m}8~P+NGix@HyeoTv!v7D6CnB zhx=LoE}!ohVRzO8ol6pU7zqk8%K{fj zziixXqnk$xVBaEu+VVfYn`!<-fcT$Z>Ax7l{|>a)b46ZD(cymNe#!{YS=^yTYSPvv z@-bOW`sA7{>2c8p|5Eps$Z)Mm_R%rX+3<8RF@5;0)k@+5jJck5x(>{j9I6E^dmC@rWp-*a81CV~RCuKsxI($-v%t+`@XUZp5HWwi!9+Fie zj^FVOdsa(LS38UqfBfiJ>R2~ZGt;0Ch3%HOpe>D%N=hCK>nsEL2c>I0%d6P@(QhCx%egkb8=U|O94=u5*wrF6`6=6))J}Y2eIgJB| zN)5>UMYLOe8@adViXQkey;q&@K*ArD99wa_-qku#=x3xc7wr% z>l8gPE_I=P(QnjvFCX8?hvKE{;#j1^tnnNkIN3K_Y*#nEz21O)u&t1eq)lBQpzE1b znrI$sb#hUO=oO3?qf8u5KkYWzAfP)Sw@yU=ut4$P^|`7vQOoF7=0dgDr@IK(7jnNF zFSJkoCM*4Ql-dMQOgkW0=^J<0yggdmi{Bu_9r;e4iIVdHS^V@*l?fI~;6a%q)wG3D z`H4W& zk3=Hk!p5<%Mx^s*FF0Yj6i3770J;?%_3TT{eMZUM6>Mdmjcw z*MJ*99{nWmS+j-GEt$em-a_JXSZoluLyq|6$f4u4)vuuEjx3 z(xO&)%OP0)U5?6oj4L05w+nSY)NF?I#6X8F%qjkHk1fn`S4}!N_%@h*vtdcX%pXl) z5X&MZT%0RouHk4#^M+1FOrVmTDtW)TUd?6=<8#z*O%jl>Tcb#%0L-bD9aR321Uh$3 zmi*bmU|XJuj_La>kz;veI38zeq#2pD)1~&^hW6z5PF~KLNb54Dd;xAUev#13MN85J zLo6-uW)J_aS)7)98mz^RgnJi_l@7b?e94n$<00$hIr|d48Qlr}%&{*W*_*2}0`uvY z7GR(Q`XdbZG1L~y{u)i8-h-gwpI52taimROfo-~x*|Jl4UgkiVK9C1*RmE`g1pw5f zVj$q6-I|yBcCQ46sqFARSI=m|Rp4g@D2En>1NI`kw(jq{lG|b4A?8Z@nFQEAStDLUVg#8AIP+&G zsP3tYdA;HrKCK~tWW8=6kxBRd{qP{6b1+^7#3gR%|1~zL{DtZK_xbm?mx!6${6XZ5 zk(KHJUzbkvU;M$~MZq4)1LFhM z=ys1^3k%KNwETrrKl0PtECs$8n22!hI=<3p*`b0DjU`=of|D-yo|Xp^V~gx)ciF3d zr>vP$<6Y321X~>Q>Px3UN@Lo!P!U63M8*t=UrijO2p3dJ4%pxwHr%#iLAx^h%*JRu z$$#KpZ0a4NPhL4yxUSB1%OwK=W==Dx=vAAD!>JQg0e-G8pN#RNau-+s6J&t%V~3S!eeWwHx$g!J_9M%72S@PS)E?DqLnocZg^_wS?oZv>Uh zt&MG+o&HJ|kF%7W2M9!O)_p0tDa7;2^gWQ&#sr0hUNHs^Zyw!>95=Ym6= z7$nUTqf5f*r|{My*kQb*MfNcLO`h6QRfBHUqpYm98=|`}o!zrXj(}4~E_9qLwF1^C zxx-U|;(JQi{Ei{rtlZKxe99^P2riq~CQpRm)Fm&n8an40Zg7w3p&9i%uOea{_6hA5*>~h0n^S+U^{!xRWo4lMKP8D{)B!Iwq@MK5QTiTg3gxq5I;GgiaSrtU>9a-i8zb*Bw3K(dF^_Gxy0!slbwUf!5ZCM?5dB$TwfSg zRYdldDLEkahV?DOjfd!+?W8~*=Ie5m4!n@xM`&BR(%!3@q37_9KzEIR`5M;fg4unczr^dW+1TL-( zH{%RjN_)h{B8&;0ZVyu`HgQBs5z;3Fd_flX)v}m3lrXkLM3XeeX04Z1DBdGD)gSgJ ztFfQ`k`PuD5&^YBv$C)MB=~u!hXaKu&^xAVfzpbpc?A#JXpMO2wZ)(`xTH-jm2_2e zKw)>!mUKZ)IAfThu?>U1<9l}JPZaR@7Mr7ICo08>>iFtHE=QqcO8E?@XrMWt>ohfRQk7{F>w)IdZtGpSCM#Bw@5r|cTt7d)$nEs|gFxKX-I_cMVQpM2 z{TL$iu{=ZQJ$hv5^gcrL``JiW$UWm&S>a-M&zxqr12GgH>q2|Kl0IL$$}7%R8S>@x zE;Vb5X>tvwU0f?`W@0U_`XoeTOA|=28Vo{!8T{ne#!l@qMHe1gQx8G5(qO_Qnnc8c zdf?;YMdCsq(R;m$dIt9v-mYfsha?h&3w+9c*HJoBRz`KMfP%I6994-m?U z#TObuE!{Cv5wzHIJ+4CI$EytH`2ZJ)F3Ok3jF_A{)%D1X8-024{`Tn#6zR)E4wJ&m~x20$a|c;4k64>cG&LEJq+-MVpZG=ox6 zOn+NJyL^~@HC$$rj@>@so!#XTutYQr8$+dk=ZtyY@tZUOpKV~TwZc|`9z!k?J>FU^Q2OOr)vx&B|V@JDp&`2@Z z7{9u|?$NI>6Ekxytd{3HdU?f=%*jIKIfCet@yY`*9c*&l6j~X5U57?-%!Vh=LJJQo zm(yNA;(JrWZX2RW>#r}C17nMrR7>owomi+O5y0vSCzh2g^%I|Ym>;NUsArAMHL#Ry zxmksda0tGud?)9bYg7#?vMgM8 zP&Pc8qhwW#9hC_b@TfLupO)A^uWK5J@hZhjB|BJs!CJNDm`FNTciCWL_I%DU(g=RI z9rV@K3(>IYtH+3IXS+<(;jVT&Hw+qiFFV$o;yTE6K4KnO3O#N*&s;JRtVl!rN^&QO zu7^HN_k&fVb9dqR?6`_UYoYpE(?+0$zms6<8Or|TTdZ{>Id|M+M~QI((xO(iyuw$Gm%pL9UmsgciiZcM|+$m%|X=C)u%OFf~`~DwFhN zV#I{QdHCBU_)vZU27cpEh>5@>W4a-MoGDBev9PF-8V&v+`KYIQ8ztg6dy!1knQxd- zc*JHt5C!I$ebnq4xkcgq0jW{dWZh*xU#FsQ=}ug!;E> z+tJP(u(tKbUddmL|L+@gzy3zhO5f2@$wA-7(F7oF{>LG%Ou648^cgU@x%&jPFF-)B z=#%`|kV*n&zzWUxehiX(l(j^w{QFC6fZ-x?Z>UdR7QA~}=K9Rp+4ImTmXG2YyvyN@ zdYD9-!W!4OI!NzG-0UFO(xR1La}_16v9rARCE4NvqZSP^E5D}T@+C3UOwH{I7)w~7 zZNpLWWd`#b1?4hA(01DxA4VLsWx%ul@oe3aRHcVe@_SzRRs+TD!T?=wy+tN0-Ol27 zc)|XBT5Fod5!2ni?q^13*AGRQS?u4iP3cAPUc2tI79sUn#HbpRJibr z75P*W5ELvidQ2`9;Ge976ZFupU7|&@f92klH$Jc@0LlY@N)ms?^zR9_zcBqZrd4$M zuX#`6AC!QNR_A?dx-HU>h@MsgC1Q^i0kscEhV`E0-3X#JKogODZYRR?PC zZ~~P!jgpC{iw#=;WwVQ}7KZ@;!7-6rW&hkO4enXlyC0*q$6u2U)1&k$H);z@JYVH* zoi{5Og<#q)Q=oUblxm%e%g@;Tar<6yw1PtNe>7ijs^czj&z$>y_mFJ$OC-am(39$B z6O`x}sd}v1cPhQ<<({`L`DVOuv1f%qMU?El{h3p=XnXFV;iWAC?ZqM7;gLtR@e$1H z(c6u)>B4QHi=_3qfs!B!0|Ek;Fc>@zk$8_7d{{E2R|`m;YlnVnn;{qk!5mr0sAf2q ze0GCy!mQX^EI&<-X2=TqE6&{d5+@7ZbK~@9o$Tl`+anjrFEqIHVG?F4OEa&&l9uwP zaDXws&G&-`4&$lmZ&=?a?HfOQ?;j3ZlR0Ry!sUOa-~#TWbOWh-K?siF&GYcR1G_j zlFU0l8vRHkQs9tKX+8ROA!0now(KiZ-8nK;HbAC035X0CB4fWBLiAHH^0S1FYY0gt z4u*lcrD!m@xoM1SHI@trg(G_Y>4+gL2_!1hjDxbI>R2?oo-t(xayu7^R_xdGVGx1t#x5m#I&Y4AfR|f-Q);XC&?7@`~ zIL_X4-vzJsQT2l!+w-tK1uOn$ozqH{K$n)UZrb$-y(K{#$|JdTi8V|WWwT{&_3skI zMmquq3*ZuH7(}i zUb@~$lc-)u(9zaCo&tV6Cnpc!I2l0pB9l{R2JmzN;me$Co4su}kSig=UcES>|K)MNBNNt!TM?f)PcvnvtPj@N|aZ+_-GNe4uCz z*h^SQl}n`^II!DkCzRUAFK4TFo>i`JM7^LMco@^Pv{0{EuVpGTX=5Hm(tk2_ND;{a zj|Bgi%e&OVD0AX4u`q)iupwdD#FWTB^eZpezP$OvUrr1hw8wvt2=1LD(89pIlQ`bT)I|3APZWNf1EZ1o3A<<~Rg56}GrN8&$z zAp`7atvqtd^~W;U!UEL7tRJ*pXO_NV9|n{uKr`2Biy_#d_N7)XuVCgWylt-t-8 z=qd=s3MGB}@a{twiuba$NuRZfdNAlWXr!Cve&OaOGc`Bo^>qJ6-Flztr-r>e3;IOq=Ncl ziO(ps3=tqQ;qI6-vAgPSu7(I53_?%13ni)?$#~2T4x8&ZttnJL=!}l-teazA9|^AI zAFCbvlJqX!{ug!+}hIwIuX_ zz5~yG9ETi+7$zhCs@{#V(u>k263%{0w;6fUT?ZhapHz*RI}VKn4J@U%t**Hh*rsby zmuW9dgk7NT;BuJ0Q!cclryaR>WeQ!5wb-U$V|-nPcybiRhu|NM3n1lVq%g{LM`niF zXV5BMbqo_dmMRZqIhf~0UT9%aYFrlCuGH-iO1W|5A&R2we@Nu;=%cwYDuG)d46XOE z4b6(PjZ|C$cg|IXf0#Woa*8Us%;{vukA9rI@o|k<9*+fjFr>*sSfZ z2muZU@oWajNgSa6Yajow@l@~sR~Pt~)pw}6dnn!mu8yxXuu9V*LnWYsP|-a{{S*)o zHnYy9rKzYi_5n)1V~H#hAJ6x3beue#XU_~Xq+4G*E1D#*7!^cdC7WzvINX3^{nnD~ z-qI4>8tFMDJxgj%3Bvm%I{ErK$=-F#IdwVVdEo8-LqBZ zjAeuhZ048FjZwKcw9+%EL7 z*%4~{d%zd0viR`u(#Y80ZysCH_Cf>uOz>CM=~@hKgiEJPF-#ziXv`hy;Xi*9Nq-d~ z-hstb$q?Ra^Vg}a_*^wW6NsU;F9yY(LKrs=e|LvDCu+-v4n>i0hZXyvw`_Ht{^>3P zF){;|qnqVwBHxgwFQHHvs18m-s#9!?kGwVV_*QtL1_5Tap0PAz4$X;W;OS|+=KHES z=~{LgVnL%Y2P=G-a9iH^h>w6Utj(xQD9J6>$Lcu=xM%wCIV+(&b5*G+#Nr$E)PYNI z5zRvD*{Gt@hR4^Ur7JqqbD~&#C7{?6gpeob*n(0i3}yAkN5VOz2|Wf57YeA<35&4D zm!y32(bA$B8kOz{j;Twh;yU#3rsKpPRpe!H2Eei|D) zWf35qHGM{ZTizA9-fG?}56{_* z9^_{$kDJ*SwUpA4)vsYe^~}_ZTeCGng84!9pQ#t@qJoSulPt|dJ`fCcd1;N6UGgmB57v^gAJz{=ku9&&Pg|TFxyjTP83dmg1J+qr%IZ@2rl+Ozf4d_HgOs^UO>olHzrGIabmT* zKKU8)cp3%dx^1qS@qFrbT^ea=%VDUGOj5W#tGHps74F@CL1D+bCa-88P#XyLU3*<& zn-uFR4B79*P+r*WmU2^;@%4C@!a*UCmug0#P-OQp)d-@#_Ljk~vY=MwsbpLb%^`}< z{HaRzAbm)|@HKNMWDe52P$Jma=}@QH=gL8k#<)vl#(TJAF~@_utD?mS+P!yxZA ziTV>e*W_aLNN#rO&n;BL`pt$6dzDDV`;&}aVkGTE_O_P$ewXu~QHQQn%-HpRj$DJt-Mla#Gr*z)8248`9#cB=Rv8y2KK0MhFh@XaoPYt0|C$ zbb|xdv{fwNb>7Y5G36_HjRdw4j)v`u=S8=N0#?<13+n;jWxpk%t{s{j@FG8)jol2% zi*%z2Ry7!n(=4K7BNlTB`RE_x&32{VPQ7OfyB2T>^_chxuV*u`{iJ*1XT3ixH{G5w zfj7d9Dp=EQvVaca&6I&wI39$X{P1O%J&qUt9@@Z9crU==w1H+FrK+LumrgU-htXn5 zNBZlf=Ao;h=4_@|Tfq(*c>2D@oZ%tKP>*G&eo&V5ZJQpgM^&6V^fz3hvv|)q^`wzC zR0#-F>{>~zf;KnXgJ47XpnB5l3HJ~JH1t%?YNT%?EuO2svzMk_%9qs8Q#j%YJ-yHgtW7`h&+f}esNx}X#5irZ^E{Otr8fAGU;%IkfK#0az%zJpc22m1V74pzq=>~x&?_|a|%+;m!U z{KAwMMvgFExf?>({u4aypySmpN|mm#8VX^a1ibONVHv`z5*2Oz(-x&v-a27cYcft# zswW3n_TGs`l}ul8OKWzy;x$E8)~*GMqk@<%Zq6%RZStu-Jd{1FRYTDn@69~)SF3CK z@Q%|V(}MT;QKM>e3-(>WPNYp4hyS%ll;;wk7e*ULZdd#h#P?(hNvV;gN1`lJcdwYH z!s64E!zDO@Y6_Z_N(sE4)4FQu#pZ-_OH-J$3L7mPRt~sAE}B@&@eO1VjquY7EhSPb z($|Cqjy1yxah!T@GVWod7n$+Ux;crD#_3A#))aKZmUw>gj`p`J^RHH5?v;1yxM+C> zbjlE4PJ$OAhT@6ld$Q7rUX>;Xw9JdkwVTo)v7DpuI9?Q9WxnE<tW8qd45>5MMWk9Ag0!jE-dAjbt{D$BB`Bx&i%oS8p&PRrMsL$n!KI7rtS3}R z;$|mr)qBty%2Yf>Dv=Yo>j@2NkWk%GdwbN9)H~u`QW1IwpXfn`r6$vij}=;;2HU%$ zzsE9!?RHsegUIT~jz>-?*Ff10TeOzT6*->3V(Wf`TU}Zlw!!=y!dd^LMB-y~pX?%U zpz*!^i!FG#b`wFyMO~rkM$$N!qPBj#RvRO29s&rL;R__``==5}6{LuZw%T zmwwKEVp&NuPMj5ig2>X2<5n2OQE^Lbj02L`zR0=98BsVPs>>uIdXT=M6xE?Jbs*z# z@vUo4}0KWmRNI5YNIHFZ%yisaViDt@k>rh_i`vTRXooNs`T6s75 z_WSODa5PDm6V!(f4cPzm0^t6?e*yffyw6nBmPO)6=I#^y(nc#sDJM_u*PlSzN&p-h z3opH3rjRQym}k3sUI>lxWn{eR8t)PEi7zj-niQ}@m01XW{n6-4e2eMDhU3N1_}kO# zIhYU18VY(9%_Vp^vpC^;RAbFw=%^?BOk-)a1|;wOUd3R7olzyc>t?8JlHAv@gdeT{RZ8sbVw3 z&E@3M2EMUQvY24`u#m9WfYf4fI~vGyFrt;|Jd5kNWHHv_z*DiG^F6#F)!+4N4E5G7 zCaEX1;IeURi2b-Ly5N8y1gmRFWPu=1_`9RsTR9u)x*TBt3YavFv8Zxmh~@Z;zGkgN zOR7-r3PfYWv+wOjVIWTp45X`Vd{)#^pi%%xm1D#^`NIk*dL;b;?a$2Jb&DXMnI{Gt zTn6-Bu4-J(EgrCMcEPdUi2Y6DO~Y@epL|=+pvJs<&h(RpZ=h1YWD;>p!4T@2n)^z; zl96`h-|1lQE~<`u1u2?3Cbf^>bR+3xGur5ZPEDc6>&veOkIfWoQ`a_lzBty2z9S>q zy%;1Jeq5j)gC}+6AaC*T+iixq{LNs}P;x7q0<=!N{=fG#f8CQ4w=uE(m#sJ}YTM4y zBX?{Jj)G!Yf8!q-0`-@!j0tfS#M}W10G1eQXh7LHk8@2eo`;R%9M#?R#p{k1&}yTw zo{dS)cfYuPWpnX*e|&`cpwkQCzuF4*C~>!P0qcO)}ze>5pUlQ#)y!*J+lg2 zA7NS$(PHmgOyLPL?*TR)z|Osti)VT*>)zyD2m#5u zq4JN`#qoaK9JfT^OL#= zxJa@|3W^viZ;Aqy3q@dq@r?Jr+D?Q>{jar7Pn=kq6Tkt+!%p|jj>D{HosB77UGERe zLc)IJ{08v1>#Zy17SIB@%f>MlL4b?CT@&z?(rCRlZIkeuwicC>UiptA)}K!(`{tHT za=i+?OCqF}>bW+ZpzC)cD@MyxHwl88$~m)aelAX%dt;|62ZiXv;lz`>c@8XQdKDf= zfI&we5*UGVKKWq1ckIuIiMDdHXkME6>FtMxgsQ>k(i1PJOHO>DDVBA}8s{8>y1)6* z_!!a@YSALs&bN4ilNKbqo!GnSr+ef6RfT-g^QG+_pJr0B$vL({|1?YW74P{hrwmd^ zOBLT99$pcDH^`6NhEi-x1)9Q$ZSBBLbP#dTh`4rgyGp9ox%GVTibngZKqI!JE!R=1 zjppTd(yray+*R%M#%5lagDTvD@q31T=JLysA! z6gu#&N;Ui?8O7U}N&cQ347~kh~X%8shM z=7`CVt2$U;Pa%Jv-!2wA>_msUN+cRI{cTz~L+y}~RfY5>9#Jw=TWS2OwbD+{y&Q=Y zXwNrq4rs4uYrko+KE>Xb;Oc+?k#eW^r9v6@#o-eOuar#Zex^jb$gNyZ%_vE`=?`J* zZc{t%AcAJ79YF8DJ=8y9pQ{V-FRT8keg0Pl>)&6#RJ6AObie_vaYbV%X?;68a~sot z#s0w9O$i`*0PiM#CFympj*&2oa3(#esx6EWyuqjuznz0N9-G~L(Pc`aQv;2g4-RDl z(=29YA~p5$MbD)3@u!D7m|awz&o1|xvrW3Quhn7cK+m;cCNL?g8lhpLa=*DgzUb`2 z?A`kFhK~o89b0?>LvEr<;9y5a5l}J3^$9FRn6lN#&%Gwy22uMw>vStc*x=E-mfF~l z5PKpDWPA9BdNaO6c55lCc~ul!BOJQ~+Thp|!CZC6{|{wf0hQIZwM}TMS(%_ z8S*q-)7dZr;W#uG)0d>}N5!UZLFUWh!)7P<(^+o-6b*1pmb*% zj1}}JrLf`l6y8?6wh%orIQz;F;oYu~NkBVUe?@Df@l0KW5RAtAn{=vN)bn1(C^}WC z@}(RY9P0;^I{%!}jwqYhyKIbWuQqrA^-W8jY8X5ey{84V*!*}*6c`$1z;A!X5d3|3 zejh{do8kEndt_2-KptVZ=Y#Fsjg=?bv$sMD$e2L1wloSGfenW7>lV~^cy_f-J_JX4 z=W9f3MC7G}pGxx6wz9T_1QRD~uaH3&*om({%m_L1;i7bInb6koDZyjlnlzj^aBj{% zlU1cMu{lbfX+yO0BPTHrZ>d{*Go50T<%r#r8D6>6n!X~A5z?$7vPYX-{&Bb+c~UIQ zp3T~Zmx0^tAkn1n6+pJ7G{l*s6bR&tcd&JCM+Z_GODkv8C*5w}yfwbAb%EfGw%q64 zHPhKIU!RjOp3}`Io6Vcnl~MkN)7!-@54j z%@`dGO&RR%puJ}1v$RGn>lR_%iJ&L?+3OdP0;n&!m{-5QX}FvCS6qfNB(c7mXUoy0 zj8d;7(GtG39PWOYP{4+`Z1JWPkMj`}=~;AdpLC*Tm?$oWgqGr7sKnNX6iFUhZx4?|qzc$7qm);zL(7e{0p!*W zO%p0?q56H9SgE@qVDDB1^WqSw^>*o9*^eBWN57`?lyPlSfAN^uo56En>BBSoH%OM${`R) z=~BJe2N9X#S&Ccoq7&HO+d{FBwX`t;qGWk4a|(I|ExAyXl|(tnFY9MHaN*6)1Vc45 z1>EgDhw9N9*#rP4u;_3w(L-{euKU{-`w@#$rr{Sm42iEZ`QEH6b7mQ$kfP!1;pN(C zg+=kQ@We%0tfSU+b|xOAE924cM^bdIcY8 z(^p3q+kjFPhs<{Xm!+d9wK@; zF+f$}^+RmWVD<+xli(Omw;P`-snr7EQ>m_`x3$J(hAg_Sq!7cSEa1)ER%X5+F2lqHD~`0#ik;&_K`D=hDlk*&5*6U% zEkM~8n5m?A32CgiFd6N%=~F(eJ$ZIJ&Zr?_mZQ7CYJD#hmjW3q({aE6I@9^a7*DoC z*$IjOG~=$MvB1dLeXpm1+tJf@5#04QHTs5BPFQ*{Iotv?;`Zx?=4w+mn@uO@$7S97 zn(}vKqo5IrmIl(UOPofgdDBi`@?ON=B<#%g`{B>b^o+nKncQu@AzeP{JnNZnvuUW} z?+1reKw=6*F@!^>r9`POBaRJIC{9?f>o-UdQWZtDVp1G&P*0;uE4t(O(p_McS+0fT z#kkj)UWrGSb*iyEuu5jn z`tc~Df#9i2n(2vPl+OMlDeG%8l(W%Nxj=)wEz;F+$gst!%mNa&CR6wDPFL8=4YvVU zzfp2&5+U1$k)#Zeo9(RQ80qoHAB1mYolpZBU|q-mia@lt z1rQtjH`Y{&eBTpwIlIZC$f8lRTJ5XI5_c+UyWtN3cV1Ff7=q$*}gx z`!p{2-01c%=3{Q_;}B7lG6}V>ORWz;7H*Vx%|*29Ivm*y9Hl96m<`T?O&L=@UChbZ zC8seNw0vS&=`mbjmTL;%4wn9yxWrh5+mU%5tTI0>?dfvFGj$K93WpGUyL zECsMf0GM1d}mWr8g4wdJ{oJy~yM_l#OZr zUgTPSxVfhaQlU(1>q-*m?FztKGd zFHk~$OM4HH7VePGvH{@$$j%yQw4BLSlt(6d^18KGn-jBVM=Wyeo=-{v-SyJuHk0j@ zxE+R@Ai+Y)wE^FbxrZ~p6DZJ~_PeoQ5Bmk#D^_E_UR?gMgwp9I)jg?TB>r&;{f9%N zi~avFcYbEWXB36nxtK*2g|a2y&rHh9%2ZPXQryRwCD{QqiK!>8 z*u@uQ{Y&pP(ZrdU*w?1Ef(A6pv=U^Xu zf4H<%f0@#B1-kJ&g0T;GlLFF+dY)s;u-~q&rob0}g zeUAw85|Q4{F;mYqoDyYpb(1z5>V+I{_5BxMuuw;=OJs} z^bC1<0ie?7eQ!B>y$S2sX~18ln;Nc#jKPCxJl9X9{9*B2hC&Ytz}m;ZcJRY+2ezTPu8EIsi$QuenXs+AV3U1GCL_TF12ta_fArV-oQ8> z99Crys~8i*3^T2Ghy3-Iuw=}w;`W{5ozfj3bdsTgxsHL!d{J2aCjX$hc>vwe7-fD+ z7}fQ?1T8&Oq@-cwl6jc0`ZDOYu>4k_R3y{+bU^=qY{sAF1i71rN&dRsWCw&JaD;vq zeBao}Sl>wB1O)U&z7-(ki|y|VeTvN<;GOTwkOeJ$CTOzwBmT`^<`expz#Cb9@y5SX z-Jkz{Mtj(s82^Ru-j%9hH^YxIY$g@8{|r@<1b>@S%z@pKL>DFYIklv4v2w{^HFfUd ziztT!_CDRBZ#iPT&yc-Ac+m|z_iagB(Pr4W$6YcOF4x^AonMyEl({s)B{^+=k%NUs@X(xy7-5P>z;{7EQYW2%At&>neu=Gvoec;IG_EFWz~ z+nx#CN4Ae!De&!AH>!6mpO$AGp?r=LQfoFie|gIK?&BV;omn+i(!Kk}joGKO`$tsq z)Ju*DN>lCiqcyk&90;#N0b}^(a;MeF*c$} zBsYNg_Eo~}TIvRTU>XJ=y0+qXar>S}p0`(!eDYQB^wizYubVf>``pl1MR=jrF!Y3S z0}djwPOpTytMN79dVV+}7pd1A2QWeB0B*$3R~52owrzE9Swex z$`k%4$%RtUW#ydc32|iDwC+=u3nF6kl*Y^H8MbJHF;k_al|sLXAiiEAa#?Qdgut{F zx6dBw2By5ctDix$pQHHu6U9ISymFd=OZFLhR`3i(lBvwm5QHWc33t961#3to8Y$c) z4GR_1{&ONKdu`O0>)B_57^%|I>x*S(Vpthe*7PasOu6ewU@;-HvEJ^yxV)+mv9@XR zapCYOU!?PaJtp1)qld}84j+G%n`G=pw)Qa!dr3PZ_-p<`9_{(^TzuLESM% z4w~!xas85^wM&1WC>2arna>Z4V|hb~-~-cs_Lr&u_gM7bKPsylSX%&juYfn{e_dZ13~q(c@NfN4ZWSh@!>>-~3L(xq(~Lj1iiT1tgPCx< zE|8@d(_YF``$|`#c+Qp;a_h7C@KD5Q!C7uw?)D9EoG&q2oKINS6572y30Hv>mhswS z0Bz`OHpG+dnRXTh2eo>YU_m;JZNCW|=uy247fI)RrIr9L#B&lu(ZMSJV%Is{E1j|Z znfZ^)dtMbX@RcjlOQf2bbh@O?Uc=)TRGnoHR9obHA3HTW=Nx$Mq=GhV!aEis5P<{z zmmW?XL@gEK_1Hjm=nNqUSV*{3jBs^@I)5(*WBa@Ta3m}Id6VM!lmyY(fl%ps{1qkA z)+UY#M|CTfJ;tlvy^qnW@26M}W7jo5!$6<+eyyy^?W1c@djYiP9RMearq9R?lx$nx?7owbYU&LtzK9 z#*OBy_EFQTHYH>~#e9PB6is=B%;}^t@t7ba-NxDG=Mg>;;)PW9%w-c%bQ7aD&RzLl z0kS;FD6gU4ieC|Jk+;0iVs=+SE^SXwH0#Mx_GWlpPc<<(eN?Zww#Ql<8SOcg*gJZx z)Iw4__5z96K3pELvH2csmz%5V{bJIwIZCV_E_5))I=a^nCOB5Vtn?{ho7i7wFR8yL z7x3kO$`jGdzz|3gL2vGCW4*1l?6@J0>XRFh?pJ0)>Us!Edo!Ke-S#nFz_t<`JSg9W zPX@gV=iTNZP3lcqBz5 zl`P%Kx8uz(BlT9(AuPTqTsP6JJ{_AhW0@P^hMAGWBg60Vd(`cwgoZy_Uu_#=hZ5)aMQS zEL950X5)m=zS)t@M4uasHmBIRnYWFw&AzmWC0vbw5z6w-9lr0IN{J+3W$Q$6Ti5tx z9>K`PId1CW>g|60Fdn_1V#Gm%z?C`x#oYJ4w?1euzZY&jNutaaLfA`z&=$iQ z8w@KidS*$L0maVVghRGs!&rH7#5Up1d>&?z-N(45ZXKdu(DAH{um0KKEuqZ^W8LE)0~u zpq?pzwD>tK zw@L2UmMu-UKY$(L@I}O~uXx3=PrdE?tIHxzCy_TN0`~B)kEj=p+p3ScFSMjaO%@CX z6*<(0Olw_yzaHU9gQXIC2ABr9eaO<63q=cTl7Onwy!FpWJU>8)4-Hfz8Bt53gefeE zE^Oqx@3pu1;5iZmCF_ZK9doI&wtAOhBA+>Q2%kArETX2R7bcCUpF@d8MW4)!_Tr{{ zG<2j-n`qp%t|`NnNvenP&c^rr=}5-btdZvKdX>5acN1Abk971wh{c~^E;{=NR?0*285 z%0=L!5JOT4auV^@5PlFJD#{XPNL#tcYtCc$)^VaDe8(_~t@cr2Cb8uEVAVjT+_;L; z*0Y90j4p5VYok(``$%|5+J3NNmL@No5^@d%vM+90ys4QAvb>9w>w2!0_jYyf!4Nlw z4Ucs3TwdPL2-SBmIT4p~Gl<~h)5anpYP?yD8diKzJz7(7(LDHaI1TxDj?Ss18apnV~TSYBW++2+(Yz=Vy^G_o1WOhV?~J={+j zB+Y=St^*T{%K_v+XQqTnBFj`esS|7(#NBuk+>#Lg@$F23n6aS_$;caQ)VU%kSwB{4 zNtR&eg!AvC!eo>xV3)9xyZFIFjOLU9Id$A66(JbXsjsk)B<{agP^de-7`k$7K!h->)yK&4P)RB{(A%MPWa#IM!MsUcXjUFo-WVNvpJOTLQOQqU&TfzY^en@L=kj zDIh*7n_m>1std(uxEtf(5@_y`T0B)X;8#hvxm<1;w`8+fxHmnhZGRQVH6dnOHd~i? ziU7jYd&jOT=tjF&c*V-$Mq6Lu~n7N9>soO6UFO zl}{Ac@u7B&f|?vIV;}0U*W0#%%I#L4aHmb!p;$HO6P)vzX9?QIt&|d{&autBV zmsgB`mE4fTBOM};E0Zi2!&V@8>u5~k?WY)KPU~CbhTiDtT%pix5fh-lMLww&nNWQ3 zMwWWxvCzQr14a@{zy-yFGZZ%d@uuvDb%XLv)*B+^XAp>;b2iv-LWYr! z*V|#`DkQ2_?Chy8wH3t5KtmtL8xb)MI_XED^1$AUJTS%iLy;2EjzAZ|_#^N7hCb+B zx{V}M%oYnjJ1J=!;YI%;2M9d%u1@8hgz z-e$#xfd!uh*4||zl~t;1upF2@v8i(ordsNFA;{77Fs+hG|;jMlph+>VA7x3LD6fY%e_% zX=7Fzi2b5iYS_f~%Fy2C(gtdw%lE2s=8e|j_f$lBoB6|Uws-G$$3Qy{o3&h5O;le) zx^V?{JMrQ>uZJEY)2Z!mw0?n#Zs2Sg@7})*XsEZ%<525r27hDlhPO_7*!7?;Fzc(q z*uHlSbNn#p3aAyA+Ix`DaAje@>M&%{)%8L(FSie)d%}hUo`5n z>Y>*HX<1co^Z@a$tH!FRi*DGanQr%;tpLSzKwHNB+z~>$mqpXE-1xDs+aWKmLcLBT zcgu3D%jf3pWPPWx{pATz+?ICBKrI1q|tGZ2p(3W z6J`O0S>f;4>ve)%GZ|JR0CBQjirid6vfT!zWkX<($$Oxem%NOAcRsDvw*Q zKP4#XX)zq5@bK4zPc({mDu8+BY>a2an>KZGnuBorzXHLkDT;?UZXMVufeWx*A4k>j zdUsjom1r%;rZ{+Yl1Ntut6Ahlu3A?fvul7!ri`PaRJxC66aLl4uSC;n?xnYb9>-fb z)*RZ_{ul2gs3NanozJLB^;5tXZds|m#c7kBAyA4jK_lYd6?N5On|}1xS6&^N!gFgBtV;#w{FNR#+-)(`%E2~qTm32fAx%D25ejSVL7**DH& zUWx*Ga3>v`$fN^k@bYebG4jUFd-iaPBIQ-#NC7Q}G!~hk9(0p95#2LozdNg95X^jT z`Dop=G$pf7XYxH_Wv*a}aX!H#@_d?bnpu7Lnc7O07Uyo1U_MsQ7x_DPu=?ltb+}XA zOkHGN%S)e;X->9HEX_U#NR4}3oR}ACAP)_2Gk~ZPLOhJHbFyK&6MJ5NJ6Y-bu)j`H zxCYBpF7A#^kZ;-%hJ);86sR15GJ@oamuuf6wh`g#3&)*;);pAwS53UsxT{nfDkW8+ zrbZ*U5S40x;t-Q>=yg&;oT$-@PyT@kqf(%>6_4O#v6giH2B-0-nmum6WYy993OH)JPqgkXJ6VcS}UY3N+MpyF(!{2I=nQS0^1Q@8ad_< zmouM2-T;{4Tz2$;E%8K);QNSMASz@D4*e?`7SCx!+m7>v66WAJN1+5xdFa2E z!V9yRT1H=52ARDYpRIpMB0~gPB(%lIV48NdOdXCZT9m7gU~uW|!aHRjG#pPvevs;h zbTs~)-aHPKYv42W&di+PCq!s&#vn-5%)<78*QWJ*9L;1UFTLjrYd34+;-l0D0!wR~ z;&CXkDZV)A-^ZN6flQGb#`sgpez&LzHI9!NThOgYkyEXGT|W}dq;Bh zQ-(%j*j2@Z6_-rM=2GaqVD0|rmX{VMqowbAByU+jPOMCQ(i867i{ColQW}|M*=@Xf zJbQ!yVGoXa{PC;D4jPBwJ~9Xh^)He;e}6%M0K6cuwlFfWbu#(!ilACcBWamE(RaAE z*PV2z1V-cNi04H=y;^Wpp-!}l1Z&|t+uUQ^V>T27UT_nEb z>Go;(EOEqsE#GAFBA)5xpjvsGJ0>+&mUcf0ha645GMjcv=SQlEE04PixQA9xM&{K{ zY?*7L45@05xkFY&=JNTSMAaCl;9UI=Tf zUZITj(G0@9@lPqPzzw8bLPBP=kFxPVma8&$Mv{p=alFovA*Z?dxW5u zyu1zOf*;gMnMjhBosxTU8wFZJ3*5~=Sj zbI-1`zS=F+A#y;d>epodKr4A+>Pc8xyJtNQ%w=y8%WP&~RKT!^-FK5ZN zOBdH)6j$Wwe#(B#_Wp1^W5plgq<+c_HX0WcvQ|R5#TAJY919QnEt1bjnz)b{8A=64 z_2@Zo&P3`J^32;b_tONvs#GGi881SL>q)s&8|N`1tdqP)lI`3o~y1P3Fy6_{n zulVzalWSkXN%rqAHGA{a{JwWpXSU45RZ_OcVOpJ2=p*6x5ha;ksB7o9Py!) zt!$+0HH82fM|OrT@&})Jttshk8GWp6_XV#R=d`a)G2g8jkIv8TgHcz5m+M@336}yd z=xo5`<^kpw!uAoPyR0$2dq2ghs~iC_5_wUXy>P&YRqu?{@mYncTt#p^D|m9%OQ2tk!zNZcUwS&;c(!Ex7`bOXP~ zc~nU+7B#V^dBPx{oQ1b{+0I5{v6nO4#kqs5%2Jo$9g?ZYnxuUV4|Yb#t666DjRxbe ziif3w(h`m~Yyy?9(9i<=glBZ>&oGhwdDbk1sN=6g$zbyO#)OU`_W+Qy@N<{{T2Kr zSUf54gS1ssYPVkL%LlO-AsuCyK~vHXb=Jf_w~MW9=Esa~-~+=y%ynoE~Wz)*iU+~+d>@_)wi5i?+QVtz{=o-l2 z{i5QUSif>@4E5GN{JR&=O+Qe?`x`s91?F+R#Gv~|^l+U363Oxu3S|@b zOO}luv}^@5e!5V%;AZzPic9fLuzJeg6knnp=Ljp6%G_AS2+;Pdw}`urOGzj9_qbO4 zMVm%3*GxMqe3o)N6$kWE?Ere(+{bB8h;48#fY$ z-$)6cH=Cwjfn)o_J8)5ynB~`OtQ8dwg4eS*qWKRWZfIv99Xb%Njbr7GuB-0kgWteo z?{iTb3;8CwRIr#L%m(Xd!%wv3&+3qnNL!79VU?ex=WCLS`EL%tA{_ zuC4*jl{Q&1kb{6wx@;hARk(g~8~Z>YpjX|j*^e+7e9#OLqp?D!8m3->fZKPyh!?Uh z-(PjM!7Z+P7F-})Te3LDuY>dz>LKNwWUs|VG3Ptc!$CQQ4hb9m)a;age?PiPA$sN$ zIejY%6vk$)ltcubRA)k1e~v0oPZr2PV^R390f=R>bTIUG`D*@^lVddV>4)#wwPnb7 z=!i4?WY(24Iw~0#Urn@o?4feQ@@;33BOMF2zKW^jSG=O#+lWVL4F`L9DA_UA0HOE3 zMc}wmK=bzEA|Ey>{$6qXrgW)}5y8Qv9kxGQ07H}rq`8nT*`i5(5(8rXl{3UZ^>YEH zp0e}L!c#DB`#i|Eb*1lk#iM;3S4$K?u=&vz)w7>lYRx5&p!O%9;Bj)4UG9$9H9&a9mG{V1-q_v9-eD`M{2(O<6S%)1E zgjSv;dG#;|$BLdz^AMh{&P7)_^`V!dqTQ;6EdDbnt)O$}M=S#(bRmCHuqEC?)$lhE zQ01_}%v3xMeQ+7&&1)8wSrrTf8c`o%ISx2=>Ua^+J9EDUP|?EsxD&6a^ElKd%V=*p$ivJ}pC4OvBNVH| zTgP?~fR!t+-aeJzcp$9eu^yZhEYO)aQ~CA71SbTB()ffT!)2apzd#6GcEIQ`X9tnY z(Cm1YunW}(bc-YNtykP9jl~ztBYx7?)*gOD)uyM;gwjw0#!QuP5Tuc7zTWt;iu`j3 znl^3FvtiSqI_%=}I+(q3Hdf?)c)coCAi;=@BEin(~`7S@>0D6{Boz}x`k z)+0$%p(rEUFhJ+oUZj)-msz)v3~H1c6=a)2RZ17u=G~88 ze%vd%CJbb5b}yk^I&ZS~YgDi&Hyk`fJt^mu_GWSiB(tD{EihqJ1hQCcxfX=4wyAUnKwkXpSO3f8wOuQ^3Z=rscorQ;M;Ac{9K@FI%wS_Go8Kxe~!dTLK0FZQ%y-Q ztHRstf&do;dieD-{o-Ukd`Nid^PnZaX~GexZJycC-0) z%r3?4vT{0dFgE-H+QLh}Lq(hSgt~G<9k_HBu-2WPhM(9X#WVIi47>Dgk8s=Yl}8TJ zRnWMk$Gkqh!r$V*F{H>X2`i2*BD7fqcqrRL~DqwucZ%6mW+Hg*_ZllzVBDLayC+5?hH+uBUX1R_73>>=&rJxfFq<0F3UacZ2EX6^r=F3@ zL&6(gVpW@$+_*uUn6(>k!!V{84H>J3kJhU9@7xJ%d)BguHZH;BpjA$&{L*XYPY)e< zbxy;E=LVP}4jU_pDkzz+N8_rTK7P8O&KShhPO&8ei$`YHp~?O@rQ=U$N9C8vg}e?@ zG61E?yabEp8XfP{Q}}w1T4k$1=kpt8I!%a=>*A}pb=_-MTvqJy(+V|ka~F7`UN8mq zY(_m@+hX&qgTv_=0YW{8eZi%Qmg02~_85?t8muWe;^b&C*j$>Ks@5oEcilR6Qp=0! zt+nH=RW-|9Qo}E}u!*k-9(rD=t4;3AV2?+d<_*u+JQkn7tTJ--@;oWV;2>ztDBn!0 z45o0KxyfFY;S;&gapOcay&kneK=&tP_qwXZ34V6H>OGzm2IB<|6QoCKf4Bc-Y|+yZ z_5S7=Vd0R0C*M6b`C&yAj4v*VcenIBcG23{sQLJDYF?3C@1yl?IzNL97MpF4zWPfw zz4Tfdn5B;JK}eZ+Z>25tyOfVhehBX1^$VS5A#{at=hsG!=_1!(QOmt!lgtNSSF#J} zZid9Dz%&gvb)bQ2=d{uHd5`xEtjWnEvdCYz_8~S5Fmv(5BfO8=Q-!8HRcc&g9%AD_Mdx?ICE)RJak!dgobpRah@ zYHmQjV7;@qQ&61X1mofT~tpsVs4Kmy@goX(G`43?$sCh=m^== zT%Rz?sm>o2+lL^f;bk_lkIdr&-{reQZ>%4F zYtX83?shyi^imHZn`Tx~OWTE(Odg$)&f&Q7$98z|%LdPHMy&lD5bxbEnvmp$7M};c z2oul=3YzFjH0y({L@~)yvGmBJ38ABpZCv-!D1|EYj{o{PaylQD%~mC2rh2IGowS*m zSky9HhkWw1bhI@5Vu$0jxP1(Qm7PWh4TY(3gbAl^^3)o`S#REij`mkl8M?2H-VptW z-^5)_We+YQAEY`=+f#(tLBo}hf}K7c2AhJCk^yNhYeV%~MQm;b-E87=rh2ZQ>(tF@ z@}#hhBCrKSCmZk9m|cs)J3^>RI;=F+>FXTsPs<>92|AnMj9){14h4;sg^0B6gDJDS zBYe4RxM^*%lImd!+q_Ros^W_x*7*(dli18U2D(_T^?rj%;rM4VhO4tff|BS^x}&p! z#f{jL5^nM^Mk8+c7M%f{;Bh*sb)?hm`~i(uFw3h(SLz^hens{5Q?{E>>w*WEpAuWO z3rR9yoY1n~L@p{%g1&RXYIyf!qM{uHugYH? zAcAM;PXiv{blNy-@sD*L)D?wN(@n71Cma1grNRXwGc%C;Azm{EC}o`u6LsVsLqxr+ zI+yA%Dnf6%)OLEcpi}t@9YR@YTC|qqk$4K;+{pS;CgD*l>MhZ>I~O-IDdJW|Ew5+E zQsu@~yXKldc~9HJz#~81DfG(GsjiChVb&%%fB3`L1kqT`=bn&b@??bNo=@6pcKO3W`$7s!`5RcW%ywT% zkO1}N7dA~PiO!U%o%}s*#OpWkw8sn3?0tc}LW?lu(dz>u>*>rLVbjjr!+am06enXl zBKh4K{ev2;dmVC_@bSLIZujJ(LG$;?c({G|#0FPG%zIUYmggZ?5u)7e8oyPxA}37m zp{j9-Y}G<&vTq;Ikzkru?%;=bb9I2?O>Ce8-c&eN~{xgdkKgxq#e~o z6YhOEORmT=4(a<#D|_r?H#~>_r0uZF-F<2}sF-65^**z)u$QwJPOdH4La+0t*h$j| zI`aj#;BUdgAk20m?(6IiConm{;k@e`8_?ZtyI6`QXRho$8L|mC0@knNbKWC!NX}1` z+fuNjY0z5e);E(D%TI0ZLdG_zW#0{=#aM%rbO7;^7;OdEQTltmxlgZ2}2RKpagBjDeh*Pr*+)D$BOZq9{JE*8I{@C@sM^L>#~L z!%6UCaV8YuS(i$HfcJ&sM^vS0o4Ao{-rv5bFHsiwF25zRMZfF$K3n11vJ<-`FAcU+ z%1lY!7h9Hs)Lwh_(Vg||-htDWuk#z2IC-T?xs(F)(cS2$h{?%*wHo-yuMA-+=or@I zarA+<_C9UY9N&EWX8rJ^thEfPG3q$Opl92w7QQ%~lf7*}Aq4W45%S223|;#XDOIrv zGPQ+CR4dGvt*3%j48OhbabxgK9yx>S&@G@rs--LW=VQPT*(P_k7qRivz+wtZ32tzP{O0XeQn?VPU*m&Azz zX_Y+40lzfKIdf2AXe>sswPicZ*GuZ{WTG=jsU=pA&6o}g>a1(^igS;^$db^Ox%Ygx z#X6!fWD=aqY_bbI?zXF!-$nLuLM$OsImB@|-)vRCxkQ=VSsrc9#~{d)C3vt}Hz7W? zg)Y9F`CS_(xg+TPjR(jUqg{Q+zKK2~k}($zW;$bhS&H*32z3$%431pC-nACpc-q$& z%kY7fFISGzZ27~q95?jF4=Go_pD9|z>AO+-3lnCL*uQqh3G0STg~O#E%A0WFzJX_Y zpBzcZz-)w@zD!QpwTsPRaYp3=Ju|xOLBzzOv<D^X$Syq) zM}ak($X4j?>74{lVJPo=u}il%ywv9D^m*}TxGA~wqmK-J1H2op5(&Pv*#LNWVlS&ylg zAvhNx*I)r19Bh2X4?36YS&2z}3 z&||RLpXi{|=FhiFt7mR97+FPwK@lXjOZTEW`d+VZpWf$ zB$SECGl0q%H?3HJfQ7$6yF388@ZNxLjKJn4b`~!tH1A8op(v@zXve*z@uh%nR+MMR zP**M;t%yvDXvH!T%&vk;(B@3#Jo4d~3n^>bEOgIMwK|pakk-S1(2e}2XOCoQKtPtj zOA4ItUX3{3m0G(ZR4wG=A)+Q2mr@?Ql*x82#jQk6=~j4CzTjw&Y5f$F<7Mg!7Xw)( z+d1aM7;Jr4k4nozdsztGA~DU$A43{(EmCG~evs+E!Z@e%0I{l{q5rz-D2I#vSH$JN zR~=_}XCK97OF%-z2YHKVhY{3kL6&d{N)pyVi;{}5^+G3gs3YmiQ1twe-|B~@tYE?R z{E&qA*-krP$W%lb?cKd8U7=ZTH#Vyt`1(x4Pm*XUI3)XsncWSIc>0AlvKP@M>jS)w z4WzO3(YJWoq3B=c=j!pm8p{%7fxO_P~d~DxuPW8P_;x}xo#Jcn<-b1L87h6H|On;Q;Q74UB>N9_4 z+o64p$bO9=#OJ2D(xvIZ49 zA9w2EqQ5rS1NwAd;M*o!#*on_#~woXMX&^C=i59q-x9?)AjyvqS`mqg;BehH-pCRQ z&Ck(Ex_g}dh(o?#fI80yqG)e_(1Sq1&_U2aV1R#QwXEDvfwaKaKME`eF!;X|84+ax zItf|Pmk$7iMqcXITOm3ETE&3>{`3L%s=t({L|Xs3RYpKoLR3UanO;Wp2>}k|DP;G* zwIP7qyniV}!1cd`R;{v0r~Qud+dKEKnIj1ezsZ$AY16YuGK*Vj4=Rw2!FXB z7x?DC#p?x(j7*&VofqQoSmOm7MJRyvmH`6x^Ix!>fN%c^>nA<B> zpHhnaEHl8!{07g-$* z|33l$#sp8uXbRv6i~uSG(BvoWp`YauSVew=?Ck7lVd&y)^5+J7-hLFq24FXV5A{z> z;3^FE{{;IF%|(AyjOTsA1e*KAK>S&*0Jr?_<_a1(Ss4AdO`qsvN1HT$)xiBvZu2XY zKk%FLe?$FH>iIpCV%H%e901b*d`Nya3Al1c{70yt8fSkU?WdM~$P~p!2091>IL)tP z@&V}|;S^1PfiZFYiO6mkzjgq_DN3O7r-O?rGYHC|BUsoJVHOc=bu(l zyk8Jqfj|Gd%SMhC&L)l)2EQzgPqxc-$j;LPy0`@c0z&@_Q0 z+fPK)ZTb_<3jlKi==PKP$IoI3So9B#1jM)hJwQ)V+&?YwKbzPSxFCjYMEzwzY({FeADFeLyR0uls-^%wlN#DB*Bx2XkPEPg0S z2pIrTC`M1ZMSr!=Qx!|c6)?b70zCBnrFcF6|Et2zz}m#g=r`3o4e?~?SUN3G zjV4eH^Is|f0XYYz-=8e?x1LD+W+l0!5XTcZwBP@^w@As%rBb7#^r9TEh zJvr^KezhU`M^rh-|ENU$ee~nrMR&9Tm`_U*Fr)rbK*j%v3AhxXRRipkf0_+Xt!4{_ zDxCt*k)ZzW4EPfNOAXHdFd3N0%c6FHruGA{zs_7fsXs$ny8tPj{@KD{5mutyfGe~C zUiOq!^k?yt{xdL;O~%3qkcA@?w6n7|F|d^b9aMQpZ@L_gibCwZqMo1owyiO6(K=BN z4KPF+Y#C^Z)W4ek>P3i3)mr%!2~YCFRZKiGwiTx%&aHu@c0?F_KF>%trn=H_fk3WX zuUB$I541d$I1YlY1DYG$(}N{!bVeRFky#npWCGZrDL1l=sB}$_5m{qB6Icr{V9oCaczosiz=&kVNR=p zTd;-qfDYD729s(3ND-TrX1CeoaD=+wcZR$5|G;?>ydcJFnxf{}ZL)L)%dcG{+RHlW zhNl|Q;uXBoE)n(U3Z?USc(AlAP2^=eGn|59Qt{9Oma&Z|fKvyKrX%ZVJ)4>plMnBe z=X3j&cH)x~uRoX+Z36mzJUmc5ZdaqpFH#LwP4b=5w`2VxV-b`&q*+f()Q4Yx8)OFgCp!{5Oal!bYxUwi@mm=2ixIq+8Ro+h5rRQe9B_G z+9S@IMsZ^x1Px|_uFjFMF4UTZD-_J1c&);R&8`d9=P-jH#%qjx=(LU>YV)6}8 z3LqgLqlr$DmT%2;+3Xc1`3$Tz3GmIw=AFL@_)5Te*u2kW08etk`Fny`13slqLM|5v z+yhm70acipD%wnx_@4MctqV(Zqs1id0*Kp&;ikGpT!uZ#evD$44uS87SkipH#Bc;NLa^{X{ z226GdymnO~X5vh15oMvCfviY7X+G&QC8o646DM|W;iJFc+e^Wri%h(q!dWus3Ce!x zry{}Xbe!L#JwW%NvwVQhJuSkr(~-w=rlR7c3=D6zXKMsty`VY1##?9m0m?&6R$afz zUp*VpVt|9a{l#C`=SWc6j;r=a)azPD_P$;ly{6qh?{&#sW1gT!60l*jcaOLL+XVs5 zOu&}&R6xyT&Az6x@#yaPSAcmINTdNZpOfsdZ4dMXt7WR zS;raAwyw+9?w|?02II?pW6UBIF*zg4j_YC!+!qZ>!NeUSDMPyz^k7Ywkyw67BEyBd zlO6PLecy?9X7$=%-QdyYosWt86*Vt0TVdGqFOF#08n`d<@I6+q*JNByU%NuI@uSS^ zYk@NaO{;6geOyhfVSAIb2!4344yZ*p841kI-g0y#e$-kC$0I?y6Jeaz+L`{sz zR(ooq-k5iE_zY0@>!{A9YO25KyvN$m(EwDsbc}CP`V>@$Dokn;wnfPT6D8eB@o*=w zu27BTw>~6!8`_LA6GajziM#~jU+=u9O|9VrWyVb%kbqH?(;yygwHq+lyQ1i6z zMRl~%l1;;dD&HmO-~(3$LPgCge*mToCOV4%AoP4fgJ)m~{#>Q#AS8DgnCR(4kX zKiN%CgI_lf3(JHGTHoaxQ>VOL%?9=QrMtsMe6wO26ltPEY#ywKzGDpb9A>H|wx5rW z$%Kk-V>Ru@RAheF4>%*+6Rw6tRs5lDWZI%PkNyPsXONzU?pJV_s!t7hN3@cy*9xio zht2`Li^0tRmmkseSZmDnM(va##^&^-aPn!NPD;x2E1IvL~2xB06!7?a~|NULXz&Y)C;%%>P7@X5O(wnB0$ zk-}q?Wktq(iXZ$ay{LWjQMX}~I~)3D0Jdy2hW{2-+thNGkGNfz9A8J^J3)T$r!i8- zMy7}~wt?6*NjtWip0f!+3W5LO1>>)YTl})wA}h;JCcpTxo`WdX_*u8YEdJO}80KNK>-P6wtLc43o6+`2

    ${user.email}${user.roles}checked/>
    + + \ No newline at end of file diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css index ef7ff212..3377ccde 100644 --- a/src/main/webapp/resources/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -23,10 +23,23 @@ tr[data-mealExcess="true"] { color: red; } -header, footer { - background: none repeat scroll 0 0 #A6C9E2; - color: #2E6E9E; - font-size: 20px; - padding: 5px 20px; - margin: 6px 0; +.fa { + cursor: pointer; +} + +/*https://getbootstrap.com/docs/4.0/examples/sticky-footer/sticky-footer.css*/ +html { + position: relative; + min-height: 100%; +} +body { + margin-bottom: 60px; /* Margin bottom by footer height */ +} +.footer { + position: absolute; + bottom: 0; + width: 100%; + height: 60px; /* Set the fixed height of the footer here */ + line-height: 60px; /* Vertically center the text there */ + background-color: #f5f5f5; } diff --git a/src/main/webapp/resources/images/icon-meal.png b/src/main/webapp/resources/images/icon-meal.png new file mode 100644 index 0000000000000000000000000000000000000000..b4fc54ad01294abb103d9238c12b1826a9240dfd GIT binary patch literal 1898 zcmV-w2bK7VP)PS&+k>eS5@~O7WLfN z#z>r1)zQ7@JLlXpd}L<$FKLxT1mO7i`0((sD2l48LI|y1MB+H^bUOWhKS`1&t~@IA z1b~^D88e&NXf)c{*(uAi)iCqP{SZP3!OTRY*Xx}A^;#TU>54SHnYXW#g&y6RSh99^HTs$tZ#2`udlC*h;z;d0}zWj3TB}R zFn|flWT07)I)DiT%x1GFidI)w^E?kB5K$Y-9N@(I#>U3x=4P6v0K6d;BWwP3q;Kx$ z_a^B<71hQG`=w-hwO4#}p&VpZ25mf4sA(GS{pHJ-yWQ>y2H8Vl=B=%*jg5^g%R(>^ zpsL+&Chb!3l!F8yFZyq(Lj5wj%J{QS=1FV`-RXv|gz1JiLuH$O`+tkPjc%tTC8 zFh=;G>b&~)rPnv3Ze$)5$8pm%o12^M8_WSlqmlRCIcLNj^|#;a|88fwA6w$=xgmY{ z3@O6{xx>xRAXX4z=JADR>E-A6R2)3HpZxQ}ojuAO`G7P{_xJaw(g~evn6dlC zz2#Zsoia0v$b$zDh=_^k=;$bf;GAP-3;wnBGbR$umb>)D6^v`t0WV%eKS2{4Wl=yZli zQ3XsrIx+ew0zib-1)S^QbOHd(2!?BCD8SOl{xn)BYg1tn0Wg_N*vzV`a?TO4WBc1d zR;d)!V2&CN+Nj4+MfR07QZ`!jIp;_5>jO?C-d9fIKE* zu-I^;zq+&~{XOY%# z5#czF=Rpv~PA_$85Q)&-ghCj?bBuPEs6ZGZPzDp6%=1G+xi%NO7D!e2q_RG|zaIE4tDSi`uGlo$I_gH^9nL%#7Qm zwy>};o6WrUPPl}7=~DWQ)4QejGoqQdqOrEijv55RG{6antne1h4gzPPym{gH%CI-} zh+{V%kK;IQ>CAj(647`(-rL(-UVf&o8)A;F{=@y^)=no$GbJ3?U|1qpi10xPAv8v@ zz){Gm34shDAOyfb1Qrm7fW`-5YB|H<;o)E~NYk{<`O}$Qi%}HC?(S}yru}~3 zIcGo!W-x#-pHg5VCNi_KEDsJ2h-fevsOqEEp9DC8X(Ky0I4FujRns(`7d6kT&U;_i z^>jKd%aVwCy Date: Thu, 1 Apr 2021 15:13:11 +0300 Subject: [PATCH 095/144] 8_09_ajax_datatables.patch --- config/messages/app.properties | 1 + config/messages/app_ru.properties | 1 + .../topjava/web/user/AdminUIController.java | 35 ++++++++++++ .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 7 +++ src/main/webapp/WEB-INF/jsp/users.jsp | 54 +++++++++++++++++-- .../webapp/resources/js/topjava.common.js | 39 ++++++++++++++ src/main/webapp/resources/js/topjava.users.js | 42 +++++++++++++++ 7 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java create mode 100644 src/main/webapp/resources/js/topjava.common.js create mode 100644 src/main/webapp/resources/js/topjava.users.js diff --git a/config/messages/app.properties b/config/messages/app.properties index 65d9fe20..fc7665a8 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -10,6 +10,7 @@ user.email=Email user.roles=Roles user.active=Active user.registered=Registered +user.password=Password meal.title=Meals meal.edit=Edit meal diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 7210c082..5ea49005 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -10,6 +10,7 @@ user.email=Почта user.roles=Роли user.active=Активный user.registered=Зарегистрирован +user.password=Пароль meal.title=Моя еда meal.edit=Редактировать еду diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java new file mode 100644 index 00000000..887cd471 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -0,0 +1,35 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +@RestController +@RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) +public class AdminUIController extends AbstractUserController { + + @Override + @GetMapping + public List getAll() { + return super.getAll(); + } + + @Override + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable int id) { + super.delete(id); + } + + @PostMapping + @ResponseStatus(HttpStatus.NO_CONTENT) + public void create(@RequestParam String name, + @RequestParam String email, + @RequestParam String password) { + super.create(new User(null, name, email, password, Role.USER)); + } +} diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index e89f67e9..3c399a4b 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -10,5 +10,12 @@ + + + <%--http://stackoverflow.com/a/24070373/548473--%> + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index b9219c62..3cad74b4 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -6,16 +6,18 @@ + +

    - - +
    @@ -33,15 +35,59 @@ - + - +
    ${user.email} ${user.roles}checked/>checked id="${user.id}"/>
    + + \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js new file mode 100644 index 00000000..69c0d72c --- /dev/null +++ b/src/main/webapp/resources/js/topjava.common.js @@ -0,0 +1,39 @@ +function makeEditable() { + $(".delete").click(function () { + deleteRow($(this).attr("id")); + }); +} + +function add() { + $("#detailsForm").find(":input").val(""); + $("#editRow").modal(); +} + +function deleteRow(id) { + $.ajax({ + url: ajaxUrl + id, + type: "DELETE", + success: function () { + updateTable(); + } + }); +} + +function updateTable() { + $.get(ajaxUrl, function (data) { + datatableApi.clear().rows.add(data).draw(); + }); +} + +function save() { + const form = $("#detailsForm"); + $.ajax({ + type: "POST", + url: ajaxUrl, + data: form.serialize(), + success: function () { + $("#editRow").modal("hide"); + updateTable(); + } + }); +} \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js new file mode 100644 index 00000000..db10e94b --- /dev/null +++ b/src/main/webapp/resources/js/topjava.users.js @@ -0,0 +1,42 @@ +const ajaxUrl = "admin/users/"; +let datatableApi; + +// $(document).ready(function () { +$(function () { + datatableApi = $("#datatable").DataTable({ + "paging": false, + "info": true, + "columns": [ + { + "data": "name" + }, + { + "data": "email" + }, + { + "data": "roles" + }, + { + "data": "enabled" + }, + { + "data": "registered" + }, + { + "defaultContent": "Edit", + "orderable": false + }, + { + "defaultContent": "Delete", + "orderable": false + } + ], + "order": [ + [ + 0, + "asc" + ] + ] + }); + makeEditable(); +}); \ No newline at end of file From cfe3bd58b55955631bbf2bc4ec250e40db212dff Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 16:00:57 +0300 Subject: [PATCH 096/144] 8_10_refactor_js.patch --- src/main/webapp/WEB-INF/jsp/users.jsp | 6 +- .../webapp/resources/js/topjava.common.js | 37 +++++---- src/main/webapp/resources/js/topjava.users.js | 79 ++++++++++--------- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index 3cad74b4..9540b438 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -31,14 +31,14 @@ - + ${user.email} ${user.roles} - checked id="${user.id}"/> + checked/> - + diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index 69c0d72c..d99060ee 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -1,27 +1,33 @@ -function makeEditable() { +let form; + +function makeEditable(datatableApi) { + ctx.datatableApi = datatableApi; + + form = $('#detailsForm'); $(".delete").click(function () { - deleteRow($(this).attr("id")); + if (confirm('Are you sure?')) { + deleteRow($(this).closest('tr').attr("id")); + } }); } function add() { - $("#detailsForm").find(":input").val(""); + form.find(":input").val(""); $("#editRow").modal(); } function deleteRow(id) { $.ajax({ - url: ajaxUrl + id, - type: "DELETE", - success: function () { - updateTable(); - } + url: ctx.ajaxUrl + id, + type: "DELETE" + }).done(function () { + updateTable(); }); } function updateTable() { - $.get(ajaxUrl, function (data) { - datatableApi.clear().rows.add(data).draw(); + $.get(ctx.ajaxUrl, function (data) { + ctx.datatableApi.clear().rows.add(data).draw(); }); } @@ -29,11 +35,10 @@ function save() { const form = $("#detailsForm"); $.ajax({ type: "POST", - url: ajaxUrl, - data: form.serialize(), - success: function () { - $("#editRow").modal("hide"); - updateTable(); - } + url: ctx.ajaxUrl, + data: form.serialize() + }).done(function () { + $("#editRow").modal("hide"); + updateTable(); }); } \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js index db10e94b..10a3d301 100644 --- a/src/main/webapp/resources/js/topjava.users.js +++ b/src/main/webapp/resources/js/topjava.users.js @@ -1,42 +1,47 @@ -const ajaxUrl = "admin/users/"; -let datatableApi; +const userAjaxUrl = "admin/users/"; + +// https://stackoverflow.com/a/5064235/548473 +const ctx = { + ajaxUrl: userAjaxUrl +}; // $(document).ready(function () { $(function () { - datatableApi = $("#datatable").DataTable({ - "paging": false, - "info": true, - "columns": [ - { - "data": "name" - }, - { - "data": "email" - }, - { - "data": "roles" - }, - { - "data": "enabled" - }, - { - "data": "registered" - }, - { - "defaultContent": "Edit", - "orderable": false - }, - { - "defaultContent": "Delete", - "orderable": false - } - ], - "order": [ - [ - 0, - "asc" + makeEditable( + $("#datatable").DataTable({ + "paging": false, + "info": true, + "columns": [ + { + "data": "name" + }, + { + "data": "email" + }, + { + "data": "roles" + }, + { + "data": "enabled" + }, + { + "data": "registered" + }, + { + "defaultContent": "Edit", + "orderable": false + }, + { + "defaultContent": "Delete", + "orderable": false + } + ], + "order": [ + [ + 0, + "asc" + ] ] - ] - }); - makeEditable(); + }) + ); }); \ No newline at end of file From 226c1893700fadd2a59dfe69368329f290dc65e5 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 1 Apr 2021 16:01:37 +0300 Subject: [PATCH 097/144] 8_11_notification.patch --- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 2 + src/main/webapp/WEB-INF/jsp/users.jsp | 4 +- .../webapp/resources/js/topjava.common.js | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 3c399a4b..23ed8d21 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -11,11 +11,13 @@ + <%--http://stackoverflow.com/a/24070373/548473--%> + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index 9540b438..67a9a173 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -50,7 +50,7 @@ diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index e7e1a17d..81bb7f25 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -82,4 +82,16 @@ function failNoty(jqXHR) { type: "error", layout: "bottomRight" }).show(); +} + +function renderEditBtn(data, type, row) { + if (type === "display") { + return ""; + } +} + +function renderDeleteBtn(data, type, row) { + if (type === "display") { + return ""; + } } \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js index 71e6889c..b07a5267 100644 --- a/src/main/webapp/resources/js/topjava.users.js +++ b/src/main/webapp/resources/js/topjava.users.js @@ -27,6 +27,10 @@ function enable(chkbox, id) { $(function () { makeEditable( $("#datatable").DataTable({ + "ajax": { + "url": userAjaxUrl, + "dataSrc": "" + }, "paging": false, "info": true, "columns": [ @@ -34,24 +38,44 @@ $(function () { "data": "name" }, { - "data": "email" + "data": "email", + "render": function (data, type, row) { + if (type === "display") { + return "" + data + ""; + } + return data; + } }, { "data": "roles" }, { - "data": "enabled" + "data": "enabled", + "render": function (data, type, row) { + if (type === "display") { + return ""; + } + return data; + } }, { - "data": "registered" + "data": "registered", + "render": function (date, type, row) { + if (type === "display") { + return date.substring(0, 10); + } + return date; + } }, { - "defaultContent": "Edit", - "orderable": false + "orderable": false, + "defaultContent": "", + "render": renderEditBtn }, { - "defaultContent": "Delete", - "orderable": false + "orderable": false, + "defaultContent": "", + "render": renderDeleteBtn } ], "order": [ @@ -59,7 +83,12 @@ $(function () { 0, "asc" ] - ] + ], + "createdRow": function (row, data, dataIndex) { + if (!data.enabled) { + $(row).attr("data-userEnabled", false); + } + } }) ); }); \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java index d0cad54e..233fee24 100644 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -1,16 +1,11 @@ package ru.javawebinar.topjava.web; -import org.assertj.core.matcher.AssertionMatcher; import org.junit.jupiter.api.Test; -import ru.javawebinar.topjava.model.User; - -import java.util.List; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ru.javawebinar.topjava.MealTestData.meals; -import static ru.javawebinar.topjava.UserTestData.*; import static ru.javawebinar.topjava.util.MealsUtil.getTos; class RootControllerTest extends AbstractControllerTest { @@ -22,14 +17,7 @@ void getUsers() throws Exception { .andExpect(status().isOk()) .andExpect(view().name("users")) .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")) - .andExpect(model().attribute("users", - new AssertionMatcher>() { - @Override - public void assertion(List actual) throws AssertionError { - USER_MATCHER.assertMatch(actual, admin, user); - } - } - )); + .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")); } @Test From 452407eac58641eaa2b91bb46304adf02aafb98f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Fri, 9 Apr 2021 18:40:07 +0300 Subject: [PATCH 107/144] 9_08_js_i18n.patch --- config/messages/app.properties | 6 ++++++ config/messages/app_ru.properties | 6 ++++++ src/main/webapp/WEB-INF/jsp/users.jsp | 11 ++++++++++- src/main/webapp/resources/js/topjava.common.js | 14 ++++++++------ src/main/webapp/resources/js/topjava.users.js | 2 +- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/config/messages/app.properties b/config/messages/app.properties index 4f3f5595..d0df81e6 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -26,5 +26,11 @@ meal.calories=Calories common.add=Add common.select=Select +common.deleted=Record deleted +common.saved=Record saved +common.enabled=Record enabled +common.disabled=Record disabled +common.errorStatus=Error status +common.confirm=Are you sure? common.save=Save common.cancel=Cancel \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index bca68663..90d4aff1 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -26,5 +26,11 @@ meal.calories=Калории common.add=Добавить common.select=Выбрать +common.deleted=Запись удалена +common.saved=Запись сохранена +common.enabled=Запись активирована +common.disabled=Запись деактивирована +common.errorStatus=Статус ошибки +common.confirm=Вы уверены? common.save=Сохранить common.cancel=Отменить \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index f603d449..4f1ea1d1 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -37,7 +37,7 @@ diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index 8c3fa901..1febd2e1 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -46,7 +46,6 @@ function updateTableByData(data) { } function save() { - const form = $("#detailsForm"); $.ajax({ type: "POST", url: ctx.ajaxUrl, @@ -83,7 +82,8 @@ function failNoty(jqXHR) { text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + (jqXHR.responseJSON ? "
    " + jqXHR.responseJSON : ""), type: "error", layout: "bottomRight" - }).show(); + }); + failedNote.show(); } function renderEditBtn(data, type, row) { diff --git a/src/main/webapp/resources/js/topjava.meals.js b/src/main/webapp/resources/js/topjava.meals.js index c37dc9f8..dacdad3d 100644 --- a/src/main/webapp/resources/js/topjava.meals.js +++ b/src/main/webapp/resources/js/topjava.meals.js @@ -6,7 +6,7 @@ const ctx = { updateTable: function () { $.ajax({ type: "GET", - url: "profile/meals/filter", + url: mealAjaxUrl + "filter", data: $("#filter").serialize() }).done(updateTableByData); } @@ -14,7 +14,7 @@ const ctx = { function clearFilter() { $("#filter")[0].reset(); - $.get("profile/meals/", updateTableByData); + $.get(mealAjaxUrl, updateTableByData); } $(function () { diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java index bb07e165..88e94c95 100644 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java @@ -19,7 +19,6 @@ void getUsers() throws Exception { .andDo(print()) .andExpect(status().isOk()) .andExpect(view().name("users")) - .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")) .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")); } From 0ed37e89eb70bda8fe3007a30be2dbcf2c7df6aa Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 13:02:16 +0300 Subject: [PATCH 113/144] 10_0_fix.patch --- .../javawebinar/topjava/util/ErrorUtil.java | 25 +++++++++++++++++++ src/main/webapp/WEB-INF/jsp/i18n.jsp | 19 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/util/ErrorUtil.java create mode 100644 src/main/webapp/WEB-INF/jsp/i18n.jsp diff --git a/src/main/java/ru/javawebinar/topjava/util/ErrorUtil.java b/src/main/java/ru/javawebinar/topjava/util/ErrorUtil.java new file mode 100644 index 00000000..e1217d53 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/ErrorUtil.java @@ -0,0 +1,25 @@ +package ru.javawebinar.topjava.util; + +/* + * @autor Alexandr.Yakubov + **/ + +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; + +import java.util.stream.Collectors; + +public class ErrorUtil { + private ErrorUtil() { + } + + public static ResponseEntity getStringResponseEntity(BindingResult result) { + if (result.hasErrors()) { + String errorFieldsMsg = result.getFieldErrors().stream() + .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) + .collect(Collectors.joining("
    ")); + return ResponseEntity.unprocessableEntity().body(errorFieldsMsg); + } + return null; + } +} diff --git a/src/main/webapp/WEB-INF/jsp/i18n.jsp b/src/main/webapp/WEB-INF/jsp/i18n.jsp new file mode 100644 index 00000000..cf8341d2 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/i18n.jsp @@ -0,0 +1,19 @@ +<%-- + Created by IntelliJ IDEA. + User: Alexandr.Yakubov + Date: 13.04.2021 + Time: 11:54 + To change this template use File | Settings | File Templates. +--%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + From ead84a4fe11de346e5c8a8e51a38f7ce0d48affe Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 13:02:54 +0300 Subject: [PATCH 114/144] 10_01_HW9_binding_ajax.patch --- .../ru/javawebinar/topjava/model/Meal.java | 7 +- .../topjava/util/ValidationUtil.java | 11 ++ .../topjava/web/RootController.java | 11 +- .../topjava/web/meal/MealUIController.java | 26 +++- .../topjava/web/user/AdminUIController.java | 7 +- src/main/resources/logback.xml | 2 +- .../webapp/WEB-INF/jsp/fragments/i18n.jsp | 14 +++ src/main/webapp/WEB-INF/jsp/meals.jsp | 20 +--- src/main/webapp/WEB-INF/jsp/users.jsp | 12 +- .../webapp/resources/js/topjava.common.js | 15 ++- src/main/webapp/resources/js/topjava.meals.js | 67 ++++++----- src/main/webapp/resources/js/topjava.users.js | 112 ++++++++---------- 12 files changed, 163 insertions(+), 141 deletions(-) create mode 100644 src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 14b3445c..44dabbf5 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -4,6 +4,7 @@ import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import org.hibernate.validator.constraints.Range; +import org.springframework.format.annotation.DateTimeFormat; import javax.persistence.*; import javax.validation.constraints.NotBlank; @@ -32,6 +33,7 @@ public class Meal extends AbstractBaseEntity { @Column(name = "date_time", nullable = false) @NotNull + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) private LocalDateTime dateTime; @Column(name = "description", nullable = false) @@ -40,8 +42,9 @@ public class Meal extends AbstractBaseEntity { private String description; @Column(name = "calories", nullable = false) + @NotNull @Range(min = 10, max = 5000) - private int calories; + private Integer calories; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) @@ -88,7 +91,7 @@ public void setDescription(String description) { this.description = description; } - public void setCalories(int calories) { + public void setCalories(Integer calories) { this.calories = calories; } diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index b77462d3..ed5a7cd7 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -2,12 +2,15 @@ import org.springframework.core.NestedExceptionUtils; +import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; +import org.springframework.validation.BindingResult; import ru.javawebinar.topjava.HasId; import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.validation.*; import java.util.Set; +import java.util.stream.Collectors; public class ValidationUtil { @@ -72,4 +75,12 @@ public static Throwable getRootCause(@NonNull Throwable t) { Throwable rootCause = NestedExceptionUtils.getRootCause(t); return rootCause != null ? rootCause : t; } + + public static ResponseEntity getErrorResponse(BindingResult result) { + return ResponseEntity.unprocessableEntity().body( + result.getFieldErrors().stream() + .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) + .collect(Collectors.joining("
    ")) + ); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 2fb0301c..a1f32d13 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -1,18 +1,11 @@ package ru.javawebinar.topjava.web; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import ru.javawebinar.topjava.service.MealService; -import ru.javawebinar.topjava.util.MealsUtil; @Controller public class RootController { - @Autowired - private MealService mealService; - @GetMapping("/") public String root() { return "redirect:meals"; @@ -29,9 +22,7 @@ public String login() { } @GetMapping("/meals") - public String getMeals(Model model) { - model.addAttribute("meals", - MealsUtil.getTos(mealService.getAll(SecurityUtil.authUserId()), SecurityUtil.authUserCaloriesPerDay())); + public String getMeals() { return "meals"; } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java index dafc7a70..2b4f2bdc 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java @@ -1,15 +1,17 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.to.MealTo; +import ru.javawebinar.topjava.util.ValidationUtil; +import javax.validation.Valid; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; @@ -23,6 +25,12 @@ public List getAll() { return super.getAll(); } + @Override + @GetMapping( "/{id}") + public Meal get(@PathVariable int id) { + return super.get(id); + } + @Override @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) @@ -32,10 +40,16 @@ public void delete(@PathVariable int id) { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public void create(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime, - @RequestParam String description, - @RequestParam int calories) { - super.create(new Meal(null, dateTime, description, calories)); + public ResponseEntity createOrUpdate(@Valid Meal meal, BindingResult result) { + if (result.hasErrors()) { + return ValidationUtil.getErrorResponse(result); + } + if (meal.isNew()) { + super.create(meal); + } else { + super.update(meal, meal.getId()); + } + return ResponseEntity.ok().build(); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 199daa8f..98084be5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -7,10 +7,10 @@ import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.util.ValidationUtil; import javax.validation.Valid; import java.util.List; -import java.util.stream.Collectors; @RestController @RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) @@ -39,10 +39,7 @@ public void delete(@PathVariable int id) { @ResponseStatus(HttpStatus.NO_CONTENT) public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { if (result.hasErrors()) { - String errorFieldsMsg = result.getFieldErrors().stream() - .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) - .collect(Collectors.joining("
    ")); - return ResponseEntity.unprocessableEntity().body(errorFieldsMsg); + return ValidationUtil.getErrorResponse(result); } if (userTo.isNew()) { super.create(userTo); diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index dc5b6776..c327926f 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -23,7 +23,7 @@ - + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp new file mode 100644 index 00000000..3e2ad09b --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp @@ -0,0 +1,14 @@ +<%@ page contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/meals.jsp b/src/main/webapp/WEB-INF/jsp/meals.jsp index 0724425a..5669bec1 100644 --- a/src/main/webapp/WEB-INF/jsp/meals.jsp +++ b/src/main/webapp/WEB-INF/jsp/meals.jsp @@ -62,21 +62,6 @@ - - - - - <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> - <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> - <%--${fn:replace(meal.dateTime, 'T', ' ')}--%> - ${fn:formatDateTime(meal.dateTime)} - - ${meal.description} - ${meal.calories} - - - - @@ -85,7 +70,7 @@

    + diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 56159c5f..39fa5a1d 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; @@ -14,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; import static ru.javawebinar.topjava.web.user.ProfileRestController.REST_URL; @@ -46,6 +48,23 @@ void delete() throws Exception { USER_MATCHER.assertMatch(userService.getAll(), admin); } + @Test + void register() throws Exception { + UserTo newTo = new UserTo(null, "newName", "newemail@ya.ru", "newPassword", 1500); + User newUser = UserUtil.createNewFromTo(newTo); + ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL + "/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(newTo))) + .andDo(print()) + .andExpect(status().isCreated()); + + User created = readFromJson(action, User.class); + int newId = created.getId(); + newUser.setId(newId); + USER_MATCHER.assertMatch(created, newUser); + USER_MATCHER.assertMatch(userService.get(newId), newUser); + } + @Test void update() throws Exception { UserTo updatedTo = new UserTo(null, "newName", "newemail@ya.ru", "newPassword", 1500); From 9de22b179bc2b61eafd5f22cc96fa0d855fefd76 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:12:44 +0300 Subject: [PATCH 123/144] 10_10_not_found_422.patch --- .../util/exception/NotFoundException.java | 5 ++++ .../web/meal/MealRestControllerTest.java | 15 ++++++++++++ .../web/user/AdminRestControllerTest.java | 24 +++++++++++++++---- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java index f1e9b0e4..9ff0bbbb 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -1,5 +1,10 @@ package ru.javawebinar.topjava.util.exception; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +// http://stackoverflow.com/a/22358422/548473 +@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "No data found") // 422 public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java index 8dfddf06..d5062c4f 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -47,6 +47,14 @@ void getUnauth() throws Exception { .andExpect(status().isUnauthorized()); } + @Test + void getNotFound() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_MEAL_ID) + .with(userHttpBasic(user))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()); + } + @Test void delete() throws Exception { perform(MockMvcRequestBuilders.delete(REST_URL + MEAL1_ID) @@ -55,6 +63,13 @@ void delete() throws Exception { assertThrows(NotFoundException.class, () -> mealService.get(MEAL1_ID, USER_ID)); } + @Test + void deleteNotFound() throws Exception { + perform(MockMvcRequestBuilders.delete(REST_URL + ADMIN_MEAL_ID) + .with(userHttpBasic(user))) + .andExpect(status().isUnprocessableEntity()); + } + @Test void update() throws Exception { Meal updated = getUpdated(); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 90940757..7ed785bb 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -5,7 +5,6 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import ru.javawebinar.topjava.UserTestData; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -39,6 +38,14 @@ void get() throws Exception { .andExpect(USER_MATCHER.contentJson(admin)); } + @Test + void getNotFound() throws Exception { + perform(MockMvcRequestBuilders.get(REST_URL + NOT_FOUND) + .with(userHttpBasic(admin))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()); + } + @Test void getByEmail() throws Exception { perform(MockMvcRequestBuilders.get(REST_URL + "by?email=" + admin.getEmail()) @@ -57,6 +64,14 @@ void delete() throws Exception { assertThrows(NotFoundException.class, () -> userService.get(USER_ID)); } + @Test + void deleteNotFound() throws Exception { + perform(MockMvcRequestBuilders.delete(REST_URL + NOT_FOUND) + .with(userHttpBasic(admin))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()); + } + @Test void getUnAuth() throws Exception { perform(MockMvcRequestBuilders.get(REST_URL)) @@ -72,19 +87,20 @@ void getForbidden() throws Exception { @Test void update() throws Exception { - User updated = UserTestData.getUpdated(); + User updated = getUpdated(); + updated.setId(null); perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) .contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(admin)) .content(JsonUtil.writeValue(updated))) .andExpect(status().isNoContent()); - USER_MATCHER.assertMatch(userService.get(USER_ID), updated); + USER_MATCHER.assertMatch(userService.get(USER_ID), getUpdated()); } @Test void createWithLocation() throws Exception { - User newUser = UserTestData.getNew(); + User newUser = getNew(); ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) .contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(admin)) From 4329adcc5260802e7d90854435511ece4d7e3458 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:13:08 +0300 Subject: [PATCH 124/144] 10_11_global_exception.patch --- config/messages/app.properties | 1 + config/messages/app_ru.properties | 1 + .../topjava/web/GlobalExceptionHandler.java | 36 +++++++++++++++++++ src/main/webapp/WEB-INF/jsp/exception.jsp | 26 ++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java create mode 100644 src/main/webapp/WEB-INF/jsp/exception.jsp diff --git a/config/messages/app.properties b/config/messages/app.properties index 22d530cb..8987ce18 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -40,6 +40,7 @@ common.saved=Record saved common.enabled=Record enabled common.disabled=Record disabled common.errorStatus=Error status +common.appError=Application error common.confirm=Are you sure? common.save=Save common.cancel=Cancel \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 7a9030c1..be85ad30 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -40,6 +40,7 @@ common.saved=Запись сохранена common.enabled=Запись активирована common.disabled=Запись деактивирована common.errorStatus=Статус ошибки +common.appError=Ошибка приложения common.confirm=Вы уверены? common.save=Сохранить common.cancel=Отменить \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java new file mode 100644 index 00000000..bd401d1c --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.util.ValidationUtil; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler(Exception.class) + public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { + log.error("Exception at request " + req.getRequestURL(), e); + Throwable rootCause = ValidationUtil.getRootCause(e); + + HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + ModelAndView mav = new ModelAndView("exception", + Map.of("exception", rootCause, "message", rootCause.toString(), "status", httpStatus)); + mav.setStatus(httpStatus); + + // Interceptor is not invoked, put userTo + AuthorizedUser authorizedUser = SecurityUtil.safeGet(); + if (authorizedUser != null) { + mav.addObject("userTo", authorizedUser.getUserTo()); + } + return mav; + } +} diff --git a/src/main/webapp/WEB-INF/jsp/exception.jsp b/src/main/webapp/WEB-INF/jsp/exception.jsp new file mode 100644 index 00000000..90f84f4b --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/exception.jsp @@ -0,0 +1,26 @@ +<%@ page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + + +
    +
    +
    +

    ${status}

    +

    +

    ${message}

    +
    +
    + + + + \ No newline at end of file From 33a25b934da474a0063e0bbbcec84d138ef4fd67 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:13:41 +0300 Subject: [PATCH 125/144] 10_12_controller_advice_exception.patch --- config/curl.md | 5 +- .../topjava/util/ValidationUtil.java | 5 +- .../topjava/util/exception/ErrorInfo.java | 13 ++++ .../topjava/util/exception/ErrorType.java | 8 +++ .../IllegalRequestDataException.java | 7 ++ .../util/exception/NotFoundException.java | 5 -- .../topjava/web/ExceptionInfoHandler.java | 65 +++++++++++++++++++ .../topjava/web/meal/MealUIController.java | 1 + .../topjava/web/user/AdminUIController.java | 1 + .../webapp/resources/js/topjava.common.js | 4 +- 10 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java diff --git a/config/curl.md b/config/curl.md index 71387870..3128ccbf 100644 --- a/config/curl.md +++ b/config/curl.md @@ -32,4 +32,7 @@ `curl -s -X POST -d '{"dateTime":"2020-02-01T12:00","description":"Created lunch","calories":300}' -H 'Content-Type:application/json;charset=UTF-8' http://localhost:8080/topjava/rest/profile/meals --user user@yandex.ru:password` #### update Meals -`curl -s -X PUT -d '{"dateTime":"2020-01-30T07:00", "description":"Updated breakfast", "calories":200}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` \ No newline at end of file +`curl -s -X PUT -d '{"dateTime":"2020-01-30T07:00", "description":"Updated breakfast", "calories":200}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` + +#### validate with Error +`curl -s -X POST -d '{}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/admin/users --user admin@gmail.com:admin` diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index ed5a7cd7..6c91f95f 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -6,6 +6,7 @@ import org.springframework.lang.NonNull; import org.springframework.validation.BindingResult; import ru.javawebinar.topjava.HasId; +import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.validation.*; @@ -56,7 +57,7 @@ public static void checkNotFound(boolean found, String msg) { public static void checkNew(HasId bean) { if (!bean.isNew()) { - throw new IllegalArgumentException(bean + " must be new (id=null)"); + throw new IllegalRequestDataException(bean + " must be new (id=null)"); } } @@ -65,7 +66,7 @@ public static void assureIdConsistent(HasId bean, int id) { if (bean.isNew()) { bean.setId(id); } else if (bean.id() != id) { - throw new IllegalArgumentException(bean + " must be with id=" + id); + throw new IllegalRequestDataException(bean + " must be with id=" + id); } } diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java new file mode 100644 index 00000000..d4332359 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava.util.exception; + +public class ErrorInfo { + private final String url; + private final ErrorType type; + private final String detail; + + public ErrorInfo(CharSequence url, ErrorType type, String detail) { + this.url = url.toString(); + this.type = type; + this.detail = detail; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java new file mode 100644 index 00000000..c53a433b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java @@ -0,0 +1,8 @@ +package ru.javawebinar.topjava.util.exception; + +public enum ErrorType { + APP_ERROR, + DATA_NOT_FOUND, + DATA_ERROR, + VALIDATION_ERROR +} diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java b/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java new file mode 100644 index 00000000..2b144f91 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java @@ -0,0 +1,7 @@ +package ru.javawebinar.topjava.util.exception; + +public class IllegalRequestDataException extends RuntimeException { + public IllegalRequestDataException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java index 9ff0bbbb..f1e9b0e4 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -1,10 +1,5 @@ package ru.javawebinar.topjava.util.exception; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -// http://stackoverflow.com/a/22358422/548473 -@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "No data found") // 422 public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java new file mode 100644 index 00000000..aa841a06 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -0,0 +1,65 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import ru.javawebinar.topjava.util.ValidationUtil; +import ru.javawebinar.topjava.util.exception.ErrorInfo; +import ru.javawebinar.topjava.util.exception.ErrorType; +import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import javax.servlet.http.HttpServletRequest; + +import static ru.javawebinar.topjava.util.exception.ErrorType.*; + +@RestControllerAdvice(annotations = RestController.class) +@Order(Ordered.HIGHEST_PRECEDENCE + 5) +public class ExceptionInfoHandler { + private static Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); + + // http://stackoverflow.com/a/22358422/548473 + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ExceptionHandler(NotFoundException.class) + public ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { + return logAndGetErrorInfo(req, e, false, DATA_NOT_FOUND); + } + + @ResponseStatus(HttpStatus.CONFLICT) // 409 + @ExceptionHandler(DataIntegrityViolationException.class) + public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { + return logAndGetErrorInfo(req, e, true, DATA_ERROR); + } + + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) // 422 + @ExceptionHandler({IllegalRequestDataException.class, MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class}) + public ErrorInfo illegalRequestDataError(HttpServletRequest req, Exception e) { + return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR); + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(Exception.class) + public ErrorInfo handleError(HttpServletRequest req, Exception e) { + return logAndGetErrorInfo(req, e, true, APP_ERROR); + } + + // https://stackoverflow.com/questions/538870/should-private-helper-methods-be-static-if-they-can-be-static + private static ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType) { + Throwable rootCause = ValidationUtil.getRootCause(e); + if (logException) { + log.error(errorType + " at request " + req.getRequestURL(), rootCause); + } else { + log.warn("{} at request {}: {}", errorType, req.getRequestURL(), rootCause.toString()); + } + return new ErrorInfo(req.getRequestURL(), errorType, rootCause.toString()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java index 2b4f2bdc..67715ce7 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java @@ -42,6 +42,7 @@ public void delete(@PathVariable int id) { @ResponseStatus(HttpStatus.NO_CONTENT) public ResponseEntity createOrUpdate(@Valid Meal meal, BindingResult result) { if (result.hasErrors()) { + // TODO change to exception handler return ValidationUtil.getErrorResponse(result); } if (meal.isNew()) { diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 98084be5..0f521856 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -39,6 +39,7 @@ public void delete(@PathVariable int id) { @ResponseStatus(HttpStatus.NO_CONTENT) public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { if (result.hasErrors()) { + // TODO change to exception handler return ValidationUtil.getErrorResponse(result); } if (userTo.isNew()) { diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index c39a1e51..e0a3997c 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -89,8 +89,10 @@ function successNoty(key) { function failNoty(jqXHR) { closeNoty(); + var errorInfo = jqXHR.responseJSON; failedNote = new Noty({ - text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + (jqXHR.responseJSON ? "
    " + jqXHR.responseJSON : ""), + text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + + "
    " + errorInfo.type + "
    " + errorInfo.detail, type: "error", layout: "bottomRight" }); From 07806c39e667cf8396ce4508feef52afb6cbc70f Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:14:25 +0300 Subject: [PATCH 126/144] 10_13_password_encoding.patch --- .../topjava/service/UserService.java | 17 ++++++++++++----- .../ru/javawebinar/topjava/util/UserUtil.java | 9 +++++++++ src/main/resources/db/populateDB.sql | 4 ++-- src/main/resources/spring/spring-security.xml | 4 ++-- .../ru/javawebinar/topjava/UserTestData.java | 4 ++-- src/test/resources/spring/inmemory.xml | 1 + 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 70c8bb52..0946273f 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -6,6 +6,7 @@ import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -17,6 +18,7 @@ import java.util.List; +import static ru.javawebinar.topjava.util.UserUtil.prepareToSave; import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; @@ -25,15 +27,17 @@ public class UserService implements UserDetailsService { private final UserRepository repository; + private final PasswordEncoder passwordEncoder; - public UserService(UserRepository repository) { + public UserService(UserRepository repository, PasswordEncoder passwordEncoder) { this.repository = repository; + this.passwordEncoder = passwordEncoder; } @CacheEvict(value = "users", allEntries = true) public User create(User user) { Assert.notNull(user, "user must not be null"); - return repository.save(user); + return prepareAndSave(user); } @CacheEvict(value = "users", allEntries = true) @@ -59,15 +63,14 @@ public List getAll() { public void update(User user) { Assert.notNull(user, "user must not be null"); // checkNotFoundWithId : check works only for JDBC, disabled - repository.save(user); + prepareAndSave(user); } @CacheEvict(value = "users", allEntries = true) @Transactional public void update(UserTo userTo) { User user = get(userTo.id()); - User updatedUser = UserUtil.updateFromTo(user, userTo); - repository.save(updatedUser); // !! need only for JDBC implementation + prepareAndSave(UserUtil.updateFromTo(user, userTo)); } @CacheEvict(value = "users", allEntries = true) @@ -87,6 +90,10 @@ public AuthorizedUser loadUserByUsername(String email) throws UsernameNotFoundEx return new AuthorizedUser(user); } + private User prepareAndSave(User user) { + return repository.save(prepareToSave(user, passwordEncoder)); + } + public User getWithMeals(int id) { return checkNotFoundWithId(repository.getWithMeals(id), id); } diff --git a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java index 9d44dad3..f81b1a66 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.util; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.StringUtils; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; @@ -23,4 +25,11 @@ public static User updateFromTo(User user, UserTo userTo) { user.setPassword(userTo.getPassword()); return user; } + + public static User prepareToSave(User user, PasswordEncoder passwordEncoder) { + String password = user.getPassword(); + user.setPassword(StringUtils.hasText(password) ? passwordEncoder.encode(password) : password); + user.setEmail(user.getEmail().toLowerCase()); + return user; + } } \ No newline at end of file diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index 9982edf3..dd2adc28 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -4,8 +4,8 @@ DELETE FROM users; ALTER SEQUENCE global_seq RESTART WITH 100000; INSERT INTO users (name, email, password, calories_per_day) -VALUES ('User', 'user@yandex.ru', 'password', 2005), - ('Admin', 'admin@gmail.com', 'admin', 1900); +VALUES ('User', 'user@yandex.ru', '{noop}password', 2005), + ('Admin', 'admin@gmail.com', '{noop}admin', 1900); INSERT INTO user_roles (role, user_id) VALUES ('USER', 100000), diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index c3a3ff0e..7c2ee05b 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -15,7 +15,7 @@ - + @@ -33,7 +33,7 @@ - + + diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index 7c2ee05b..e3c3a864 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -28,7 +28,7 @@ authentication-failure-url="/login?error=true" login-processing-url="/spring_security_check"/> - + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 6e7f3aaf..62f05f9a 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,29 +1,30 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> +<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 5b5089e4..da17fc80 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -4,6 +4,10 @@ + + + + <spring:message code="app.title"/> diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index e0a3997c..71f5a71e 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -21,6 +21,12 @@ function makeEditable(datatableOpts) { // solve problem with cache in IE: https://stackoverflow.com/a/4303862/548473 $.ajaxSetup({cache: false}); + + var token = $("meta[name='_csrf']").attr("content"); + var header = $("meta[name='_csrf_header']").attr("content"); + $(document).ajaxSend(function (e, xhr, options) { + xhr.setRequestHeader(header, token); + }); } function add() { From b7bf5cd4ffab3a5a23fe6eba629e9b5efc49620d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Sun, 18 Apr 2021 19:35:27 +0300 Subject: [PATCH 129/144] fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d1e9b7a7..3a4dcc24 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ UTF-8 5.3.4 - 5.4.5 + 5.4.1 2.4.5 2.12.2 9.0.43 From e851fcaaffcfa55a4edfde7fe9624a8114582191 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:08:51 +0300 Subject: [PATCH 130/144] 11_0_fix.patch --- src/main/java/ru/javawebinar/topjava/AuthorizedUser.java | 5 +++-- .../ru/javawebinar/topjava/web/user/ProfileUIController.java | 2 +- src/main/webapp/WEB-INF/jsp/login.jsp | 2 +- src/main/webapp/resources/css/style.css | 2 +- .../topjava/web/user/ProfileRestControllerTest.java | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index 0842dd2e..2126d687 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -14,14 +14,15 @@ public class AuthorizedUser extends org.springframework.security.core.userdetail public AuthorizedUser(User user) { super(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, user.getRoles()); - this.userTo = UserUtil.asTo(user); + setTo(UserUtil.asTo(user)); } public int getId() { return userTo.id(); } - public void update(UserTo newTo) { + public void setTo(UserTo newTo) { + newTo.setPassword(null); userTo = newTo; } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index 1bcbdd3f..657396a7 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -27,7 +27,7 @@ public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionS return "profile"; } else { super.update(userTo, SecurityUtil.authUserId()); - SecurityUtil.get().update(userTo); + SecurityUtil.get().setTo(userTo); status.setComplete(); return "redirect:/meals"; } diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp index 3ba2feae..7f29ad7a 100644 --- a/src/main/webapp/WEB-INF/jsp/login.jsp +++ b/src/main/webapp/WEB-INF/jsp/login.jsp @@ -14,7 +14,7 @@
    ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
    -
    +
    diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css index e29b59dd..6c383472 100644 --- a/src/main/webapp/resources/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -38,7 +38,7 @@ html { min-height: 100%; } body { - margin-bottom: 60px; /* Margin bottom by footer height */ + margin-bottom: 60px !important; /* Margin bottom by footer height */ } .footer { position: absolute; diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 39fa5a1d..16b73247 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -59,7 +59,7 @@ void register() throws Exception { .andExpect(status().isCreated()); User created = readFromJson(action, User.class); - int newId = created.getId(); + int newId = created.id(); newUser.setId(newId); USER_MATCHER.assertMatch(created, newUser); USER_MATCHER.assertMatch(userService.get(newId), newUser); From c5851c64591a62ca6676f48d30de52757ad226af Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:09:40 +0300 Subject: [PATCH 131/144] 11_01_HW10_fix_encoding.patch --- src/main/webapp/WEB-INF/web.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 9816e2a1..c80a34ec 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -37,16 +37,6 @@ / - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - - encodingFilter org.springframework.web.filter.CharacterEncodingFilter @@ -63,4 +53,14 @@ encodingFilter /* + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + From 2a8c113253fe049344c877d74dd801316f786542 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:10:33 +0300 Subject: [PATCH 132/144] 11_02_HW10_validation.patch --- config/curl.md | 1 + pom.xml | 8 +++++ .../ru/javawebinar/topjava/to/UserTo.java | 2 +- .../topjava/util/ValidationUtil.java | 11 ------- .../topjava/util/exception/ErrorInfo.java | 6 ++-- .../topjava/web/ExceptionInfoHandler.java | 16 +++++++-- .../topjava/web/meal/MealRestController.java | 5 +-- .../topjava/web/meal/MealUIController.java | 10 +----- .../topjava/web/user/AdminRestController.java | 5 +-- .../topjava/web/user/AdminUIController.java | 10 +----- .../web/user/ProfileRestController.java | 5 +-- .../webapp/resources/js/topjava.common.js | 2 +- .../web/meal/MealRestControllerTest.java | 33 ++++++++++++++++--- .../web/user/AdminRestControllerTest.java | 33 ++++++++++++++++--- .../web/user/ProfileRestControllerTest.java | 27 +++++++++++++-- 15 files changed, 122 insertions(+), 52 deletions(-) diff --git a/config/curl.md b/config/curl.md index 3128ccbf..1d444112 100644 --- a/config/curl.md +++ b/config/curl.md @@ -36,3 +36,4 @@ #### validate with Error `curl -s -X POST -d '{}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/admin/users --user admin@gmail.com:admin` +`curl -s -X PUT -d '{"dateTime":"2015-05-30T07:00"}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` diff --git a/pom.xml b/pom.xml index 3a4dcc24..933b1415 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ 5.7.1 3.19.0 2.2 + 2.5.0 5.4.28.Final @@ -311,12 +312,19 @@ ${hamcrest.version} test + + com.jayway.jsonpath + json-path + ${json-path.version} + test + org.springframework spring-test test + org.springframework.security spring-security-test diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index 6f63cfa5..bfbb05f8 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -24,7 +24,7 @@ public class UserTo extends BaseTo implements Serializable { private String email; @NotBlank - @Size(min = 5, max = 32, message = "length must be between 5 and 32 characters") + @Size(min = 5, max = 32) private String password; @Range(min = 10, max = 10000) diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 6c91f95f..17e897c4 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -2,16 +2,13 @@ import org.springframework.core.NestedExceptionUtils; -import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; -import org.springframework.validation.BindingResult; import ru.javawebinar.topjava.HasId; import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.validation.*; import java.util.Set; -import java.util.stream.Collectors; public class ValidationUtil { @@ -76,12 +73,4 @@ public static Throwable getRootCause(@NonNull Throwable t) { Throwable rootCause = NestedExceptionUtils.getRootCause(t); return rootCause != null ? rootCause : t; } - - public static ResponseEntity getErrorResponse(BindingResult result) { - return ResponseEntity.unprocessableEntity().body( - result.getFieldErrors().stream() - .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) - .collect(Collectors.joining("
    ")) - ); - } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java index d4332359..0c5e15a9 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java @@ -3,11 +3,11 @@ public class ErrorInfo { private final String url; private final ErrorType type; - private final String detail; + private final String[] details; - public ErrorInfo(CharSequence url, ErrorType type, String detail) { + public ErrorInfo(CharSequence url, ErrorType type, String... details) { this.url = url.toString(); this.type = type; - this.detail = detail; + this.details = details; } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index aa841a06..a5b8b770 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -7,6 +7,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @@ -40,6 +41,16 @@ public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExceptio return logAndGetErrorInfo(req, e, true, DATA_ERROR); } + @ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY) // 422 + @ExceptionHandler(BindException.class) + public ErrorInfo bindValidationError(HttpServletRequest req, BindException e) { + String[] details = e.getBindingResult().getFieldErrors().stream() + .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) + .toArray(String[]::new); + + return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, details); + } + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) // 422 @ExceptionHandler({IllegalRequestDataException.class, MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class}) public ErrorInfo illegalRequestDataError(HttpServletRequest req, Exception e) { @@ -53,13 +64,14 @@ public ErrorInfo handleError(HttpServletRequest req, Exception e) { } // https://stackoverflow.com/questions/538870/should-private-helper-methods-be-static-if-they-can-be-static - private static ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType) { + private static ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, String... details) { Throwable rootCause = ValidationUtil.getRootCause(e); if (logException) { log.error(errorType + " at request " + req.getRequestURL(), rootCause); } else { log.warn("{} at request {}: {}", errorType, req.getRequestURL(), rootCause.toString()); } - return new ErrorInfo(req.getRequestURL(), errorType, rootCause.toString()); + return new ErrorInfo(req.getRequestURL(), errorType, + details.length != 0 ? details : new String[]{rootCause.toString()}); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index af1da813..8f705151 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -9,6 +9,7 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.to.MealTo; +import javax.validation.Valid; import java.net.URI; import java.time.LocalDate; import java.time.LocalTime; @@ -41,12 +42,12 @@ public List getAll() { @Override @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestBody Meal meal, @PathVariable int id) { + public void update(@Valid @RequestBody Meal meal, @PathVariable int id) { super.update(meal, id); } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity createWithLocation(@RequestBody Meal meal) { + public ResponseEntity createWithLocation(@Valid @RequestBody Meal meal) { Meal created = super.create(meal); URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java index 67715ce7..d2872269 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java @@ -2,13 +2,10 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; -import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.to.MealTo; -import ru.javawebinar.topjava.util.ValidationUtil; import javax.validation.Valid; import java.time.LocalDate; @@ -40,17 +37,12 @@ public void delete(@PathVariable int id) { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public ResponseEntity createOrUpdate(@Valid Meal meal, BindingResult result) { - if (result.hasErrors()) { - // TODO change to exception handler - return ValidationUtil.getErrorResponse(result); - } + public void createOrUpdate(@Valid Meal meal) { if (meal.isNew()) { super.create(meal); } else { super.update(meal, meal.getId()); } - return ResponseEntity.ok().build(); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index 7e932386..ba857ce7 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -7,6 +7,7 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; +import javax.validation.Valid; import java.net.URI; import java.util.List; @@ -29,7 +30,7 @@ public User get(@PathVariable int id) { } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity createWithLocation(@RequestBody User user) { + public ResponseEntity createWithLocation(@Valid @RequestBody User user) { User created = super.create(user); URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() .path(REST_URL + "/{id}") @@ -47,7 +48,7 @@ public void delete(@PathVariable int id) { @Override @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestBody User user, @PathVariable int id) { + public void update(@Valid @RequestBody User user, @PathVariable int id) { super.update(user, id); } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 0f521856..96f0fbc8 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -2,12 +2,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.ValidationUtil; import javax.validation.Valid; import java.util.List; @@ -37,17 +34,12 @@ public void delete(@PathVariable int id) { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { - if (result.hasErrors()) { - // TODO change to exception handler - return ValidationUtil.getErrorResponse(result); - } + public void createOrUpdate(@Valid UserTo userTo) { if (userTo.isNew()) { super.create(userTo); } else { super.update(userTo, userTo.id()); } - return ResponseEntity.ok().build(); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index eb24bc85..4bce9cee 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import javax.validation.Valid; import java.net.URI; import static ru.javawebinar.topjava.web.SecurityUtil.authUserId; @@ -30,7 +31,7 @@ public void delete() { @PostMapping(value = "/register", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity register(@RequestBody UserTo userTo) { + public ResponseEntity register(@Valid @RequestBody UserTo userTo) { User created = super.create(userTo); URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() .path(REST_URL).build().toUri(); @@ -39,7 +40,7 @@ public ResponseEntity register(@RequestBody UserTo userTo) { @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestBody UserTo userTo) { + public void update(@Valid @RequestBody UserTo userTo) { super.update(userTo, authUserId()); } diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index 71f5a71e..d3c9342a 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -98,7 +98,7 @@ function failNoty(jqXHR) { var errorInfo = jqXHR.responseJSON; failedNote = new Noty({ text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + - "
    " + errorInfo.type + "
    " + errorInfo.detail, + "
    " + errorInfo.type + "
    " + errorInfo.details.join("
    "), type: "error", layout: "bottomRight" }); diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java index d5062c4f..bfaf4a5e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -8,19 +8,20 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.util.exception.NotFoundException; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.MealTestData.getNew; +import static ru.javawebinar.topjava.MealTestData.getUpdated; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; -import static ru.javawebinar.topjava.UserTestData.USER_ID; -import static ru.javawebinar.topjava.UserTestData.user; +import static ru.javawebinar.topjava.UserTestData.*; import static ru.javawebinar.topjava.util.MealsUtil.createTo; import static ru.javawebinar.topjava.util.MealsUtil.getTos; @@ -124,4 +125,28 @@ void getBetweenAll() throws Exception { .andExpect(status().isOk()) .andExpect(MEAL_TO_MATCHER.contentJson(getTos(meals, user.getCaloriesPerDay()))); } + + @Test + void createInvalid() throws Exception { + Meal invalid = new Meal(null, null, "Dummy", 200); + perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(invalid)) + .with(userHttpBasic(admin))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } + + @Test + void updateInvalid() throws Exception { + Meal invalid = new Meal(MEAL1_ID, null, null, 6000); + perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(invalid)) + .with(userHttpBasic(user))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 3ee750f0..4d5a069a 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -5,17 +5,17 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.util.exception.NotFoundException; import ru.javawebinar.topjava.web.AbstractControllerTest; -import ru.javawebinar.topjava.web.json.JsonUtil; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; @@ -92,7 +92,7 @@ void update() throws Exception { perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) .contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(admin)) - .content(JsonUtil.writeValue(updated))) + .content(jsonWithPassword(updated, "newPass"))) .andExpect(status().isNoContent()); USER_MATCHER.assertMatch(userService.get(USER_ID), getUpdated()); @@ -145,4 +145,29 @@ void enable() throws Exception { assertFalse(userService.get(USER_ID).isEnabled()); } + + @Test + void createInvalid() throws Exception { + User invalid = new User(null, null, "", "newPass", 7300, Role.USER, Role.ADMIN); + perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(admin)) + .content(jsonWithPassword(invalid, "newPass"))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } + + @Test + void updateInvalid() throws Exception { + User invalid = new User(user); + invalid.setName(""); + perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) + .contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(admin)) + .content(jsonWithPassword(invalid, "password"))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 16b73247..3322fc07 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -9,12 +9,12 @@ import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; +import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; @@ -87,4 +87,27 @@ void getWithMeals() throws Exception { .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(USER_WITH_MEALS_MATCHER.contentJson(user)); } + + @Test + void registerInvalid() throws Exception { + UserTo newTo = new UserTo(null, null, null, null, 1); + perform(MockMvcRequestBuilders.post(REST_URL + "/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(newTo))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } + + @Test + void updateInvalid() throws Exception { + UserTo updatedTo = new UserTo(null, null, "password", null, 1500); + perform(MockMvcRequestBuilders.put(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(user)) + .content(JsonUtil.writeValue(updatedTo))) + .andDo(print()) + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + } } \ No newline at end of file From 28f72eb31a73ee6322cf96dcabd1d1103cc524a9 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:11:24 +0300 Subject: [PATCH 133/144] 11_03_HW10_duplicate_email.patch --- config/messages/app.properties | 4 +++- config/messages/app_ru.properties | 4 +++- .../topjava/util/ValidationUtil.java | 18 ++++++++++++++++- .../topjava/web/ExceptionInfoHandler.java | 11 +++------- .../topjava/web/GlobalExceptionHandler.java | 5 +++-- .../web/user/AbstractUserController.java | 2 ++ .../topjava/web/user/AdminUIController.java | 20 +++++++++++++++---- .../topjava/web/user/ProfileUIController.java | 14 +++++++++++-- 8 files changed, 59 insertions(+), 19 deletions(-) diff --git a/config/messages/app.properties b/config/messages/app.properties index 8987ce18..7653774a 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -43,4 +43,6 @@ common.errorStatus=Error status common.appError=Application error common.confirm=Are you sure? common.save=Save -common.cancel=Cancel \ No newline at end of file +common.cancel=Cancel + +exception.user.duplicateEmail=User with this email already exists \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index be85ad30..720ee2eb 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -43,4 +43,6 @@ common.errorStatus=Статус ошибки common.appError=Ошибка приложения common.confirm=Вы уверены? common.save=Сохранить -common.cancel=Отменить \ No newline at end of file +common.cancel=Отменить + +exception.user.duplicateEmail=Пользователь с такой почтой уже есть в приложении \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 17e897c4..855d3d6e 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -1,12 +1,14 @@ package ru.javawebinar.topjava.util; - +import org.slf4j.Logger; import org.springframework.core.NestedExceptionUtils; import org.springframework.lang.NonNull; import ru.javawebinar.topjava.HasId; +import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; import ru.javawebinar.topjava.util.exception.NotFoundException; +import javax.servlet.http.HttpServletRequest; import javax.validation.*; import java.util.Set; @@ -73,4 +75,18 @@ public static Throwable getRootCause(@NonNull Throwable t) { Throwable rootCause = NestedExceptionUtils.getRootCause(t); return rootCause != null ? rootCause : t; } + + public static String getMessage(Throwable e) { + return e.getLocalizedMessage() != null ? e.getLocalizedMessage() : e.getClass().getName(); + } + + public static Throwable logAndGetRootCause(Logger log, HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType) { + Throwable rootCause = ValidationUtil.getRootCause(e); + if (logStackTrace) { + log.error(errorType + " at request " + req.getRequestURL(), rootCause); + } else { + log.warn("{} at request {}: {}", errorType, req.getRequestURL(), rootCause.toString()); + } + return rootCause; + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index a5b8b770..641b3308 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -64,14 +64,9 @@ public ErrorInfo handleError(HttpServletRequest req, Exception e) { } // https://stackoverflow.com/questions/538870/should-private-helper-methods-be-static-if-they-can-be-static - private static ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, String... details) { - Throwable rootCause = ValidationUtil.getRootCause(e); - if (logException) { - log.error(errorType + " at request " + req.getRequestURL(), rootCause); - } else { - log.warn("{} at request {}: {}", errorType, req.getRequestURL(), rootCause.toString()); - } + private static ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType, String... details) { + Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logStackTrace, errorType); return new ErrorInfo(req.getRequestURL(), errorType, - details.length != 0 ? details : new String[]{rootCause.toString()}); + details.length != 0 ? details : new String[]{ValidationUtil.getMessage(rootCause)}); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java index bd401d1c..8e995ba2 100644 --- a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java @@ -8,6 +8,7 @@ import org.springframework.web.servlet.ModelAndView; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.util.ValidationUtil; +import ru.javawebinar.topjava.util.exception.ErrorType; import javax.servlet.http.HttpServletRequest; import java.util.Map; @@ -19,11 +20,11 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { log.error("Exception at request " + req.getRequestURL(), e); - Throwable rootCause = ValidationUtil.getRootCause(e); + Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, true, ErrorType.APP_ERROR); HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; ModelAndView mav = new ModelAndView("exception", - Map.of("exception", rootCause, "message", rootCause.toString(), "status", httpStatus)); + Map.of("exception", rootCause, "message", ValidationUtil.getMessage(rootCause), "status", httpStatus)); mav.setStatus(httpStatus); // Interceptor is not invoked, put userTo diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 532e1781..9d5169bf 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -16,6 +16,8 @@ public abstract class AbstractUserController { protected final Logger log = LoggerFactory.getLogger(getClass()); + public static final String EXCEPTION_DUPLICATE_EMAIL = "exception.user.duplicateEmail"; + @Autowired private UserService service; diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 96f0fbc8..7013bf83 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -1,10 +1,15 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; import javax.validation.Valid; import java.util.List; @@ -13,6 +18,9 @@ @RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) public class AdminUIController extends AbstractUserController { + @Autowired + private MessageSource messageSource; + @Override @GetMapping public List getAll() { @@ -35,10 +43,14 @@ public void delete(@PathVariable int id) { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void createOrUpdate(@Valid UserTo userTo) { - if (userTo.isNew()) { - super.create(userTo); - } else { - super.update(userTo, userTo.id()); + try { + if (userTo.isNew()) { + super.create(userTo); + } else { + super.update(userTo, userTo.id()); + } + } catch (DataIntegrityViolationException e) { + throw new IllegalRequestDataException(messageSource.getMessage(EXCEPTION_DUPLICATE_EMAIL, null, LocaleContextHolder.getLocale())); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index 657396a7..bd1a4238 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; @@ -25,11 +26,15 @@ public String profile() { public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionStatus status) { if (result.hasErrors()) { return "profile"; - } else { + } + try { super.update(userTo, SecurityUtil.authUserId()); SecurityUtil.get().setTo(userTo); status.setComplete(); return "redirect:/meals"; + } catch (DataIntegrityViolationException ex) { + result.rejectValue("email", EXCEPTION_DUPLICATE_EMAIL); + return "profile"; } } @@ -45,10 +50,15 @@ public String saveRegister(@Valid UserTo userTo, BindingResult result, SessionSt if (result.hasErrors()) { model.addAttribute("register", true); return "profile"; - } else { + } + try { super.create(userTo); status.setComplete(); return "redirect:/login?message=app.registered&username=" + userTo.getEmail(); + } catch (DataIntegrityViolationException ex) { + result.rejectValue("email", EXCEPTION_DUPLICATE_EMAIL); + model.addAttribute("register", true); + return "profile"; } } } \ No newline at end of file From 793f1148043f37290b3c08b7ffed5f192d16ac61 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:12:07 +0300 Subject: [PATCH 134/144] 11_04_HW10_duplicate_datetime.patch --- .travis.yml | 4 ++ config/messages/app.properties | 3 +- config/messages/app_ru.properties | 3 +- .../topjava/web/ExceptionInfoHandler.java | 26 +++++++++++- .../web/user/AbstractUserController.java | 2 - .../topjava/web/user/AdminUIController.java | 20 ++------- .../topjava/web/user/ProfileUIController.java | 2 + src/main/resources/spring/spring-mvc.xml | 4 ++ .../topjava/web/AbstractControllerTest.java | 26 ++++++++++-- .../web/meal/MealRestControllerTest.java | 41 +++++++++++++++++-- .../web/user/AdminRestControllerTest.java | 41 +++++++++++++++++-- .../web/user/ProfileRestControllerTest.java | 26 ++++++++++-- src/test/resources/spring/inmemory.xml | 3 +- 13 files changed, 164 insertions(+), 37 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2fda05fb..1d77f84f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,10 @@ branches: services: - postgresql +#https://docs.travis-ci.com/user/environment-variables#Default-Environment-Variables +env: +- TOPJAVA_ROOT=$TRAVIS_BUILD_DIR + # https://docs.travis-ci.com/user/notifications#Configuring-email-notifications #notifications: # email: false \ No newline at end of file diff --git a/config/messages/app.properties b/config/messages/app.properties index 7653774a..bffcdb98 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -45,4 +45,5 @@ common.confirm=Are you sure? common.save=Save common.cancel=Cancel -exception.user.duplicateEmail=User with this email already exists \ No newline at end of file +exception.user.duplicateEmail=User with this email already exists +exception.meal.duplicateDateTime=You already have meal with this date/time \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 720ee2eb..94c0a2af 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -45,4 +45,5 @@ common.confirm=Вы уверены? common.save=Сохранить common.cancel=Отменить -exception.user.duplicateEmail=Пользователь с такой почтой уже есть в приложении \ No newline at end of file +exception.user.duplicateEmail=Пользователь с такой почтой уже есть в приложении +exception.meal.duplicateDateTime=У вас уже есть еда с такой датой/временем \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 641b3308..9445371c 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.dao.DataIntegrityViolationException; @@ -20,13 +21,27 @@ import ru.javawebinar.topjava.util.exception.NotFoundException; import javax.servlet.http.HttpServletRequest; +import java.util.Map; import static ru.javawebinar.topjava.util.exception.ErrorType.*; @RestControllerAdvice(annotations = RestController.class) @Order(Ordered.HIGHEST_PRECEDENCE + 5) public class ExceptionInfoHandler { - private static Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); + private static final Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); + + public static final String EXCEPTION_DUPLICATE_EMAIL = "exception.user.duplicateEmail"; + public static final String EXCEPTION_DUPLICATE_DATETIME = "exception.meal.duplicateDateTime"; + + private static final Map CONSTRAINS_I18N_MAP = Map.of( + "users_unique_email_idx", EXCEPTION_DUPLICATE_EMAIL, + "meals_unique_user_datetime_idx", EXCEPTION_DUPLICATE_DATETIME); + + private final MessageSourceAccessor messageSourceAccessor; + + public ExceptionInfoHandler(MessageSourceAccessor messageSourceAccessor) { + this.messageSourceAccessor = messageSourceAccessor; + } // http://stackoverflow.com/a/22358422/548473 @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @@ -38,6 +53,15 @@ public ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { @ResponseStatus(HttpStatus.CONFLICT) // 409 @ExceptionHandler(DataIntegrityViolationException.class) public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { + String rootMsg = ValidationUtil.getRootCause(e).getMessage(); + if (rootMsg != null) { + String lowerCaseMsg = rootMsg.toLowerCase(); + for (Map.Entry entry : CONSTRAINS_I18N_MAP.entrySet()) { + if (lowerCaseMsg.contains(entry.getKey())) { + return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, messageSourceAccessor.getMessage(entry.getValue())); + } + } + } return logAndGetErrorInfo(req, e, true, DATA_ERROR); } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 9d5169bf..532e1781 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -16,8 +16,6 @@ public abstract class AbstractUserController { protected final Logger log = LoggerFactory.getLogger(getClass()); - public static final String EXCEPTION_DUPLICATE_EMAIL = "exception.user.duplicateEmail"; - @Autowired private UserService service; diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 7013bf83..96f0fbc8 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -1,15 +1,10 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; import javax.validation.Valid; import java.util.List; @@ -18,9 +13,6 @@ @RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) public class AdminUIController extends AbstractUserController { - @Autowired - private MessageSource messageSource; - @Override @GetMapping public List getAll() { @@ -43,14 +35,10 @@ public void delete(@PathVariable int id) { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void createOrUpdate(@Valid UserTo userTo) { - try { - if (userTo.isNew()) { - super.create(userTo); - } else { - super.update(userTo, userTo.id()); - } - } catch (DataIntegrityViolationException e) { - throw new IllegalRequestDataException(messageSource.getMessage(EXCEPTION_DUPLICATE_EMAIL, null, LocaleContextHolder.getLocale())); + if (userTo.isNew()) { + super.create(userTo); + } else { + super.update(userTo, userTo.id()); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index bd1a4238..9e2e2555 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -13,6 +13,8 @@ import javax.validation.Valid; +import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; + @Controller @RequestMapping("/profile") public class ProfileUIController extends AbstractUserController { diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index a0364ba7..0fd634be 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -70,6 +70,10 @@ + + + + diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java index 94078e4c..8cc91f1b 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -2,11 +2,13 @@ import org.junit.jupiter.api.Assumptions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.env.Environment; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; @@ -14,10 +16,13 @@ import org.springframework.web.filter.CharacterEncodingFilter; import ru.javawebinar.topjava.AllActiveProfileResolver; import ru.javawebinar.topjava.Profiles; +import ru.javawebinar.topjava.util.exception.ErrorType; import javax.annotation.PostConstruct; +import java.util.Locale; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @SpringJUnitWebConfig(locations = { "classpath:spring/spring-app.xml", @@ -32,9 +37,6 @@ public abstract class AbstractControllerTest { private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); - @Autowired - public Environment env; - static { CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); CHARACTER_ENCODING_FILTER.setForceEncoding(true); @@ -42,9 +44,15 @@ public abstract class AbstractControllerTest { private MockMvc mockMvc; + @Autowired + public Environment env; + @Autowired private WebApplicationContext webApplicationContext; + @Autowired + protected MessageSourceAccessor messageSourceAccessor; + public void assumeDataJpa() { Assumptions.assumeTrue(env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.DATAJPA)), "DATA-JPA only"); } @@ -61,4 +69,16 @@ private void postConstruct() { protected ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception { return mockMvc.perform(builder); } + + private String getMessage(String code) { + return messageSourceAccessor.getMessage(code, Locale.ENGLISH); + } + + public ResultMatcher errorType(ErrorType type) { + return jsonPath("$.type").value(type.name()); + } + + public ResultMatcher detailMessage(String code) { + return jsonPath("$.details").value(getMessage(code)); + } } diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java index bfaf4a5e..73540576 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -6,16 +6,18 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.service.MealService; -import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.util.exception.NotFoundException; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static ru.javawebinar.topjava.MealTestData.getNew; import static ru.javawebinar.topjava.MealTestData.getUpdated; import static ru.javawebinar.topjava.MealTestData.*; @@ -24,6 +26,8 @@ import static ru.javawebinar.topjava.UserTestData.*; import static ru.javawebinar.topjava.util.MealsUtil.createTo; import static ru.javawebinar.topjava.util.MealsUtil.getTos; +import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; +import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_DATETIME; class MealRestControllerTest extends AbstractControllerTest { @@ -135,7 +139,7 @@ void createInvalid() throws Exception { .with(userHttpBasic(admin))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); } @Test @@ -147,6 +151,35 @@ void updateInvalid() throws Exception { .with(userHttpBasic(user))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); + } + + @Test + @Transactional(propagation = Propagation.NEVER) + void updateDuplicate() throws Exception { + Meal invalid = new Meal(MEAL1_ID, meal2.getDateTime(), "Dummy", 200); + + perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(invalid)) + .with(userHttpBasic(user))) + .andDo(print()) + .andExpect(status().isConflict()) + .andExpect(errorType(VALIDATION_ERROR)) + .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); + } + + @Test + @Transactional(propagation = Propagation.NEVER) + void createDuplicate() throws Exception { + Meal invalid = new Meal(null, adminMeal1.getDateTime(), "Dummy", 200); + perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(invalid)) + .with(userHttpBasic(admin))) + .andDo(print()) + .andExpect(status().isConflict()) + .andExpect(errorType(VALIDATION_ERROR)) + .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 4d5a069a..bc1c2056 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -5,20 +5,24 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; -import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.util.exception.NotFoundException; import ru.javawebinar.topjava.web.AbstractControllerTest; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; +import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; +import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; class AdminRestControllerTest extends AbstractControllerTest { @@ -155,7 +159,7 @@ void createInvalid() throws Exception { .content(jsonWithPassword(invalid, "newPass"))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); } @Test @@ -168,6 +172,35 @@ void updateInvalid() throws Exception { .content(jsonWithPassword(invalid, "password"))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); + } + + @Test + @Transactional(propagation = Propagation.NEVER) + void updateDuplicate() throws Exception { + User updated = new User(user); + updated.setEmail("admin@gmail.com"); + perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) + .contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(admin)) + .content(jsonWithPassword(updated, "password"))) + .andDo(print()) + .andExpect(status().isConflict()) + .andExpect(errorType(VALIDATION_ERROR)) + .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); + } + + @Test + @Transactional(propagation = Propagation.NEVER) + void createDuplicate() throws Exception { + User expected = new User(null, "New", "user@yandex.ru", "newPass", 2300, Role.USER, Role.ADMIN); + perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(admin)) + .content(jsonWithPassword(expected, "newPass"))) + .andDo(print()) + .andExpect(status().isConflict()) + .andExpect(errorType(VALIDATION_ERROR)) + .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 3322fc07..a9be9183 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -5,19 +5,23 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; -import ru.javawebinar.topjava.util.exception.ErrorType; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static ru.javawebinar.topjava.TestUtil.readFromJson; import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; +import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; +import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; import static ru.javawebinar.topjava.web.user.ProfileRestController.REST_URL; class ProfileRestControllerTest extends AbstractControllerTest { @@ -96,7 +100,7 @@ void registerInvalid() throws Exception { .content(JsonUtil.writeValue(newTo))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); } @Test @@ -108,6 +112,20 @@ void updateInvalid() throws Exception { .content(JsonUtil.writeValue(updatedTo))) .andDo(print()) .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.type").value(ErrorType.VALIDATION_ERROR.name())); + .andExpect(errorType(VALIDATION_ERROR)); + } + + @Test + @Transactional(propagation = Propagation.NEVER) + void updateDuplicate() throws Exception { + UserTo updatedTo = new UserTo(null, "newName", "admin@gmail.com", "newPassword", 1500); + + perform(MockMvcRequestBuilders.put(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(user)) + .content(JsonUtil.writeValue(updatedTo))) + .andDo(print()) + .andExpect(status().isConflict()) + .andExpect(errorType(VALIDATION_ERROR)) + .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); } } \ No newline at end of file diff --git a/src/test/resources/spring/inmemory.xml b/src/test/resources/spring/inmemory.xml index f7e2dbbd..1207399d 100644 --- a/src/test/resources/spring/inmemory.xml +++ b/src/test/resources/spring/inmemory.xml @@ -5,6 +5,7 @@ - + + \ No newline at end of file From 5fc2a936ae60584e25fa5cdaa13e4a2a3bc0bd4b Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:13:10 +0300 Subject: [PATCH 135/144] 11_05_HW10_binder_validation.patch --- .../ru/javawebinar/topjava/HasIdAndEmail.java | 5 +++ .../ru/javawebinar/topjava/model/User.java | 4 +- .../ru/javawebinar/topjava/to/UserTo.java | 4 +- .../topjava/web/ExceptionInfoHandler.java | 2 +- .../web/user/AbstractUserController.java | 10 +++++ .../topjava/web/user/ProfileUIController.java | 28 ++++---------- .../topjava/web/user/UniqueMailValidator.java | 37 +++++++++++++++++++ src/main/webapp/WEB-INF/jsp/profile.jsp | 1 + .../ru/javawebinar/topjava/UserTestData.java | 3 ++ .../web/user/AdminRestControllerTest.java | 4 +- .../web/user/ProfileRestControllerTest.java | 2 +- 11 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java diff --git a/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java b/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java new file mode 100644 index 00000000..6389876b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java @@ -0,0 +1,5 @@ +package ru.javawebinar.topjava; + +public interface HasIdAndEmail extends HasId { + String getEmail(); +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index f109845f..f8c2612b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -17,6 +17,7 @@ import org.hibernate.annotations.Cache; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; +import ru.javawebinar.topjava.HasIdAndEmail; import javax.persistence.*; import java.util.*; @@ -31,7 +32,7 @@ }) @Entity @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) -public class User extends AbstractNamedEntity { +public class User extends AbstractNamedEntity implements HasIdAndEmail { public static final String DELETE = "User.delete"; public static final String BY_EMAIL = "User.getByEmail"; @@ -100,6 +101,7 @@ public User(Integer id, String name, String email, String password, int calories setRoles(roles); } + @Override public String getEmail() { return email; } diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index bfbb05f8..a2d508a7 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.to; import org.hibernate.validator.constraints.Range; +import ru.javawebinar.topjava.HasIdAndEmail; import ru.javawebinar.topjava.util.UserUtil; import javax.validation.constraints.Email; @@ -10,7 +11,7 @@ import java.io.Serial; import java.io.Serializable; -public class UserTo extends BaseTo implements Serializable { +public class UserTo extends BaseTo implements HasIdAndEmail, Serializable { @Serial private static final long serialVersionUID = 1L; @@ -58,6 +59,7 @@ public void setName(String name) { this.name = name; } + @Override public String getEmail() { return email; } diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 9445371c..beb8d257 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -69,7 +69,7 @@ public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExceptio @ExceptionHandler(BindException.class) public ErrorInfo bindValidationError(HttpServletRequest req, BindException e) { String[] details = e.getBindingResult().getFieldErrors().stream() - .map(fe -> String.format("[%s] %s", fe.getField(), fe.getDefaultMessage())) + .map(messageSourceAccessor::getMessage) .toArray(String[]::new); return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, details); diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 532e1781..cc1c7fac 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -3,6 +3,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.to.UserTo; @@ -19,6 +21,14 @@ public abstract class AbstractUserController { @Autowired private UserService service; + @Autowired + private UniqueMailValidator emailValidator; + + @InitBinder + protected void initBinder(WebDataBinder binder) { + binder.addValidators(emailValidator); + } + public List getAll() { log.info("getAll"); return service.getAll(); diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index 9e2e2555..8091371b 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; @@ -13,8 +12,6 @@ import javax.validation.Valid; -import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; - @Controller @RequestMapping("/profile") public class ProfileUIController extends AbstractUserController { @@ -29,15 +26,10 @@ public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionS if (result.hasErrors()) { return "profile"; } - try { - super.update(userTo, SecurityUtil.authUserId()); - SecurityUtil.get().setTo(userTo); - status.setComplete(); - return "redirect:/meals"; - } catch (DataIntegrityViolationException ex) { - result.rejectValue("email", EXCEPTION_DUPLICATE_EMAIL); - return "profile"; - } + super.update(userTo, SecurityUtil.authUserId()); + SecurityUtil.get().setTo(userTo); + status.setComplete(); + return "redirect:/meals"; } @GetMapping("/register") @@ -53,14 +45,8 @@ public String saveRegister(@Valid UserTo userTo, BindingResult result, SessionSt model.addAttribute("register", true); return "profile"; } - try { - super.create(userTo); - status.setComplete(); - return "redirect:/login?message=app.registered&username=" + userTo.getEmail(); - } catch (DataIntegrityViolationException ex) { - result.rejectValue("email", EXCEPTION_DUPLICATE_EMAIL); - model.addAttribute("register", true); - return "profile"; - } + super.create(userTo); + status.setComplete(); + return "redirect:/login?message=app.registered&username=" + userTo.getEmail(); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java b/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java new file mode 100644 index 00000000..9fbb6882 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java @@ -0,0 +1,37 @@ +package ru.javawebinar.topjava.web.user; + + +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.validation.Errors; +import ru.javawebinar.topjava.HasIdAndEmail; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; + + +@Component +public class UniqueMailValidator implements org.springframework.validation.Validator { + + private final UserRepository repository; + + public UniqueMailValidator(UserRepository repository) { + this.repository = repository; + } + + @Override + public boolean supports(Class clazz) { + return HasIdAndEmail.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + HasIdAndEmail user = ((HasIdAndEmail) target); + if (StringUtils.hasText(user.getEmail())) { + User dbUser = repository.getByEmail(user.getEmail().toLowerCase()); + if (dbUser != null && !dbUser.getId().equals(user.getId())) { + errors.rejectValue("email", ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL); + } + } + } +} diff --git a/src/main/webapp/WEB-INF/jsp/profile.jsp b/src/main/webapp/WEB-INF/jsp/profile.jsp index c6d2df55..9b6f6af1 100644 --- a/src/main/webapp/WEB-INF/jsp/profile.jsp +++ b/src/main/webapp/WEB-INF/jsp/profile.jsp @@ -18,6 +18,7 @@ + diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 989852b3..32d6cc07 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -41,7 +41,10 @@ public static User getNew() { public static User getUpdated() { User updated = new User(user); + +// TODO comment for fail AdminRestControllerTest.update updated.setEmail("update@gmail.com"); + updated.setName("UpdatedName"); updated.setCaloriesPerDay(330); updated.setPassword("newPass"); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index bc1c2056..88bd6e50 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -185,7 +185,7 @@ void updateDuplicate() throws Exception { .with(userHttpBasic(admin)) .content(jsonWithPassword(updated, "password"))) .andDo(print()) - .andExpect(status().isConflict()) + .andExpect(status().isUnprocessableEntity()) .andExpect(errorType(VALIDATION_ERROR)) .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); } @@ -199,7 +199,7 @@ void createDuplicate() throws Exception { .with(userHttpBasic(admin)) .content(jsonWithPassword(expected, "newPass"))) .andDo(print()) - .andExpect(status().isConflict()) + .andExpect(status().isUnprocessableEntity()) .andExpect(errorType(VALIDATION_ERROR)) .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); } diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index a9be9183..38a3f9ed 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -124,7 +124,7 @@ void updateDuplicate() throws Exception { .with(userHttpBasic(user)) .content(JsonUtil.writeValue(updatedTo))) .andDo(print()) - .andExpect(status().isConflict()) + .andExpect(status().isUnprocessableEntity()) .andExpect(errorType(VALIDATION_ERROR)) .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); } From 2380053ffdb0c0dd89b9f1d205bdc4bd582c7d90 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:13:32 +0300 Subject: [PATCH 136/144] 11_06_HW10_manual_binder_validation.patch --- .../web/user/AbstractUserController.java | 28 +++++++++++++------ .../topjava/web/user/AdminRestController.java | 8 ++++-- .../web/user/ProfileRestController.java | 4 ++- src/main/resources/spring/spring-mvc.xml | 3 ++ .../ru/javawebinar/topjava/UserTestData.java | 5 +--- .../web/user/AdminRestControllerTest.java | 1 + .../web/user/ProfileRestControllerTest.java | 2 +- src/test/resources/spring/inmemory.xml | 1 + 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index cc1c7fac..c882cf0e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -3,8 +3,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.validation.BindException; +import org.springframework.validation.DataBinder; +import org.springframework.validation.Validator; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; +import ru.javawebinar.topjava.HasId; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.to.UserTo; @@ -19,11 +24,15 @@ public abstract class AbstractUserController { protected final Logger log = LoggerFactory.getLogger(getClass()); @Autowired - private UserService service; + protected UserService service; @Autowired private UniqueMailValidator emailValidator; + @Autowired + @Qualifier("defaultValidator") + private Validator validator; + @InitBinder protected void initBinder(WebDataBinder binder) { binder.addValidators(emailValidator); @@ -56,15 +65,8 @@ public void delete(int id) { service.delete(id); } - public void update(User user, int id) { - log.info("update {} with id={}", user, id); - assureIdConsistent(user, id); - service.update(user); - } - public void update(UserTo userTo, int id) { log.info("update {} with id={}", userTo, id); - assureIdConsistent(userTo, id); service.update(userTo); } @@ -82,4 +84,14 @@ public void enable(int id, boolean enabled) { log.info(enabled ? "enable {}" : "disable {}", id); service.enable(id, enabled); } + + protected void validateBeforeUpdate(HasId user, int id) throws BindException { + assureIdConsistent(user, id); + DataBinder binder = new DataBinder(user); + binder.addValidators(emailValidator, validator); + binder.validate(); + if (binder.getBindingResult().hasErrors()) { + throw new BindException(binder.getBindingResult()); + } + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index ba857ce7..c06e582f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; @@ -45,11 +46,12 @@ public void delete(@PathVariable int id) { super.delete(id); } - @Override @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@Valid @RequestBody User user, @PathVariable int id) { - super.update(user, id); + public void update(@RequestBody User user, @PathVariable int id) throws BindException { + validateBeforeUpdate(user, id); + log.info("update {} with id={}", user, id); + service.update(user); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 4bce9cee..5cd976be 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; @@ -40,7 +41,8 @@ public ResponseEntity register(@Valid @RequestBody UserTo userTo) { @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@Valid @RequestBody UserTo userTo) { + public void update(@RequestBody UserTo userTo) throws BindException { + validateBeforeUpdate(userTo, authUserId()); super.update(userTo, authUserId()); } diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 0fd634be..7372c925 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -77,4 +77,7 @@ + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 32d6cc07..e88210bb 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -41,10 +41,7 @@ public static User getNew() { public static User getUpdated() { User updated = new User(user); - -// TODO comment for fail AdminRestControllerTest.update - updated.setEmail("update@gmail.com"); - +// updated.setEmail("update@gmail.com"); updated.setName("UpdatedName"); updated.setCaloriesPerDay(330); updated.setPassword("newPass"); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 88bd6e50..af277921 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -97,6 +97,7 @@ void update() throws Exception { .contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(admin)) .content(jsonWithPassword(updated, "newPass"))) + .andDo(print()) .andExpect(status().isNoContent()); USER_MATCHER.assertMatch(userService.get(USER_ID), getUpdated()); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 38a3f9ed..f2a19ccc 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -71,7 +71,7 @@ void register() throws Exception { @Test void update() throws Exception { - UserTo updatedTo = new UserTo(null, "newName", "newemail@ya.ru", "newPassword", 1500); + UserTo updatedTo = new UserTo(null, "newName", "user@yandex.ru", "newPassword", 1500); perform(MockMvcRequestBuilders.put(REST_URL).contentType(MediaType.APPLICATION_JSON) .with(userHttpBasic(user)) .content(JsonUtil.writeValue(updatedTo))) diff --git a/src/test/resources/spring/inmemory.xml b/src/test/resources/spring/inmemory.xml index 1207399d..5ad23949 100644 --- a/src/test/resources/spring/inmemory.xml +++ b/src/test/resources/spring/inmemory.xml @@ -8,4 +8,5 @@ + \ No newline at end of file From aecbd9ff38be96137c3211e1be32ddc7feb1b516 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:14:20 +0300 Subject: [PATCH 137/144] 11_07_HW10_change_locale.patch --- src/main/resources/spring/spring-mvc.xml | 8 +++ .../WEB-INF/jsp/fragments/bodyHeader.jsp | 63 ++++++++++++------- src/main/webapp/resources/js/topjava.meals.js | 2 + .../topjava/web/AbstractControllerTest.java | 4 +- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 7372c925..28ba4cc1 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -74,8 +74,16 @@ + + + + + + + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 62f05f9a..73616fce 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -3,28 +3,49 @@ <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -
    diff --git a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp index 3e2ad09b..ac1c7187 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp @@ -8,7 +8,7 @@ i18n["addTitle"] = ''; i18n["editTitle"] = ''; - + i18n['${key}'] = ''; \ No newline at end of file diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css index 6c383472..85452748 100644 --- a/src/main/webapp/resources/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -32,6 +32,11 @@ tr[data-userEnabled="false"] { border: 1px solid #9feba6; } +/*https://stackoverflow.com/a/53855189/548473*/ +#noty_layout__bottomRight { + width: 385px !important; +} + /*https://getbootstrap.com/docs/4.0/examples/sticky-footer/sticky-footer.css*/ html { position: relative; diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index d3c9342a..edd5d527 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -10,7 +10,10 @@ function makeEditable(datatableOpts) { "dataSrc": "" }, "paging": false, - "info": true + "info": true, + "language": { + "search": i18n["common.search"] + } } )); @@ -97,8 +100,7 @@ function failNoty(jqXHR) { closeNoty(); var errorInfo = jqXHR.responseJSON; failedNote = new Noty({ - text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + - "
    " + errorInfo.type + "
    " + errorInfo.details.join("
    "), + text: "  " + errorInfo.typeMessage + "
    " + errorInfo.details.join("
    "), type: "error", layout: "bottomRight" }); From b87eb95f2867ee87e154a188ff3be3cd353e0f8d Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 18:01:45 +0300 Subject: [PATCH 139/144] 11_09_404_status.patch --- config/messages/app.properties | 1 + config/messages/app_ru.properties | 1 + .../topjava/util/exception/ErrorType.java | 3 ++- .../topjava/web/GlobalExceptionHandler.java | 15 ++++++++++++--- src/main/resources/spring/spring-mvc.xml | 3 ++- src/main/webapp/WEB-INF/web.xml | 4 ++++ src/main/webapp/test.html | 13 ------------- 7 files changed, 22 insertions(+), 18 deletions(-) delete mode 100644 src/main/webapp/test.html diff --git a/config/messages/app.properties b/config/messages/app.properties index f1fd782b..9d03fd63 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -56,6 +56,7 @@ error.appError=Application error error.dataNotFound=Data not found error.dataError=Data error error.validationError=Validation error +error.wrongRequest=Wrong request NotEmpty=[{0}] must not be empty NotBlank=[{0}] must not be empty diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 370f2c88..f596d5c4 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -56,6 +56,7 @@ error.appError=Ошибка приложения error.dataNotFound=Данные не найдены error.dataError=Ошибка в данных error.validationError=Ошибка проверки данных +error.wrongRequest=Неверный запрос NotEmpty=[{0}] не должно быть пустым NotBlank=[{0}] не должно быть пустым diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java index cb231e1e..a3fa60db 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java @@ -4,7 +4,8 @@ public enum ErrorType { APP_ERROR("error.appError"), DATA_NOT_FOUND("error.dataNotFound"), DATA_ERROR("error.dataError"), - VALIDATION_ERROR("error.validationError"); + VALIDATION_ERROR("error.validationError"), + WRONG_REQUEST("error.wrongRequest"); private final String errorCode; diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java index 91467987..9afba9d1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.NoHandlerFoundException; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.util.ValidationUtil; import ru.javawebinar.topjava.util.exception.ErrorType; @@ -24,15 +25,23 @@ public GlobalExceptionHandler(MessageSourceAccessor messageSourceAccessor) { this.messageSourceAccessor = messageSourceAccessor; } + @ExceptionHandler(NoHandlerFoundException.class) + public ModelAndView wrongRequest(HttpServletRequest req, NoHandlerFoundException e) throws Exception { + return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST, HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { log.error("Exception at request " + req.getRequestURL(), e); - Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, true, ErrorType.APP_ERROR); + return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR, HttpStatus.INTERNAL_SERVER_ERROR); + } + + private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, HttpStatus httpStatus) { + Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logException, errorType); - HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; ModelAndView mav = new ModelAndView("exception", Map.of("exception", rootCause, "message", ValidationUtil.getMessage(rootCause), - "typeMessage", messageSourceAccessor.getMessage(ErrorType.APP_ERROR.getErrorCode()), + "typeMessage", messageSourceAccessor.getMessage(errorType.getErrorCode()), "status", httpStatus)); mav.setStatus(httpStatus); diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 28ba4cc1..fa157157 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -11,7 +11,8 @@ - + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index c80a34ec..834c2657 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -30,6 +30,10 @@ contextConfigLocation classpath:spring/spring-mvc.xml + + throwExceptionIfNoHandlerFound + true + 1 diff --git a/src/main/webapp/test.html b/src/main/webapp/test.html deleted file mode 100644 index e50b3327..00000000 --- a/src/main/webapp/test.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - -
    - -
    - - \ No newline at end of file From 3045f274b20546cd63161d2d3464121f14a3da3a Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 18:04:10 +0300 Subject: [PATCH 140/144] 11_10_auth_user.patch --- .../topjava/web/GlobalExceptionHandler.java | 7 ------ .../web/interceptor/ModelInterceptor.java | 25 ------------------- .../web/user/ProfileRestController.java | 22 ++++++++-------- .../topjava/web/user/ProfileUIController.java | 12 +++++---- src/main/resources/spring/spring-mvc.xml | 5 ++-- .../WEB-INF/jsp/fragments/bodyHeader.jsp | 2 +- 6 files changed, 22 insertions(+), 51 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java index 9afba9d1..a3df96ae 100644 --- a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java @@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; -import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.util.ValidationUtil; import ru.javawebinar.topjava.util.exception.ErrorType; @@ -44,12 +43,6 @@ private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, "typeMessage", messageSourceAccessor.getMessage(errorType.getErrorCode()), "status", httpStatus)); mav.setStatus(httpStatus); - - // Interceptor is not invoked, put userTo - AuthorizedUser authorizedUser = SecurityUtil.safeGet(); - if (authorizedUser != null) { - mav.addObject("userTo", authorizedUser.getUserTo()); - } return mav; } } diff --git a/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java b/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java deleted file mode 100644 index 4ee6eaef..00000000 --- a/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.javawebinar.topjava.web.interceptor; - -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; -import ru.javawebinar.topjava.AuthorizedUser; -import ru.javawebinar.topjava.web.SecurityUtil; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * This interceptor adds userTo to the model of every requests - */ -public class ModelInterceptor implements HandlerInterceptor { - - @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { - if (modelAndView != null && !modelAndView.isEmpty()) { - AuthorizedUser authorizedUser = SecurityUtil.safeGet(); - if (authorizedUser != null) { - modelAndView.getModelMap().addAttribute("userTo", authorizedUser.getUserTo()); - } - } - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 5cd976be..314349c0 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -3,31 +3,31 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; import javax.validation.Valid; import java.net.URI; -import static ru.javawebinar.topjava.web.SecurityUtil.authUserId; - @RestController @RequestMapping(value = ProfileRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class ProfileRestController extends AbstractUserController { static final String REST_URL = "/rest/profile"; @GetMapping - public User get() { - return super.get(authUserId()); + public User get(@AuthenticationPrincipal AuthorizedUser authUser) { + return super.get(authUser.getId()); } @DeleteMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete() { - super.delete(authUserId()); + public void delete(@AuthenticationPrincipal AuthorizedUser authUser) { + super.delete(authUser.getId()); } @PostMapping(value = "/register", consumes = MediaType.APPLICATION_JSON_VALUE) @@ -41,9 +41,9 @@ public ResponseEntity register(@Valid @RequestBody UserTo userTo) { @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestBody UserTo userTo) throws BindException { - validateBeforeUpdate(userTo, authUserId()); - super.update(userTo, authUserId()); + public void update(@RequestBody UserTo userTo, @AuthenticationPrincipal AuthorizedUser authUser) throws BindException { + validateBeforeUpdate(userTo, authUser.getId()); + super.update(userTo, authUser.getId()); } @GetMapping("/text") @@ -52,7 +52,7 @@ public String testUTF() { } @GetMapping("/with-meals") - public User getWithMeals() { - return super.getWithMeals(authUserId()); + public User getWithMeals(@AuthenticationPrincipal AuthorizedUser authUser) { + return super.getWithMeals(authUser.getId()); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index 8091371b..608cb2eb 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; @@ -7,8 +8,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.support.SessionStatus; +import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.web.SecurityUtil; import javax.validation.Valid; @@ -17,17 +18,18 @@ public class ProfileUIController extends AbstractUserController { @GetMapping - public String profile() { + public String profile(ModelMap model, @AuthenticationPrincipal AuthorizedUser authUser) { + model.addAttribute("userTo", authUser.getUserTo()); return "profile"; } @PostMapping - public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionStatus status) { + public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionStatus status, @AuthenticationPrincipal AuthorizedUser authUser) { if (result.hasErrors()) { return "profile"; } - super.update(userTo, SecurityUtil.authUserId()); - SecurityUtil.get().setTo(userTo); + super.update(userTo, authUser.getId()); + authUser.setTo(userTo); status.setComplete(); return "redirect:/meals"; } diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index fa157157..0bc2d2e0 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -30,6 +30,9 @@
    + + + @@ -80,8 +83,6 @@ - - diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 73616fce..b355dc25 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -19,7 +19,7 @@ - ${userTo.name} + From c5ae2f28e19d1205f8a86ecd182b2d4dcdb00804 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 18:05:56 +0300 Subject: [PATCH 141/144] 11_11_error_status.patch --- .../topjava/util/exception/ErrorType.java | 23 ++++++++++----- .../topjava/web/ExceptionInfoHandler.java | 29 ++++++++----------- .../topjava/web/GlobalExceptionHandler.java | 11 ++++--- .../web/meal/MealRestControllerTest.java | 4 +-- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java index a3fa60db..5c16cf8f 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java @@ -1,19 +1,28 @@ package ru.javawebinar.topjava.util.exception; +import org.springframework.http.HttpStatus; + public enum ErrorType { - APP_ERROR("error.appError"), - DATA_NOT_FOUND("error.dataNotFound"), - DATA_ERROR("error.dataError"), - VALIDATION_ERROR("error.validationError"), - WRONG_REQUEST("error.wrongRequest"); + APP_ERROR("error.appError", HttpStatus.INTERNAL_SERVER_ERROR), + // http://stackoverflow.com/a/22358422/548473 + DATA_NOT_FOUND("error.dataNotFound", HttpStatus.UNPROCESSABLE_ENTITY), + DATA_ERROR("error.dataError", HttpStatus.CONFLICT), + VALIDATION_ERROR("error.validationError", HttpStatus.UNPROCESSABLE_ENTITY), + WRONG_REQUEST("error.wrongRequest", HttpStatus.BAD_REQUEST); private final String errorCode; + private final HttpStatus status; - ErrorType(String errorCode) { + ErrorType(String errorCode, HttpStatus status) { this.errorCode = errorCode; + this.status = status; } public String getErrorCode() { return errorCode; } -} + + public HttpStatus getStatus() { + return status; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 541344b9..0a9474db 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -6,11 +6,10 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; @@ -43,16 +42,13 @@ public ExceptionInfoHandler(MessageSourceAccessor messageSourceAccessor) { this.messageSourceAccessor = messageSourceAccessor; } - // http://stackoverflow.com/a/22358422/548473 - @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @ExceptionHandler(NotFoundException.class) - public ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { + public ResponseEntity handleError(HttpServletRequest req, NotFoundException e) { return logAndGetErrorInfo(req, e, false, DATA_NOT_FOUND); } - @ResponseStatus(HttpStatus.CONFLICT) // 409 @ExceptionHandler(DataIntegrityViolationException.class) - public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { + public ResponseEntity conflict(HttpServletRequest req, DataIntegrityViolationException e) { String rootMsg = ValidationUtil.getRootCause(e).getMessage(); if (rootMsg != null) { String lowerCaseMsg = rootMsg.toLowerCase(); @@ -65,9 +61,8 @@ public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExceptio return logAndGetErrorInfo(req, e, true, DATA_ERROR); } - @ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY) // 422 @ExceptionHandler(BindException.class) - public ErrorInfo bindValidationError(HttpServletRequest req, BindException e) { + public ResponseEntity bindValidationError(HttpServletRequest req, BindException e) { String[] details = e.getBindingResult().getFieldErrors().stream() .map(messageSourceAccessor::getMessage) .toArray(String[]::new); @@ -75,23 +70,23 @@ public ErrorInfo bindValidationError(HttpServletRequest req, BindException e) { return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, details); } - @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) // 422 @ExceptionHandler({IllegalRequestDataException.class, MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class}) - public ErrorInfo illegalRequestDataError(HttpServletRequest req, Exception e) { + public ResponseEntity illegalRequestDataError(HttpServletRequest req, Exception e) { return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR); } - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) - public ErrorInfo handleError(HttpServletRequest req, Exception e) { + public ResponseEntity handleError(HttpServletRequest req, Exception e) { return logAndGetErrorInfo(req, e, true, APP_ERROR); } // https://stackoverflow.com/questions/538870/should-private-helper-methods-be-static-if-they-can-be-static - private ErrorInfo logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType, String... details) { + private ResponseEntity logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType, String... details) { Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logStackTrace, errorType); - return new ErrorInfo(req.getRequestURL(), errorType, - messageSourceAccessor.getMessage(errorType.getErrorCode()), - details.length != 0 ? details : new String[]{ValidationUtil.getMessage(rootCause)}); + return ResponseEntity.status(errorType.getStatus()) + .body(new ErrorInfo(req.getRequestURL(), errorType, + messageSourceAccessor.getMessage(errorType.getErrorCode()), + details.length != 0 ? details : new String[]{ValidationUtil.getMessage(rootCause)}) + ); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java index a3df96ae..4f2a423f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java @@ -3,7 +3,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; @@ -26,23 +25,23 @@ public GlobalExceptionHandler(MessageSourceAccessor messageSourceAccessor) { @ExceptionHandler(NoHandlerFoundException.class) public ModelAndView wrongRequest(HttpServletRequest req, NoHandlerFoundException e) throws Exception { - return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST, HttpStatus.BAD_REQUEST); + return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST); } @ExceptionHandler(Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { log.error("Exception at request " + req.getRequestURL(), e); - return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR, HttpStatus.INTERNAL_SERVER_ERROR); + return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR); } - private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, HttpStatus httpStatus) { + private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType) { Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logException, errorType); ModelAndView mav = new ModelAndView("exception", Map.of("exception", rootCause, "message", ValidationUtil.getMessage(rootCause), "typeMessage", messageSourceAccessor.getMessage(errorType.getErrorCode()), - "status", httpStatus)); - mav.setStatus(httpStatus); + "status", errorType.getStatus())); + mav.setStatus(errorType.getStatus()); return mav; } } diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java index 73540576..4b5b42f6 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java @@ -164,7 +164,7 @@ void updateDuplicate() throws Exception { .content(JsonUtil.writeValue(invalid)) .with(userHttpBasic(user))) .andDo(print()) - .andExpect(status().isConflict()) + .andExpect(status().isUnprocessableEntity()) .andExpect(errorType(VALIDATION_ERROR)) .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); } @@ -178,7 +178,7 @@ void createDuplicate() throws Exception { .content(JsonUtil.writeValue(invalid)) .with(userHttpBasic(admin))) .andDo(print()) - .andExpect(status().isConflict()) + .andExpect(status().isUnprocessableEntity()) .andExpect(errorType(VALIDATION_ERROR)) .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); } From a77927423e35ce0a3dc867d4e830cb0594f4ed69 Mon Sep 17 00:00:00 2001 From: source-store <73580046+source-store@users.noreply.github.com> Date: Tue, 27 Apr 2021 18:08:30 +0300 Subject: [PATCH 142/144] 11_12_swagger2.patch --- pom.xml | 11 +++++++++++ .../ru/javawebinar/topjava/web/RootController.java | 2 ++ .../topjava/web/meal/MealUIController.java | 4 +++- .../topjava/web/user/AdminUIController.java | 2 ++ .../topjava/web/user/ProfileRestController.java | 9 +++++---- .../topjava/web/user/ProfileUIController.java | 2 ++ src/main/resources/spring/spring-mvc.xml | 4 ++++ src/main/resources/spring/spring-security.xml | 5 +++++ src/main/webapp/WEB-INF/jsp/login.jsp | 4 +++- 9 files changed, 37 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 933b1415..83ba93fd 100644 --- a/pom.xml +++ b/pom.xml @@ -151,6 +151,17 @@ ${spring-data-jpa.version} + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + org.springframework.security diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index fd1f6d6f..5db6d7fb 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -3,7 +3,9 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import springfox.documentation.annotations.ApiIgnore; +@ApiIgnore @Controller public class RootController { diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java index d2872269..762067f3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java @@ -6,12 +6,14 @@ import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.to.MealTo; +import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; +@ApiIgnore @RestController @RequestMapping(value = "/profile/meals", produces = MediaType.APPLICATION_JSON_VALUE) public class MealUIController extends AbstractMealController { @@ -23,7 +25,7 @@ public List getAll() { } @Override - @GetMapping( "/{id}") + @GetMapping("/{id}") public Meal get(@PathVariable int id) { return super.get(id); } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index 96f0fbc8..b0093fc8 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -5,10 +5,12 @@ import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.util.List; +@ApiIgnore @RestController @RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) public class AdminUIController extends AbstractUserController { diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index 314349c0..d0a70bc6 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -10,6 +10,7 @@ import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.net.URI; @@ -20,13 +21,13 @@ public class ProfileRestController extends AbstractUserController { static final String REST_URL = "/rest/profile"; @GetMapping - public User get(@AuthenticationPrincipal AuthorizedUser authUser) { + public User get(@AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) { return super.get(authUser.getId()); } @DeleteMapping @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@AuthenticationPrincipal AuthorizedUser authUser) { + public void delete(@AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) { super.delete(authUser.getId()); } @@ -41,7 +42,7 @@ public ResponseEntity register(@Valid @RequestBody UserTo userTo) { @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@RequestBody UserTo userTo, @AuthenticationPrincipal AuthorizedUser authUser) throws BindException { + public void update(@RequestBody UserTo userTo, @AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) throws BindException { validateBeforeUpdate(userTo, authUser.getId()); super.update(userTo, authUser.getId()); } @@ -52,7 +53,7 @@ public String testUTF() { } @GetMapping("/with-meals") - public User getWithMeals(@AuthenticationPrincipal AuthorizedUser authUser) { + public User getWithMeals(@AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) { return super.getWithMeals(authUser.getId()); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java index 608cb2eb..fbfa06a6 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java @@ -10,9 +10,11 @@ import org.springframework.web.bind.support.SessionStatus; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.to.UserTo; +import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; +@ApiIgnore @Controller @RequestMapping("/profile") public class ProfileUIController extends AbstractUserController { diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 0bc2d2e0..651c5270 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -52,6 +52,10 @@ + + + + diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index e3c3a864..066769bf 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -19,6 +19,11 @@ + + + + + diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp index 7f29ad7a..1944ccca 100644 --- a/src/main/webapp/WEB-INF/jsp/login.jsp +++ b/src/main/webapp/WEB-INF/jsp/login.jsp @@ -54,7 +54,9 @@ -
    +