From f7da3b72e28d59c4e1232af306812fb0476382f1 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Sat, 3 Dec 2016 23:06:00 +0300 Subject: [PATCH 01/63] Prepare_to_HW0 --- .../javawebinar/topjava/model/UserMeal.java | 33 +++++++++++++++++ .../topjava/model/UserMealWithExceed.java | 24 +++++++++++++ .../ru/javawebinar/topjava/util/TimeUtil.java | 13 +++++++ .../topjava/util/UserMealsUtil.java | 35 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/model/UserMeal.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/TimeUtil.java create 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/UserMeal.java new file mode 100644 index 00000000..aec9beb9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -0,0 +1,33 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +/** + * GKislin + * 11.01.2015. + */ +public class UserMeal { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + public UserMeal(LocalDateTime dateTime, String description, int calories) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + } + + public LocalDateTime getDateTime() { + return dateTime; + } + + public String getDescription() { + return description; + } + + public int getCalories() { + return calories; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java new file mode 100644 index 00000000..f4f880e4 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +/** + * GKislin + * 11.01.2015. + */ +public class UserMealWithExceed { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + private final boolean exceed; + + public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + this.exceed = exceed; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java new file mode 100644 index 00000000..02399b72 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava.util; + +import java.time.LocalTime; + +/** + * GKislin + * 07.01.2015. + */ +public class TimeUtil { + public static boolean isBetween(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/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java new file mode 100644 index 00000000..8c5f1dc6 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -0,0 +1,35 @@ +package ru.javawebinar.topjava.util; + +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.model.UserMealWithExceed; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.util.Arrays; +import java.util.List; + +/** + * GKislin + * 31.05.2015. + */ +public class UserMealsUtil { + public static void main(String[] args) { + List mealList = Arrays.asList( + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,10,0), "Завтрак", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,13,0), "Обед", 1000), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,20,0), "Ужин", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,10,0), "Завтрак", 1000), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) + ); + getFilteredWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); +// .toLocalDate(); +// .toLocalTime(); + } + + public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + // TODO return filtered list with correctly exceeded field + return null; + } +} From 824d20581824786c26efaa9ca15eac2a31ef2372 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:20:24 +0300 Subject: [PATCH 02/63] 1_1_HW0_stream --- .gitignore | 4 +- .../model/{UserMeal.java => Meal.java} | 14 +++++- .../topjava/model/MealWithExceed.java | 34 ++++++++++++++ .../topjava/model/UserMealWithExceed.java | 24 ---------- .../javawebinar/topjava/util/MealsUtil.java | 47 +++++++++++++++++++ .../topjava/util/UserMealsUtil.java | 35 -------------- 6 files changed, 94 insertions(+), 64 deletions(-) rename src/main/java/ru/javawebinar/topjava/model/{UserMeal.java => Meal.java} (63%) create mode 100644 src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java delete mode 100644 src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java 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/.gitignore b/.gitignore index 6ca05c4a..c7757bd0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ out target *.iml log - - - +*.patch diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java similarity index 63% rename from src/main/java/ru/javawebinar/topjava/model/UserMeal.java rename to src/main/java/ru/javawebinar/topjava/model/Meal.java index aec9beb9..006d5cac 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,19 +1,21 @@ package ru.javawebinar.topjava.model; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; /** * GKislin * 11.01.2015. */ -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; @@ -30,4 +32,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/model/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java new file mode 100644 index 00000000..d14128b1 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java @@ -0,0 +1,34 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +/** + * GKislin + * 11.01.2015. + */ +public class MealWithExceed { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + private final boolean exceed; + + public MealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + this.exceed = exceed; + } + + @Override + public String toString() { + return "UserMealWithExceed{" + + "dateTime=" + dateTime + + ", description='" + description + '\'' + + ", calories=" + calories + + ", exceed=" + exceed + + '}'; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java deleted file mode 100644 index f4f880e4..00000000 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.javawebinar.topjava.model; - -import java.time.LocalDateTime; - -/** - * GKislin - * 11.01.2015. - */ -public class UserMealWithExceed { - private final LocalDateTime dateTime; - - private final String description; - - private final int calories; - - private final boolean exceed; - - public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { - this.dateTime = dateTime; - this.description = description; - this.calories = calories; - this.exceed = exceed; - } -} 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..2bbfa96f --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -0,0 +1,47 @@ +package ru.javawebinar.topjava.util; + +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.model.MealWithExceed; + +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; + +/** + * GKislin + * 31.05.2015. + */ +public class MealsUtil { + public static void main(String[] args) { + List meals = Arrays.asList( + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) + ); + List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + mealsWithExceeded.forEach(System.out::println); + } + + public static List getFilteredWithExceeded(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.isBetween(meal.getTime(), startTime, endTime)) + .map(meal -> + new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), + caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .collect(Collectors.toList()); + } +} \ No newline at end of file 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 8c5f1dc6..00000000 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.javawebinar.topjava.util; - -import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.model.UserMealWithExceed; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.util.Arrays; -import java.util.List; - -/** - * GKislin - * 31.05.2015. - */ -public class UserMealsUtil { - public static void main(String[] args) { - List mealList = Arrays.asList( - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,10,0), "Завтрак", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,13,0), "Обед", 1000), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,20,0), "Ужин", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,10,0), "Завтрак", 1000), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) - ); - getFilteredWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); -// .toLocalDate(); -// .toLocalTime(); - } - - public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO return filtered list with correctly exceeded field - return null; - } -} From 4ef3c8ae73698f9c3eee8d0dd1f852ba43542252 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:21:20 +0300 Subject: [PATCH 03/63] 1_2_HW0_cycle --- .../javawebinar/topjava/util/MealsUtil.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 2bbfa96f..d6476fc0 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -7,9 +7,7 @@ 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.*; import java.util.stream.Collectors; /** @@ -28,6 +26,8 @@ public static void main(String[] args) { ); List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); mealsWithExceeded.forEach(System.out::println); + + System.out.println(getFilteredWithExceededByCycle(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); } public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { @@ -39,9 +39,25 @@ public static List getFilteredWithExceeded(List meals, Loc return meals.stream() .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) - .map(meal -> - new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), - caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) .collect(Collectors.toList()); } + + public static List getFilteredWithExceededByCycle(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + + final Map caloriesSumByDate = new HashMap<>(); + meals.forEach(meal -> caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum)); + + final List mealsWithExceeded = new ArrayList<>(); + meals.forEach(meal -> { + if (TimeUtil.isBetween(meal.getTime(), startTime, endTime)) { + mealsWithExceeded.add(createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)); + } + }); + return mealsWithExceeded; + } + + public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { + return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); + } } \ No newline at end of file From 4c30a62a0a4c3761e3022d052275593995d6a497 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:29:54 +0300 Subject: [PATCH 04/63] 1_3_switch_to_war --- pom.xml | 2 +- src/main/webapp/WEB-INF/web.xml | 18 ++++++++++++++++++ src/main/webapp/index.html | 14 ++++++++++++++ src/main/webapp/users.jsp | 10 ++++++++++ 4 files changed, 43 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 c8a1c78f..6f0e7fd7 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..f346a5ef --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,18 @@ + + 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..2a1536b6 --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,14 @@ + + + + + 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..fbd40d3d --- /dev/null +++ b/src/main/webapp/users.jsp @@ -0,0 +1,10 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + User list + + +

Home

+

User list

+ + From 88f210720a8d08855d8ef6fcb7cdbfc7c79dc3aa Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:35:14 +0300 Subject: [PATCH 05/63] 1_4_add_servlet_api --- pom.xml | 8 ++++++++ .../javawebinar/topjava/web/UserServlet.java | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/web/UserServlet.java diff --git a/pom.xml b/pom.xml index 6f0e7fd7..1eae3f7c 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,14 @@ + + + + javax.servlet + javax.servlet-api + 3.1.0 + 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..e2220411 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -0,0 +1,18 @@ +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; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class UserServlet extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getRequestDispatcher("/users.jsp").forward(request, response); + } +} From 1e9266396f95dcd1bde8ba50b0871d71298736ba Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:45:34 +0300 Subject: [PATCH 06/63] 1_5_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 1eae3f7c..61991334 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 e2220411..1e11c615 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -13,6 +13,7 @@ public class UserServlet extends HttpServlet { 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 e9de9f4ed8202fc9c5745483fb975609b82cee19 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:51:24 +0300 Subject: [PATCH 07/63] 1_6_logging --- pom.xml | 25 ++++++++++++++ .../javawebinar/topjava/web/UserServlet.java | 8 +++++ src/main/resources/logback.xml | 33 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index 61991334..204e2d01 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,10 @@ 1.8 UTF-8 UTF-8 + + + 1.1.7 + 1.7.21 @@ -34,6 +38,27 @@ + + + org.slf4j + slf4j-api + ${slf4j.version} + compile + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + + ch.qos.logback + logback-classic + ${logback.version} + runtime + diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 1e11c615..3371178a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,18 +1,26 @@ 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; + + /** * User: gkislin * Date: 19.08.2014 */ public class UserServlet extends HttpServlet { + private static final Logger LOG = getLogger(UserServlet.class); 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..8ee5f21b --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,33 @@ + + + + + true + + + + + + + ${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 96feeaa1afa8f73d6ff1a5e71729c5140a546307 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 8 Dec 2016 00:56:29 +0300 Subject: [PATCH 08/63] 1_7_remote_jmx --- config/setenv.bat | 4 ++++ config/setenv.sh | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 config/setenv.bat create mode 100644 config/setenv.sh diff --git a/config/setenv.bat b/config/setenv.bat new file mode 100644 index 00000000..ebbd6593 --- /dev/null +++ b/config/setenv.bat @@ -0,0 +1,4 @@ +rem run tomcat with JMX ability +rem Run Tomcat as admin +rem for remote connection add -Djava.rmi.server.hostname=TomcatServer_IP +set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false diff --git a/config/setenv.sh b/config/setenv.sh new file mode 100644 index 00000000..e7986cf3 --- /dev/null +++ b/config/setenv.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# run tomcat with JMX ability as admin +# for remote connection add -Djava.rmi.server.hostname=TomcatServer_IP +export CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" \ No newline at end of file From b1f78d47bc99a68034d52c169225bb7d0b5cf897 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 01:28:54 +0300 Subject: [PATCH 09/63] 2_1_HW1 --- pom.xml | 6 +++ .../topjava/model/MealWithExceed.java | 16 +++++++ .../topjava/util/DateTimeUtil.java | 21 +++++++++ .../javawebinar/topjava/util/MealsUtil.java | 31 ++++++++----- .../ru/javawebinar/topjava/util/TimeUtil.java | 13 ------ .../javawebinar/topjava/web/MealServlet.java | 26 +++++++++++ .../javawebinar/topjava/web/UserServlet.java | 7 ++- 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/meals.jsp | 46 +++++++++++++++++++ 11 files changed, 164 insertions(+), 29 deletions(-) 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/web/MealServlet.java create mode 100644 src/main/webapp/WEB-INF/tld/functions.tld create mode 100644 src/main/webapp/meals.jsp diff --git a/pom.xml b/pom.xml index 204e2d01..e04a647e 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,12 @@ 3.1.0 provided + + + javax.servlet + jstl + 1.2 + diff --git a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java index d14128b1..ca27ce04 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java @@ -22,6 +22,22 @@ public MealWithExceed(LocalDateTime dateTime, String description, int calories, this.exceed = exceed; } + public LocalDateTime getDateTime() { + return dateTime; + } + + public String getDescription() { + return description; + } + + public int getCalories() { + return calories; + } + + public boolean isExceed() { + return exceed; + } + @Override public String toString() { return "UserMealWithExceed{" + 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..1a0629a9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -0,0 +1,21 @@ +package ru.javawebinar.topjava.util; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * GKislin + * 07.01.2015. + */ +public class DateTimeUtil { + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + + public static boolean isBetween(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 d6476fc0..6f518ddd 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -15,19 +15,26 @@ * 31.05.2015. */ public class MealsUtil { + public static final List MEALS = Arrays.asList( + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) + ); + + public static final int DEFAULT_CALORIES_PER_DAY = 2000; + public static void main(String[] args) { - List meals = Arrays.asList( - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) - ); - List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + List mealsWithExceeded = getFilteredWithExceeded(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); mealsWithExceeded.forEach(System.out::println); - System.out.println(getFilteredWithExceededByCycle(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + System.out.println(getFilteredWithExceededByCycle(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY)); + } + + public static List getWithExceeded(List meals, int caloriesPerDay) { + return getFilteredWithExceeded(meals, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); } public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { @@ -38,7 +45,7 @@ public static List getFilteredWithExceeded(List meals, Loc ); return meals.stream() - .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) + .filter(meal -> DateTimeUtil.isBetween(meal.getTime(), startTime, endTime)) .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) .collect(Collectors.toList()); } @@ -50,7 +57,7 @@ public static List getFilteredWithExceededByCycle(List mea final List mealsWithExceeded = new ArrayList<>(); meals.forEach(meal -> { - if (TimeUtil.isBetween(meal.getTime(), startTime, endTime)) { + if (DateTimeUtil.isBetween(meal.getTime(), startTime, endTime)) { mealsWithExceeded.add(createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)); } }); 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 02399b72..00000000 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.javawebinar.topjava.util; - -import java.time.LocalTime; - -/** - * GKislin - * 07.01.2015. - */ -public class TimeUtil { - public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; - } -} 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..1bcbbeaa --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class MealServlet extends HttpServlet { + private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + LOG.info("getAll"); + request.setAttribute("meals", MealsUtil.getWithExceeded(MealsUtil.MEALS, MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 3371178a..07ede01d 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -18,10 +18,9 @@ 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"); + LOG.debug("forward to users"); + request.getRequestDispatcher("/users.jsp").forward(request, response); } } 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 f346a5ef..686b79f8 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -15,4 +15,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 2a1536b6..56c6dc68 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -9,6 +9,7 @@

Проект User List +
  • Meal List
  • diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp new file mode 100644 index 00000000..03e2e8b6 --- /dev/null +++ b/src/main/webapp/meals.jsp @@ -0,0 +1,46 @@ +<%@ 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" %> + + + Meal list + + + +
    +

    Home

    +

    Meal list

    +
    + + + + + + + + + + + + + + + + +
    DateDescriptionCalories
    + <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> + <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> + ${fn:formatDateTime(meal.dateTime)} + ${meal.description}${meal.calories}
    +
    + + \ No newline at end of file From c58050213408e42abf31ff07483908d1134bb632 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 01:32:28 +0300 Subject: [PATCH 10/63] 2_2_HW1_optional --- .../ru/javawebinar/topjava/model/Meal.java | 29 +++++++++ .../topjava/model/MealWithExceed.java | 14 ++++- .../InMemoryMealRepositoryImpl.java | 47 ++++++++++++++ .../topjava/repository/MealRepository.java | 19 ++++++ .../javawebinar/topjava/util/MealsUtil.java | 6 +- .../javawebinar/topjava/web/MealServlet.java | 61 +++++++++++++++++-- src/main/webapp/meal.jsp | 51 ++++++++++++++++ src/main/webapp/meals.jsp | 5 ++ 8 files changed, 222 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/MealRepository.java create mode 100644 src/main/webapp/meal.jsp diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 006d5cac..d2434144 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -9,6 +9,8 @@ * 11.01.2015. */ public class Meal { + private Integer id; + private final LocalDateTime dateTime; private final String description; @@ -16,11 +18,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; } @@ -40,4 +55,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/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java index ca27ce04..204c177e 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java @@ -7,6 +7,8 @@ * 11.01.2015. */ public class MealWithExceed { + private final Integer id; + private final LocalDateTime dateTime; private final String description; @@ -15,13 +17,18 @@ public class MealWithExceed { private final boolean exceed; - public MealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + public MealWithExceed(Integer id, LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.id = id; this.dateTime = dateTime; this.description = description; this.calories = calories; this.exceed = exceed; } + public Integer getId() { + return id; + } + public LocalDateTime getDateTime() { return dateTime; } @@ -40,8 +47,9 @@ public boolean isExceed() { @Override public String toString() { - return "UserMealWithExceed{" + - "dateTime=" + dateTime + + return "MealWithExceed{" + + "id=" + id + + ", dateTime=" + dateTime + ", description='" + description + '\'' + ", calories=" + calories + ", exceed=" + exceed + diff --git a/src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java new file mode 100644 index 00000000..20ca8168 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java @@ -0,0 +1,47 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.Meal; +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; + +/** + * GKislin + * 15.09.2015. + */ +public class InMemoryMealRepositoryImpl implements MealRepository { + private Map repository = new ConcurrentHashMap<>(); + private 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; + } + + @Override + public void delete(int id) { + repository.remove(id); + } + + @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/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java new file mode 100644 index 00000000..f80eec99 --- /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; + +/** + * GKislin + * 06.03.2015. + */ +public interface MealRepository { + Meal save(Meal Meal); + + void delete(int id); + + Meal get(int id); + + Collection getAll(); +} diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 6f518ddd..bceab4d1 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -33,11 +33,11 @@ public static void main(String[] args) { System.out.println(getFilteredWithExceededByCycle(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY)); } - public static List getWithExceeded(List meals, int caloriesPerDay) { + public static List getWithExceeded(Collection meals, int caloriesPerDay) { return getFilteredWithExceeded(meals, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); } - public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + public static List getFilteredWithExceeded(Collection meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { Map caloriesSumByDate = meals.stream() .collect( Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) @@ -65,6 +65,6 @@ public static List getFilteredWithExceededByCycle(List mea } public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { - return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); + return new MealWithExceed(meal.getId(), meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); } } \ 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 1bcbbeaa..a1c8e92f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,13 +2,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.InMemoryMealRepositoryImpl; +import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; +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 java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; /** * User: gkislin @@ -17,10 +24,56 @@ public class MealServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); + private MealRepository repository; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + repository = new InMemoryMealRepositoryImpl(); + } + + @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.valueOf(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 { - LOG.info("getAll"); - request.setAttribute("meals", MealsUtil.getWithExceeded(MealsUtil.MEALS, MealsUtil.DEFAULT_CALORIES_PER_DAY)); - request.getRequestDispatcher("/meals.jsp").forward(request, response); + String action = request.getParameter("action"); + + if (action == null) { + LOG.info("getAll"); + request.setAttribute("meals", + MealsUtil.getWithExceeded(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + + } else if ("delete".equals(action)) { + int id = getId(request); + LOG.info("Delete {}", id); + repository.delete(id); + response.sendRedirect("meals"); + + } else if ("create".equals(action) || "update".equals(action)) { + final Meal meal = action.equals("create") ? + new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : + repository.get(getId(request)); + request.setAttribute("meal", meal); + request.getRequestDispatcher("meal.jsp").forward(request, response); + } + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.valueOf(paramId); } -} +} \ No newline at end of file diff --git a/src/main/webapp/meal.jsp b/src/main/webapp/meal.jsp new file mode 100644 index 00000000..7541b419 --- /dev/null +++ b/src/main/webapp/meal.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 index 03e2e8b6..45b47090 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -19,6 +19,7 @@

    Home

    Meal list

    + Add Meal
    @@ -26,6 +27,8 @@ + + @@ -38,6 +41,8 @@ + +
    Date Description Calories
    ${meal.description} ${meal.calories}UpdateDelete
    From 43042f8569808e67cf5f28ea58afb118eeff08ee Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 01:50:27 +0300 Subject: [PATCH 11/63] 2_3_app_layers --- .../javawebinar/topjava/AuthorizedUser.java | 18 ++++ .../javawebinar/topjava/model/BaseEntity.java | 28 ++++++ .../topjava/model/NamedEntity.java | 31 ++++++ .../ru/javawebinar/topjava/model/Role.java | 10 ++ .../ru/javawebinar/topjava/model/User.java | 98 +++++++++++++++++++ .../topjava/repository/UserRepository.java | 24 +++++ .../topjava/service/MealService.java | 8 ++ .../topjava/service/MealServiceImpl.java | 13 +++ .../topjava/service/UserService.java | 26 +++++ .../topjava/service/UserServiceImpl.java | 49 ++++++++++ .../topjava/util/ValidationUtil.java | 27 +++++ .../util/exception/NotFoundException.java | 11 +++ .../topjava/web/meal/MealRestController.java | 12 +++ .../web/user/AbstractUserController.java | 49 ++++++++++ .../topjava/web/user/AdminRestController.java | 42 ++++++++ .../web/user/ProfileRestController.java | 23 +++++ 16 files changed, 469 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/AuthorizedUser.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/BaseEntity.java create mode 100644 src/main/java/ru/javawebinar/topjava/model/NamedEntity.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/UserRepository.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/MealService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.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/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 diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java new file mode 100644 index 00000000..7d936562 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -0,0 +1,18 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.util.MealsUtil; + +/** + * GKislin + * 06.03.2015. + */ +public class AuthorizedUser { + + public static int id() { + return 1; + } + + public static int getCaloriesPerDay() { + return MealsUtil.DEFAULT_CALORIES_PER_DAY; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java new file mode 100644 index 00000000..60f4b46f --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -0,0 +1,28 @@ +package ru.javawebinar.topjava.model; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class BaseEntity { + protected Integer id; + + public BaseEntity() { + } + + protected BaseEntity(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); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java new file mode 100644 index 00000000..ca92e394 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java @@ -0,0 +1,31 @@ +package ru.javawebinar.topjava.model; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class NamedEntity extends BaseEntity { + + protected String name; + + public NamedEntity() { + } + + protected NamedEntity(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 name; + } +} 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..f0de2b4c --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/Role.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.model; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public enum Role { + ROLE_USER, + ROLE_ADMIN +} 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..4d0842a9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -0,0 +1,98 @@ +package ru.javawebinar.topjava.model; + +import ru.javawebinar.topjava.util.MealsUtil; + +import java.util.Date; +import java.util.EnumSet; +import java.util.Set; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class User extends NamedEntity { + + private String email; + + private String password; + + private boolean enabled = true; + + private Date registered = new Date(); + + private Set roles; + + private int caloriesPerDay = MealsUtil.DEFAULT_CALORIES_PER_DAY; + + public User() { + } + + public User(Integer id, String name, String email, String password, Role role, Role... roles) { + this(id, name, email, password, MealsUtil.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 + + ')'; + } +} 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..24c1c45b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public interface UserRepository { + 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(); +} 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..0a0ba9b9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -0,0 +1,8 @@ +package ru.javawebinar.topjava.service; + +/** + * GKislin + * 15.06.2015. + */ +public interface MealService { +} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java new file mode 100644 index 00000000..6e00e324 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.repository.MealRepository; + +/** + * GKislin + * 06.03.2015. + */ +public class MealServiceImpl implements MealService { + + private MealRepository repository; + +} 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..85c15e48 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava.service; + + +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public interface UserService { + + User save(User user); + + void delete(int id) throws NotFoundException; + + User get(int id) throws NotFoundException; + + User getByEmail(String email) throws NotFoundException; + + List getAll(); + + void update(User user); +} diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java new file mode 100644 index 00000000..5945fc88 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -0,0 +1,49 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + +/** + * GKislin + * 06.03.2015. + */ +public class UserServiceImpl implements UserService { + + private UserRepository repository; + + @Override + public User save(User user) { + return repository.save(user); + } + + @Override + public void delete(int id) { + checkNotFoundWithId(repository.delete(id), id); + } + + @Override + public User get(int id) throws NotFoundException { + return checkNotFoundWithId(repository.get(id), id); + } + + @Override + public User getByEmail(String email) throws NotFoundException { + return checkNotFound(repository.getByEmail(email), "email=" + email); + } + + @Override + public List getAll() { + return repository.getAll(); + } + + @Override + public void update(User user) { + repository.save(user); + } +} 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..8fd3c9a5 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -0,0 +1,27 @@ +package ru.javawebinar.topjava.util; + + +import ru.javawebinar.topjava.util.exception.NotFoundException; + +/** + * User: gkislin + * Date: 14.05.2014 + */ +public class ValidationUtil { + public static void checkNotFoundWithId(boolean found, int id) { + checkNotFound(found, "id=" + id); + } + + public static T checkNotFoundWithId(T object, int id) { + return checkNotFound(object, "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); + } +} 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..7a770f0d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -0,0 +1,11 @@ +package ru.javawebinar.topjava.util.exception; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} 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..a9ab9e30 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -0,0 +1,12 @@ +package ru.javawebinar.topjava.web.meal; + +import ru.javawebinar.topjava.service.MealService; + +/** + * GKislin + * 06.03.2015. + */ +public class MealRestController { + private MealService service; + +} 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..aac911f5 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -0,0 +1,49 @@ +package ru.javawebinar.topjava.web.user; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; + +import java.util.List; + +/** + * User: gkislin + */ +public abstract class AbstractUserController { + protected final Logger LOG = LoggerFactory.getLogger(getClass()); + + 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) { + user.setId(null); + LOG.info("create " + user); + return service.save(user); + } + + public void delete(int id) { + LOG.info("delete " + id); + service.delete(id); + } + + public void update(User user, int id) { + user.setId(id); + LOG.info("update " + user); + service.update(user); + } + + public User getByMail(String email) { + LOG.info("getByEmail " + email); + return service.getByEmail(email); + } +} 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..e769e41b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava.web.user; + +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +/** + * GKislin + * 06.03.2015. + */ +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); + } +} 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..eb0c8d9d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -0,0 +1,23 @@ +package ru.javawebinar.topjava.web.user; + +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.model.User; + +/** + * GKislin + * 06.03.2015. + */ +public class ProfileRestController extends AbstractUserController { + + public User get() { + return super.get(AuthorizedUser.id()); + } + + public void delete() { + super.delete(AuthorizedUser.id()); + } + + public void update(User user) { + super.update(user, AuthorizedUser.id()); + } +} \ No newline at end of file From 0d1d95f74241a97d02b338674a4b53647ccc5bd2 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 01:52:53 +0300 Subject: [PATCH 12/63] 2_4_add_spring_context --- pom.xml | 15 ++++++ .../ru/javawebinar/topjava/SpringMain.java | 23 +++++++++ .../InMemoryMealRepositoryImpl.java | 3 +- .../mock/MockUserRepositoryImpl.java | 47 +++++++++++++++++++ .../javawebinar/topjava/web/MealServlet.java | 2 +- src/main/resources/spring/spring-app.xml | 7 +++ 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/SpringMain.java rename src/main/java/ru/javawebinar/topjava/repository/{ => mock}/InMemoryMealRepositoryImpl.java (90%) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java create mode 100644 src/main/resources/spring/spring-app.xml diff --git a/pom.xml b/pom.xml index e04a647e..0ae808eb 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ UTF-8 UTF-8 + 4.3.4.RELEASE + 1.1.7 1.7.21 @@ -60,6 +62,19 @@ runtime + + + org.springframework + spring-context + ${spring.version} + + + commons-logging + commons-logging + + + + javax.servlet 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..e5b040c4 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -0,0 +1,23 @@ +package ru.javawebinar.topjava; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Arrays; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class SpringMain { + public static void main(String[] args) { + ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); + +// UserRepository userRepository = (UserRepository) appCtx.getBean("mockUserRepository"); + UserRepository userRepository = appCtx.getBean(UserRepository.class); + userRepository.getAll(); + appCtx.close(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java similarity index 90% rename from src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java rename to src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index 20ca8168..f6ead517 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/InMemoryMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -1,6 +1,7 @@ -package ru.javawebinar.topjava.repository; +package ru.javawebinar.topjava.repository.mock; import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; import java.util.Collection; diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java new file mode 100644 index 00000000..8a014233 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java @@ -0,0 +1,47 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Collections; +import java.util.List; + +/** + * GKislin + * 15.06.2015. + */ +public class MockUserRepositoryImpl implements UserRepository { + private static final Logger LOG = LoggerFactory.getLogger(MockUserRepositoryImpl.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/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index a1c8e92f..63fc920a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -3,7 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.InMemoryMealRepositoryImpl; +import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml new file mode 100644 index 00000000..e2e1f031 --- /dev/null +++ b/src/main/resources/spring/spring-app.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From b493a9e7eb7b11b2f82a34d6b90c849b59038656 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 01:56:11 +0300 Subject: [PATCH 13/63] 2_5_add_dependency_injection --- src/main/java/ru/javawebinar/topjava/SpringMain.java | 7 +++++++ .../ru/javawebinar/topjava/service/UserServiceImpl.java | 4 ++++ src/main/resources/spring/spring-app.xml | 3 +++ 3 files changed, 14 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index e5b040c4..c6613bf8 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -2,7 +2,10 @@ 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.repository.UserRepository; +import ru.javawebinar.topjava.service.UserService; import java.util.Arrays; @@ -18,6 +21,10 @@ public static void main(String[] args) { // UserRepository userRepository = (UserRepository) appCtx.getBean("mockUserRepository"); UserRepository userRepository = appCtx.getBean(UserRepository.class); userRepository.getAll(); + + UserService userService = appCtx.getBean(UserService.class); + userService.save(new User(1, "userName", "email", "password", Role.ROLE_ADMIN)); + appCtx.close(); } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index 5945fc88..44a1e796 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -17,6 +17,10 @@ public class UserServiceImpl implements UserService { private UserRepository repository; + public void setRepository(UserRepository repository) { + this.repository = repository; + } + @Override public User save(User user) { return repository.save(user); diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index e2e1f031..7b4fa168 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -4,4 +4,7 @@ + + + \ No newline at end of file From fa61c51e10fed00c81f29cbc5071266d32d50cef Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 14 Dec 2016 02:00:01 +0300 Subject: [PATCH 14/63] 2_6_add_annotation_processing --- .../ru/javawebinar/topjava/SpringMain.java | 20 ++++++---------- .../mock/MockUserRepositoryImpl.java | 2 ++ .../topjava/service/UserServiceImpl.java | 8 +++---- .../web/user/AbstractUserController.java | 2 ++ .../topjava/web/user/AdminRestController.java | 2 ++ .../web/user/ProfileRestController.java | 2 ++ src/main/resources/spring/spring-app.xml | 23 +++++++++++++++---- 7 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index c6613bf8..92a53add 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -4,8 +4,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.UserRepository; -import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.web.user.AdminRestController; import java.util.Arrays; @@ -15,16 +14,11 @@ */ public class SpringMain { public static void main(String[] args) { - ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); - System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); - -// UserRepository userRepository = (UserRepository) appCtx.getBean("mockUserRepository"); - UserRepository userRepository = appCtx.getBean(UserRepository.class); - userRepository.getAll(); - - UserService userService = appCtx.getBean(UserService.class); - userService.save(new User(1, "userName", "email", "password", Role.ROLE_ADMIN)); - - appCtx.close(); + // java 7 Automatic resource management + 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(1, "userName", "email", "password", Role.ROLE_ADMIN)); + } } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java index 8a014233..932f7c44 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java @@ -2,6 +2,7 @@ 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; @@ -12,6 +13,7 @@ * GKislin * 15.06.2015. */ +@Repository public class MockUserRepositoryImpl implements UserRepository { private static final Logger LOG = LoggerFactory.getLogger(MockUserRepositoryImpl.class); diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index 44a1e796..4cddb8a4 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -13,14 +15,12 @@ * GKislin * 06.03.2015. */ +@Service public class UserServiceImpl implements UserService { + @Autowired private UserRepository repository; - public void setRepository(UserRepository repository) { - this.repository = repository; - } - @Override public User save(User user) { return repository.save(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 aac911f5..e2b84767 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.beans.factory.annotation.Autowired; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.javawebinar.topjava.model.User; @@ -13,6 +14,7 @@ public abstract class AbstractUserController { protected final Logger LOG = LoggerFactory.getLogger(getClass()); + @Autowired private UserService service; public List getAll() { 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 e769e41b..41dbc24a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.model.User; import java.util.List; @@ -8,6 +9,7 @@ * GKislin * 06.03.2015. */ +@Controller public class AdminRestController extends AbstractUserController { @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 eb0c8d9d..443db9c5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.User; @@ -7,6 +8,7 @@ * GKislin * 06.03.2015. */ +@Controller public class ProfileRestController extends AbstractUserController { public User get() { diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 7b4fa168..96e40e90 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -1,10 +1,23 @@ + xmlns:context="http://www.springframework.org/schema/context" + 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 07d3278eae94881c6a6c3b1d7e1afdaed560a179 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Tue, 20 Dec 2016 20:06:36 +0300 Subject: [PATCH 15/63] 3_0_fix_validate_user --- .../java/ru/javawebinar/topjava/SpringMain.java | 2 +- .../javawebinar/topjava/util/ValidationUtil.java | 16 ++++++++++++++++ .../topjava/web/user/AbstractUserController.java | 9 ++++++--- src/main/resources/logback.xml | 4 ---- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 92a53add..e3ac0817 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -18,7 +18,7 @@ public static void main(String[] args) { 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(1, "userName", "email", "password", Role.ROLE_ADMIN)); + adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); } } } diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index 8fd3c9a5..d52d906e 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.util; +import ru.javawebinar.topjava.model.BaseEntity; import ru.javawebinar.topjava.util.exception.NotFoundException; /** @@ -24,4 +25,19 @@ public static T checkNotFound(T object, String msg) { public static void checkNotFound(boolean found, String msg) { if (!found) throw new NotFoundException("Not found entity with " + msg); } + + public static void checkNew(BaseEntity entity) { + if (!entity.isNew()) { + throw new IllegalArgumentException(entity + " must be new (id=null)"); + } + } + + public static void checkIdConsistent(BaseEntity entity, int id) { +// 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); + } + } } 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 e2b84767..7cc2ea5c 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -1,13 +1,16 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.beans.factory.annotation.Autowired; 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.checkNew; +import static ru.javawebinar.topjava.util.ValidationUtil.checkIdConsistent; + /** * User: gkislin */ @@ -28,7 +31,7 @@ public User get(int id) { } public User create(User user) { - user.setId(null); + checkNew(user); LOG.info("create " + user); return service.save(user); } @@ -39,7 +42,7 @@ public void delete(int id) { } public void update(User user, int id) { - user.setId(id); + checkIdConsistent(user, id); LOG.info("update " + user); service.update(user); } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 8ee5f21b..e9b900b2 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,10 +1,6 @@ - - true - - From cb0e18c1f80604eedd8435089ba1bf235bd4d77d Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:18:56 +0300 Subject: [PATCH 16/63] 3_01_HW2_repository --- .../topjava/repository/MealRepository.java | 16 +++-- .../mock/InMemoryMealRepositoryImpl.java | 59 ++++++++++++++--- .../mock/InMemoryUserRepositoryImpl.java | 64 +++++++++++++++++++ .../mock/MockUserRepositoryImpl.java | 49 -------------- .../topjava/util/DateTimeUtil.java | 5 +- .../javawebinar/topjava/web/MealServlet.java | 9 +-- 6 files changed, 132 insertions(+), 70 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java delete mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index f80eec99..a274d756 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.Collection; /** @@ -9,11 +10,18 @@ * 06.03.2015. */ public interface MealRepository { - Meal save(Meal Meal); + // null if updated meal do not belong to userId + Meal save(Meal meal, int userId); - void delete(int id); + // false if meal do not belong to userId + boolean delete(int id, int userId); - Meal get(int id); + // null if meal do not belong to userId + Meal get(int id, int userId); - Collection getAll(); + // ORDERED dateTime + Collection getAll(int userId); + + // ORDERED dateTime + Collection getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index f6ead517..b06b4738 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -2,47 +2,86 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.DateTimeUtil; import ru.javawebinar.topjava.util.MealsUtil; +import java.time.LocalDateTime; +import java.time.Month; import java.util.Collection; +import java.util.Comparator; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.ADMIN_ID; +import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.USER_ID; /** * GKislin * 15.09.2015. */ public class InMemoryMealRepositoryImpl implements MealRepository { - private Map repository = new ConcurrentHashMap<>(); + + private static final Comparator MEAL_COMPARATOR = Comparator.comparing(Meal::getDateTime).reversed(); + + // Map userId -> (mealId-> meal) + private Map> repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); { - MealsUtil.MEALS.forEach(this::save); + MealsUtil.MEALS.forEach(um -> save(um, 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) { + Objects.requireNonNull(meal); + + Map meals = repository.computeIfAbsent(userId, ConcurrentHashMap::new); if (meal.isNew()) { meal.setId(counter.incrementAndGet()); + } else if (get(meal.getId(), userId) == null) { + return null; } - repository.put(meal.getId(), meal); + meals.put(meal.getId(), meal); return meal; } @Override - public void delete(int id) { - repository.remove(id); + public boolean delete(int id, int userId) { + Map meals = repository.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 = repository.get(userId); + return meals == null ? null : meals.get(id); } @Override - public Collection getAll() { - return repository.values(); + public Collection getAll(int userId) { + return getAllAsStream(userId).collect(Collectors.toList()); + } + + @Override + public Collection getBetween(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + Objects.requireNonNull(startDateTime); + Objects.requireNonNull(endDateTime); + return getAllAsStream(userId) + .filter(um -> DateTimeUtil.isBetween(um.getDateTime(), startDateTime, endDateTime)) + .collect(Collectors.toList()); + } + + private Stream getAllAsStream(int userId) { + Map meals = repository.get(userId); + return meals == null ? + Stream.empty() : meals.values().stream().sorted(MEAL_COMPARATOR); } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java new file mode 100644 index 00000000..27f4f9be --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -0,0 +1,64 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * GKislin + * 15.06.2015. + */ +@Repository +public class InMemoryUserRepositoryImpl implements UserRepository { + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + private static final Comparator USER_COMPARATOR = Comparator.comparing(User::getName).thenComparing(User::getEmail); + + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + + @Override + public User save(User user) { + Objects.requireNonNull(user); + if (user.isNew()) { + user.setId(counter.incrementAndGet()); + } + repository.put(user.getId(), user); + return user; + } + + @Override + public boolean delete(int id) { + return repository.remove(id) != null; + } + + @Override + public User get(int id) { + return repository.get(id); + } + + @Override + public List getAll() { + return repository.values().stream() + .sorted(USER_COMPARATOR) + .collect(Collectors.toList()); + } + + @Override + public User getByEmail(String email) { + Objects.requireNonNull(email); + return repository.values().stream() + .filter(u -> email.equals(u.getEmail())) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java deleted file mode 100644 index 932f7c44..00000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -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; - -/** - * GKislin - * 15.06.2015. - */ -@Repository -public class MockUserRepositoryImpl implements UserRepository { - private static final Logger LOG = LoggerFactory.getLogger(MockUserRepositoryImpl.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/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 1a0629a9..d233d48f 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,7 +1,6 @@ package ru.javawebinar.topjava.util; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.format.DateTimeFormatter; /** @@ -11,8 +10,8 @@ public class DateTimeUtil { public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; + public static > boolean isBetween(T value, T start, T end) { + return value.compareTo(start) >= 0 && value.compareTo(end) <= 0; } public static String toString(LocalDateTime ldt) { diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 63fc920a..1f2f0101 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; import ru.javawebinar.topjava.repository.MealRepository; @@ -43,7 +44,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) Integer.valueOf(request.getParameter("calories"))); LOG.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal); + repository.save(meal, AuthorizedUser.id()); response.sendRedirect("meals"); } @@ -54,19 +55,19 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t if (action == null) { LOG.info("getAll"); request.setAttribute("meals", - MealsUtil.getWithExceeded(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + MealsUtil.getWithExceeded(repository.getAll(AuthorizedUser.id()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); request.getRequestDispatcher("/meals.jsp").forward(request, response); } else if ("delete".equals(action)) { int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id); + repository.delete(id, AuthorizedUser.id()); response.sendRedirect("meals"); } else if ("create".equals(action) || "update".equals(action)) { final Meal meal = action.equals("create") ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request)); + repository.get(getId(request), AuthorizedUser.id()); request.setAttribute("meal", meal); request.getRequestDispatcher("meal.jsp").forward(request, response); } From 1bafed7a2247bec952ae54b41f49e4a6e4a1b6cb Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:19:57 +0300 Subject: [PATCH 17/63] 3_02_HW2_meal_layers --- .../ru/javawebinar/topjava/SpringMain.java | 14 ++++ .../ru/javawebinar/topjava/model/Meal.java | 18 +---- .../mock/InMemoryMealRepositoryImpl.java | 2 + .../topjava/service/MealService.java | 23 +++++++ .../topjava/service/MealServiceImpl.java | 39 +++++++++++ .../topjava/{model => to}/MealWithExceed.java | 2 +- .../topjava/util/DateTimeUtil.java | 4 ++ .../javawebinar/topjava/util/MealsUtil.java | 2 +- .../topjava/web/meal/MealRestController.java | 65 +++++++++++++++++++ src/main/webapp/meals.jsp | 2 +- 10 files changed, 152 insertions(+), 19 deletions(-) rename src/main/java/ru/javawebinar/topjava/{model => to}/MealWithExceed.java (96%) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index e3ac0817..c3bfa8fc 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.MealWithExceed; +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; /** * User: gkislin @@ -19,6 +25,14 @@ 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", "password", Role.ROLE_ADMIN)); + System.out.println(); + + MealRestController mealController = appCtx.getBean(MealRestController.class); + List filteredMealsWithExceeded = + mealController.getBetween( + LocalDate.of(2015, Month.MAY, 30), LocalTime.of(7, 0), + LocalDate.of(2015, Month.MAY, 31), LocalTime.of(11, 0)); + filteredMealsWithExceeded.forEach(System.out::println); } } } diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index d2434144..74e0de04 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -8,9 +8,7 @@ * GKislin * 11.01.2015. */ -public class Meal { - private Integer id; - +public class Meal extends BaseEntity { private final LocalDateTime dateTime; private final String description; @@ -22,20 +20,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; } @@ -56,10 +46,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/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index b06b4738..fc9e3ddb 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.mock; +import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.DateTimeUtil; @@ -23,6 +24,7 @@ * GKislin * 15.09.2015. */ +@Repository public class InMemoryMealRepositoryImpl implements MealRepository { private static final Comparator MEAL_COMPARATOR = Comparator.comparing(Meal::getDateTime).reversed(); diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index 0a0ba9b9..36ff1dea 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -1,8 +1,31 @@ package ru.javawebinar.topjava.service; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collection; + /** * GKislin * 15.06.2015. */ public interface MealService { + Meal get(int id, int userId) throws NotFoundException; + + void delete(int id, int userId) throws NotFoundException; + + default Collection getBetweenDates(LocalDate startDate, LocalDate endDate, int userId) { + return getBetweenDateTimes(LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX), userId); + } + + Collection getBetweenDateTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId); + + Collection getAll(int userId); + + Meal update(Meal meal, int userId) throws NotFoundException; + + Meal save(Meal meal, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java index 6e00e324..1f7e42a8 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -1,13 +1,52 @@ package ru.javawebinar.topjava.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import java.time.LocalDateTime; +import java.util.Collection; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + /** * GKislin * 06.03.2015. */ +@Service public class MealServiceImpl implements MealService { + @Autowired private MealRepository repository; + @Override + public Meal get(int id, int userId) { + return checkNotFoundWithId(repository.get(id, userId), id); + } + + @Override + public void delete(int id, int userId) { + checkNotFoundWithId(repository.delete(id, userId), id); + } + + @Override + public Collection getBetweenDateTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return repository.getBetween(startDateTime, endDateTime, userId); + } + + @Override + public Collection getAll(int userId) { + return repository.getAll(userId); + } + + @Override + public Meal update(Meal meal, int userId) { + return checkNotFoundWithId(repository.save(meal, userId), meal.getId()); + } + + @Override + public Meal save(Meal meal, int userId) { + return repository.save(meal, userId); + } } diff --git a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/to/MealWithExceed.java similarity index 96% rename from src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java rename to src/main/java/ru/javawebinar/topjava/to/MealWithExceed.java index 204c177e..53795a28 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealWithExceed.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 d233d48f..d1133537 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.util; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -10,6 +11,9 @@ public class DateTimeUtil { public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + public static final LocalDate MIN_DATE = LocalDate.of(1, 1, 1); + public static final LocalDate MAX_DATE = LocalDate.of(3000, 1, 1); + public static > boolean isBetween(T value, T start, T end) { return value.compareTo(start) >= 0 && value.compareTo(end) <= 0; } diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index bceab4d1..55533aef 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.MealWithExceed; +import ru.javawebinar.topjava.to.MealWithExceed; 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 a9ab9e30..f7b4f7d8 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,12 +1,77 @@ package ru.javawebinar.topjava.web.meal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.to.MealWithExceed; +import ru.javawebinar.topjava.util.DateTimeUtil; +import ru.javawebinar.topjava.util.MealsUtil; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; /** * GKislin * 06.03.2015. */ +@Controller public class MealRestController { + private static final Logger LOG = LoggerFactory.getLogger(MealRestController.class); + + @Autowired private MealService service; + public Meal get(int id) { + int userId = AuthorizedUser.id(); + LOG.info("get meal {} for User {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = AuthorizedUser.id(); + LOG.info("delete meal {} for User {}", id, userId); + service.delete(id, userId); + } + + public List getAll() { + int userId = AuthorizedUser.id(); + LOG.info("getAll for User {}", userId); + return MealsUtil.getWithExceeded(service.getAll(userId), AuthorizedUser.getCaloriesPerDay()); + } + + public Meal create(Meal meal) { + checkNew(meal); + int userId = AuthorizedUser.id(); + LOG.info("create {} for User {}", meal, userId); + return service.save(meal, userId); + } + + public void update(Meal meal, int id) { + checkIdConsistent(meal, id); + int userId = AuthorizedUser.id(); + LOG.info("update {} for User {}", meal, userId); + service.update(meal, userId); + } + + public List getBetween(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) { + int userId = AuthorizedUser.id(); + LOG.info("getBetween dates {} - {} for time {} - {} for User {}", startDate, endDate, startTime, endTime, userId); + + return MealsUtil.getFilteredWithExceeded( + service.getBetweenDates( + startDate != null ? startDate : DateTimeUtil.MIN_DATE, + endDate != null ? endDate : DateTimeUtil.MAX_DATE, userId), + startTime != null ? startTime : LocalTime.MIN, + endTime != null ? endTime : LocalTime.MAX, + AuthorizedUser.getCaloriesPerDay() + ); + } } diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 45b47090..942a2451 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -32,7 +32,7 @@ - + <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> From 27dc483faee829ad340da1abd9a2b2f3fdb1f6c0 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:20:19 +0300 Subject: [PATCH 18/63] 3_03_HW2_optional_MealServlet --- .../javawebinar/topjava/web/MealServlet.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 1f2f0101..13ea1884 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,11 +2,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ru.javawebinar.topjava.AuthorizedUser; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; -import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.meal.MealRestController; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -25,12 +24,20 @@ 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(ServletConfig config) throws ServletException { super.init(config); - repository = new InMemoryMealRepositoryImpl(); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + mealController = springContext.getBean(MealRestController.class); + } + + @Override + public void destroy() { + springContext.close(); + super.destroy(); } @Override @@ -38,13 +45,18 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.setCharacterEncoding("UTF-8"); String id = request.getParameter("id"); - Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), + final Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), LocalDateTime.parse(request.getParameter("dateTime")), request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); - LOG.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal, AuthorizedUser.id()); + if (meal.isNew()) { + LOG.info("Create {}", meal); + mealController.create(meal); + } else { + LOG.info("Update {}", meal); + mealController.update(meal, getId(request)); + } response.sendRedirect("meals"); } @@ -54,20 +66,19 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t if (action == null) { LOG.info("getAll"); - request.setAttribute("meals", - MealsUtil.getWithExceeded(repository.getAll(AuthorizedUser.id()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.setAttribute("meals", mealController.getAll()); request.getRequestDispatcher("/meals.jsp").forward(request, response); } else if ("delete".equals(action)) { int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id, AuthorizedUser.id()); + mealController.delete(id); response.sendRedirect("meals"); } else if ("create".equals(action) || "update".equals(action)) { final Meal meal = action.equals("create") ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request), AuthorizedUser.id()); + mealController.get(getId(request)); request.setAttribute("meal", meal); request.getRequestDispatcher("meal.jsp").forward(request, response); } From fa0b8cc8aa755d0a143c44097afc17f19eb7cf51 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:20:49 +0300 Subject: [PATCH 19/63] 3_04_HW2_optional_filter --- .../topjava/util/DateTimeUtil.java | 11 ++++++ .../javawebinar/topjava/web/MealServlet.java | 36 ++++++++++++------- src/main/webapp/css/style.css | 24 +++++++++++++ src/main/webapp/meal.jsp | 19 +--------- src/main/webapp/meals.jsp | 30 +++++++++++----- 5 files changed, 81 insertions(+), 39 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 d1133537..43055dcc 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,7 +1,10 @@ package ru.javawebinar.topjava.util; +import org.springframework.util.StringUtils; + import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; /** @@ -21,4 +24,12 @@ public static > boolean isBetween(T value, T sta public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); } + + public static LocalDate parseLocalDate(String str) { + return StringUtils.isEmpty(str) ? null : LocalDate.parse(str); + } + + public static LocalTime parseLocalTime(String str) { + return StringUtils.isEmpty(str) ? null : LocalTime.parse(str); + } } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 13ea1884..d975eb63 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -5,6 +5,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.util.DateTimeUtil; import ru.javawebinar.topjava.web.meal.MealRestController; import javax.servlet.ServletConfig; @@ -13,7 +14,9 @@ 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; @@ -43,21 +46,30 @@ public void destroy() { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); - String id = request.getParameter("id"); + String action = request.getParameter("action"); + if (action == null) { + final Meal meal = new Meal( + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); - final Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), - LocalDateTime.parse(request.getParameter("dateTime")), - request.getParameter("description"), - Integer.valueOf(request.getParameter("calories"))); + if (request.getParameter("id").isEmpty()) { + LOG.info("Create {}", meal); + mealController.create(meal); + } else { + LOG.info("Update {}", meal); + mealController.update(meal, getId(request)); + } + response.sendRedirect("meals"); - if (meal.isNew()) { - LOG.info("Create {}", meal); - mealController.create(meal); - } else { - LOG.info("Update {}", meal); - mealController.update(meal, getId(request)); + } else if ("filter".equals(action)) { + LocalDate startDate = DateTimeUtil.parseLocalDate(request.getParameter("startDate")); + LocalDate endDate = DateTimeUtil.parseLocalDate(request.getParameter("endDate")); + LocalTime startTime = DateTimeUtil.parseLocalTime(request.getParameter("startTime")); + LocalTime endTime = DateTimeUtil.parseLocalTime(request.getParameter("endTime")); + request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); } - response.sendRedirect("meals"); } @Override diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css new file mode 100644 index 00000000..0c9fc667 --- /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; +} + +.normal { + color: green; +} + +.exceeded { + color: red; +} diff --git a/src/main/webapp/meal.jsp b/src/main/webapp/meal.jsp index 7541b419..a549aef9 100644 --- a/src/main/webapp/meal.jsp +++ b/src/main/webapp/meal.jsp @@ -4,24 +4,7 @@ Meal - +
    diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 942a2451..090370b6 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -5,20 +5,32 @@ Meal list - +

    Home

    Meal list

    +
    +
    +
    From Date:
    +
    +
    +
    +
    To Date:
    +
    +
    +
    +
    From Time:
    +
    +
    +
    +
    To Time:
    +
    +
    + +
    +
    Add Meal
    From e14566ace31db5f03170c0ba5a2daf615e1b8cd1 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:21:04 +0300 Subject: [PATCH 20/63] 3_05_HW2_optional_select_user --- .../java/ru/javawebinar/topjava/AuthorizedUser.java | 7 ++++++- .../java/ru/javawebinar/topjava/web/UserServlet.java | 7 +++++++ src/main/webapp/index.html | 12 ++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index 7d936562..d1b16bc5 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -7,9 +7,14 @@ * 06.03.2015. */ public class AuthorizedUser { + public static int id = 1; public static int id() { - return 1; + return id; + } + + public static void setId(int id) { + AuthorizedUser.id = id; } public static int getCaloriesPerDay() { diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 07ede01d..97e0c674 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.web; import org.slf4j.Logger; +import ru.javawebinar.topjava.AuthorizedUser; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -18,6 +19,12 @@ public class UserServlet extends HttpServlet { private static final Logger LOG = getLogger(UserServlet.class); + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + int userId = Integer.valueOf(request.getParameter("userId")); + AuthorizedUser.setId(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 56c6dc68..95ef02d0 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -7,9 +7,13 @@

    Проект Java Enterprise (Topjava)


    - +
    + Meal list of  + + + From d55bab8ad83c1cea74fc3dd4941e9e0bde1c56d4 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 21 Dec 2016 22:59:01 +0300 Subject: [PATCH 21/63] 3_06_bean_life_cycle --- .../mock/InMemoryUserRepositoryImpl.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java index 27f4f9be..5b521e0d 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -1,9 +1,13 @@ package ru.javawebinar.topjava.repository.mock; +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 javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -18,6 +22,8 @@ */ @Repository public class InMemoryUserRepositoryImpl implements UserRepository { + private static final Logger LOG = LoggerFactory.getLogger(InMemoryUserRepositoryImpl.class); + private Map repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); @@ -36,6 +42,16 @@ public User save(User user) { return user; } + @PostConstruct + public void postConstruct() { + LOG.info("+++ PostConstruct"); + } + + @PreDestroy + public void preDestroy() { + LOG.info("+++ PreDestroy"); + } + @Override public boolean delete(int id) { return repository.remove(id) != null; From f3d8cde96d7bdb2340b219a5041ad0a2516d3726 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:40:34 +0300 Subject: [PATCH 22/63] 3_07_add_junit --- pom.xml | 19 +++++++ .../ru/javawebinar/topjava/SpringMain.java | 4 +- .../ru/javawebinar/topjava/UserTestData.java | 16 ++++++ .../mock/InMemoryMealRepositoryImpl.java | 4 +- .../mock/InMemoryUserRepositoryImpl.java | 3 - .../web/InMemoryAdminRestControllerTest.java | 55 +++++++++++++++++++ 6 files changed, 93 insertions(+), 8 deletions(-) rename src/{main => test}/java/ru/javawebinar/topjava/SpringMain.java (88%) create mode 100644 src/test/java/ru/javawebinar/topjava/UserTestData.java rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java (94%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java (96%) create mode 100644 src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java diff --git a/pom.xml b/pom.xml index 0ae808eb..a3aa289e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,9 @@ 1.1.7 1.7.21 + + + 4.12 @@ -36,6 +39,14 @@ ${java.version} + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + -Dfile.encoding=UTF-8 + + @@ -88,6 +99,14 @@ jstl 1.2 + + + + junit + junit + ${junit.version} + test + diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java similarity index 88% rename from src/main/java/ru/javawebinar/topjava/SpringMain.java rename to src/test/java/ru/javawebinar/topjava/SpringMain.java index c3bfa8fc..d659837a 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -2,8 +2,6 @@ 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.to.MealWithExceed; import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; @@ -24,7 +22,7 @@ public static void main(String[] args) { 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", "password", Role.ROLE_ADMIN)); + adminUserController.create(UserTestData.USER); System.out.println(); MealRestController mealController = appCtx.getBean(MealRestController.class); diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java new file mode 100644 index 00000000..bb8a9fb1 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -0,0 +1,16 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; + +/** + * GKislin + * 24.09.2015. + */ +public class UserTestData { + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + + public static final User USER = new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER); + public static final User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java similarity index 94% rename from src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java rename to src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index fc9e3ddb..c11ea2b4 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -17,8 +17,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.ADMIN_ID; -import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.USER_ID; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.USER_ID; /** * GKislin diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java similarity index 96% rename from src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java rename to src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java index 5b521e0d..8fbe7a93 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -29,9 +29,6 @@ public class InMemoryUserRepositoryImpl implements UserRepository { private static final Comparator USER_COMPARATOR = Comparator.comparing(User::getName).thenComparing(User::getEmail); - public static final int USER_ID = 1; - public static final int ADMIN_ID = 2; - @Override public User save(User user) { Objects.requireNonNull(user); diff --git a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java new file mode 100644 index 00000000..becff75a --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java @@ -0,0 +1,55 @@ +package ru.javawebinar.topjava.web; + +import org.junit.*; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; +import ru.javawebinar.topjava.web.user.AdminRestController; + +import java.util.Arrays; +import java.util.Collection; + +import static ru.javawebinar.topjava.UserTestData.ADMIN; +import static ru.javawebinar.topjava.UserTestData.USER; + +public class InMemoryAdminRestControllerTest { + private static ConfigurableApplicationContext appCtx; + private static AdminRestController controller; + + @BeforeClass + public static void beforeClass() { + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + System.out.println("\n" + Arrays.toString(appCtx.getBeanDefinitionNames()) + "\n"); + controller = appCtx.getBean(AdminRestController.class); + } + + @AfterClass + public static void afterClass() { + appCtx.close(); + } + + @Before + public void setUp() throws Exception { + // Re-initialize + UserRepository repository = appCtx.getBean(UserRepository.class); + repository.getAll().forEach(u -> repository.delete(u.getId())); + repository.save(USER); + repository.save(ADMIN); + } + + @Test + public void testDelete() throws Exception { + controller.delete(UserTestData.USER_ID); + Collection users = controller.getAll(); + Assert.assertEquals(users.size(), 1); + Assert.assertEquals(users.iterator().next(), ADMIN); + } + + @Test(expected = NotFoundException.class) + public void testDeleteNotFound() throws Exception { + controller.delete(10); + } +} \ No newline at end of file From 4dc564d60dbcd919b6014febb1b36aab7c5fb0b9 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:40:54 +0300 Subject: [PATCH 23/63] 3_08_add_spring_test --- pom.xml | 7 +++ ...InMemoryAdminRestControllerSpringTest.java | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java diff --git a/pom.xml b/pom.xml index a3aa289e..25c41185 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,13 @@ ${junit.version} test + + org.springframework + spring-test + ${spring.version} + test + + diff --git a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java new file mode 100644 index 00000000..9396810a --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java @@ -0,0 +1,54 @@ +package ru.javawebinar.topjava.web; + +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.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; +import ru.javawebinar.topjava.web.user.AdminRestController; + +import java.util.Collection; + +import static ru.javawebinar.topjava.UserTestData.ADMIN; +import static ru.javawebinar.topjava.UserTestData.USER; + +/** + * GKislin + * 13.03.2015. + */ +@ContextConfiguration("classpath:spring/spring-app.xml") +@RunWith(SpringJUnit4ClassRunner.class) +public class InMemoryAdminRestControllerSpringTest { + + @Autowired + private AdminRestController controller; + + @Autowired + private UserRepository repository; + + @Before + public void setUp() throws Exception { + repository.getAll().forEach(u -> repository.delete(u.getId())); + repository.save(USER); + repository.save(ADMIN); + } + + @Test + public void testDelete() throws Exception { + controller.delete(UserTestData.USER_ID); + Collection users = controller.getAll(); + Assert.assertEquals(users.size(), 1); + Assert.assertEquals(users.iterator().next(), ADMIN); + } + + @Test(expected = NotFoundException.class) + public void testDeleteNotFound() throws Exception { + controller.delete(10); + } +} From e0cb111f29395a55fbcd76365b57f6d092a9f54d Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:41:10 +0300 Subject: [PATCH 24/63] 3_09_add_postgresql --- 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 25c41185..7778d871 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,8 @@ 1.1.7 1.7.21 + + 9.4.1212 4.12 @@ -86,6 +88,13 @@ + + + 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..44dd64eb --- /dev/null +++ b/src/main/resources/db/postgres.properties @@ -0,0 +1,7 @@ +#database.url=jdbc:postgresql://ec2-54-217-202-110.eu-west-1.compute.amazonaws.com:5432/dehm6lvm8bink0?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory +#database.username=wegxlfzjjgxaxy +#database.password=SSQyKKE_e93kiUCR-ehzMcKCxZ + +database.url=jdbc:postgresql://localhost:5432/topjava +database.username=user +database.password=password From fc723688cc1bb21be653e1f8667fd73fe3a7dc72 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:41:21 +0300 Subject: [PATCH 25/63] 3_10_populate_and_init_db --- src/main/resources/db/initDB.sql | 25 +++++++++++++++++++++++++ src/main/resources/db/populateDB.sql | 13 +++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/resources/db/initDB.sql create mode 100644 src/main/resources/db/populateDB.sql diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql new file mode 100644 index 00000000..a0631c79 --- /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 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(), + enabled BOOL DEFAULT TRUE, + 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 +); diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql new file mode 100644 index 00000000..199e2ace --- /dev/null +++ b/src/main/resources/db/populateDB.sql @@ -0,0 +1,13 @@ +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'); + +INSERT INTO users (name, email, password) +VALUES ('Admin', 'admin@gmail.com', 'admin'); + +INSERT INTO user_roles (role, user_id) VALUES + ('ROLE_USER', 100000), + ('ROLE_ADMIN', 100001); From a10c0224188f367c78384b4e01745d150edc5bc5 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:41:45 +0300 Subject: [PATCH 26/63] 3_11_impl_JdbcUserRepository --- pom.xml | 5 ++ .../jdbc/JdbcUserRepositoryImpl.java | 86 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 25 ++++++ 3 files changed, 116 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java create mode 100644 src/main/resources/spring/spring-db.xml diff --git a/pom.xml b/pom.xml index 7778d871..df6995e6 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,11 @@ + + org.springframework + spring-jdbc + ${spring.version} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java new file mode 100644 index 00000000..563e58ed --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -0,0 +1,86 @@ +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 javax.sql.DataSource; +import java.util.List; + +/** + * User: gkislin + * Date: 26.08.2014 + */ + +@Repository +public class JdbcUserRepositoryImpl implements UserRepository { + + private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private SimpleJdbcInsert insertUser; + + @Autowired + public JdbcUserRepositoryImpl(DataSource dataSource) { + this.insertUser = new SimpleJdbcInsert(dataSource) + .withTableName("USERS") + .usingGeneratedKeyColumns("id"); + } + + @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 { + namedParameterJdbcTemplate.update( + "UPDATE users SET name=:name, email=:email, password=:password, " + + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", map); + } + 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/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml new file mode 100644 index 00000000..a1f6b09a --- /dev/null +++ b/src/main/resources/spring/spring-db.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9c8a70e75d8e6e1847650f96340920f01dfcf254 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:41:58 +0300 Subject: [PATCH 27/63] 3_12_test_UserService --- .../javawebinar/topjava/model/BaseEntity.java | 20 ++++ .../ru/javawebinar/topjava/model/User.java | 4 + .../jdbc/JdbcMealRepositoryImpl.java | 42 +++++++++ .../javawebinar/topjava/util/DbPopulator.java | 28 ++++++ src/main/resources/spring/spring-app.xml | 2 +- src/main/resources/spring/spring-db.xml | 4 + .../ru/javawebinar/topjava/MealTestData.java | 14 +++ .../ru/javawebinar/topjava/UserTestData.java | 23 ++++- .../topjava/matcher/ModelMatcher.java | 72 ++++++++++++++ .../topjava/service/UserServiceTest.java | 94 +++++++++++++++++++ 10 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/DbPopulator.java create mode 100644 src/test/java/ru/javawebinar/topjava/MealTestData.java create mode 100644 src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index 60f4b46f..2aa1a14c 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -5,6 +5,8 @@ * Date: 22.08.2014 */ public class BaseEntity { + public static final int START_SEQ = 100000; + protected Integer id; public BaseEntity() { @@ -25,4 +27,22 @@ public Integer getId() { public boolean isNew() { return (this.id == null); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BaseEntity that = (BaseEntity) o; + return id != null && id.equals(that.id); + } + + @Override + public int hashCode() { + return (id == null) ? 0 : id; + } + } diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 4d0842a9..09d5eeff 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -27,6 +27,10 @@ public class User extends NamedEntity { public User() { } + public User(User u) { + this(u.getId(), u.getName(), u.getEmail(), u.getPassword(), u.getCaloriesPerDay(), u.isEnabled(), u.getRoles()); + } + public User(Integer id, String name, String email, String password, Role role, Role... roles) { this(id, name, email, password, MealsUtil.DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java new file mode 100644 index 00000000..129c4263 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -0,0 +1,42 @@ +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; + +/** + * User: gkislin + * Date: 26.08.2014 + */ + +@Repository +public class JdbcMealRepositoryImpl 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 getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java b/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java new file mode 100644 index 00000000..8d4a6cc3 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java @@ -0,0 +1,28 @@ +package ru.javawebinar.topjava.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; + +import javax.sql.DataSource; + +/** + * User: gkislin + * Date: 26.08.2014 + */ +public class DbPopulator extends ResourceDatabasePopulator { + private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + + @Autowired + private DataSource dataSource; + + public DbPopulator(String scriptLocation) { + super(RESOURCE_LOADER.getResource(scriptLocation)); + } + + public void execute() { + DatabasePopulatorUtils.execute(this, dataSource); + } +} diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 96e40e90..04810da8 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -14,7 +14,7 @@ - + diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index a1f6b09a..051f2253 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -6,6 +6,10 @@ + + + + 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..c05cd451 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -0,0 +1,14 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.matcher.ModelMatcher; +import ru.javawebinar.topjava.model.Meal; + +/** + * GKislin + * 13.03.2015. + */ +public class MealTestData { + + public static final ModelMatcher MATCHER = new ModelMatcher<>(); + +} diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index bb8a9fb1..f7ce7774 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -1,16 +1,33 @@ package ru.javawebinar.topjava; +import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import java.util.Objects; + +import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; + /** * GKislin * 24.09.2015. */ 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 User USER = new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER); public static final User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); -} + + public static final ModelMatcher MATCHER = new ModelMatcher<>( + (expected, actual) -> expected == actual || + (Objects.equals(expected.getPassword(), actual.getPassword()) + && Objects.equals(expected.getId(), actual.getId()) + && Objects.equals(expected.getName(), actual.getName()) + && Objects.equals(expected.getEmail(), actual.getEmail()) + && Objects.equals(expected.getCaloriesPerDay(), actual.getCaloriesPerDay()) + && Objects.equals(expected.isEnabled(), actual.isEnabled()) +// && Objects.equals(expected.getRoles(), actual.getRoles()) + ) + ); +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java new file mode 100644 index 00000000..e3f4df2e --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java @@ -0,0 +1,72 @@ +package ru.javawebinar.topjava.matcher; + +import org.junit.Assert; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * GKislin + * 06.01.2015. + * + * This class wrap every entity by Wrapper before assertEquals in order to compare them by comparator + * Default comparator compare by String.valueOf(entity) + * + * @param : Entity + */ +public class ModelMatcher { + public interface Comparator { + boolean compare(T expected, T actual); + } + + private static final Comparator DEFAULT_COMPARATOR = + (Object expected, Object actual) -> expected == actual || String.valueOf(expected).equals(String.valueOf(actual)); + + private Comparator comparator; + + public ModelMatcher() { + this((Comparator) DEFAULT_COMPARATOR); + } + + public ModelMatcher(Comparator comparator) { + this.comparator = comparator; + } + + private class Wrapper { + private T entity; + + private Wrapper(T entity) { + this.entity = entity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Wrapper that = (Wrapper) o; + return entity != null ? comparator.compare(entity, that.entity) : that.entity == null; + } + + @Override + public String toString() { + return String.valueOf(entity); + } + } + + public void assertEquals(T expected, T actual) { + Assert.assertEquals(wrap(expected), wrap(actual)); + } + + public void assertCollectionEquals(Collection expected, Collection actual) { + Assert.assertEquals(wrap(expected), wrap(actual)); + } + + public Wrapper wrap(T entity) { + return new Wrapper(entity); + } + + public List wrap(Collection collection) { + return collection.stream().map(this::wrap).collect(Collectors.toList()); + } +} 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..969e022c --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -0,0 +1,94 @@ +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.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.DbPopulator; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import static ru.javawebinar.topjava.UserTestData.*; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringJUnit4ClassRunner.class) +public class UserServiceTest { + + @Autowired + private UserService service; + + @Autowired + private DbPopulator dbPopulator; + + @Before + public void setUp() throws Exception { + dbPopulator.execute(); + } + + @Test + public void testSave() throws Exception { + User newUser = new User(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); + User created = service.save(newUser); + newUser.setId(created.getId()); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, newUser, USER), service.getAll()); + } + + @Test(expected = DataAccessException.class) + public void testDuplicateMailSave() throws Exception { + service.save(new User(null, "Duplicate", "user@yandex.ru", "newPass", Role.ROLE_USER)); + } + + @Test + public void testDelete() throws Exception { + service.delete(USER_ID); + MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), service.getAll()); + } + + @Test(expected = NotFoundException.class) + public void testNotFoundDelete() throws Exception { + service.delete(1); + } + + @Test + public void testGet() throws Exception { + User user = service.get(USER_ID); + MATCHER.assertEquals(USER, user); + } + + @Test(expected = NotFoundException.class) + public void testGetNotFound() throws Exception { + service.get(1); + } + + @Test + public void testGetByEmail() throws Exception { + User user = service.getByEmail("user@yandex.ru"); + MATCHER.assertEquals(USER, user); + } + + @Test + public void testGetAll() throws Exception { + Collection all = service.getAll(); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, USER), all); + } + + @Test + public void testUpdate() throws Exception { + User updated = new User(USER); + updated.setName("UpdatedName"); + updated.setCaloriesPerDay(330); + service.update(updated); + MATCHER.assertEquals(updated, service.get(USER_ID)); + } +} \ No newline at end of file From b6524784b8631371d02fe357abd990111ed5ae0e Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:42:11 +0300 Subject: [PATCH 28/63] 3_13_test_logging --- src/test/resources/logback-test.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/resources/logback-test.xml diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 00000000..67eb98da --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + UTF-8 + %-5level %logger{0} - %msg%n + + + + + + + + + + + \ No newline at end of file From 682b2abe9ba212c9cfad3eefb6d3ad71197ff1fb Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 22 Dec 2016 00:42:23 +0300 Subject: [PATCH 29/63] 3_14_fix_servlet --- src/main/java/ru/javawebinar/topjava/AuthorizedUser.java | 3 ++- src/main/java/ru/javawebinar/topjava/web/MealServlet.java | 2 +- src/main/webapp/index.html | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index d1b16bc5..0347b999 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava; +import ru.javawebinar.topjava.model.BaseEntity; import ru.javawebinar.topjava.util.MealsUtil; /** @@ -7,7 +8,7 @@ * 06.03.2015. */ public class AuthorizedUser { - public static int id = 1; + public static int id = BaseEntity.START_SEQ; public static int id() { return id; diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index d975eb63..8a4615c2 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -33,7 +33,7 @@ public class MealServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); - 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/webapp/index.html b/src/main/webapp/index.html index 95ef02d0..f9289bfc 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -10,8 +10,8 @@

    Проект Meal list of  From 3561307987c84d33709450aeaf2b4c94876a8ae8 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Sun, 25 Dec 2016 21:19:00 +0300 Subject: [PATCH 30/63] Add github tools integration --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 11d1e6a3..0f9e22b9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7677d893d82f4857b0d131580a15620c)](https://www.codacy.com/app/javawebinar/topjava09) +[![Build Status](https://travis-ci.org/JavaWebinar/topjava09.svg?branch=master)](https://travis-ci.org/JavaWebinar/topjava09) +[![Dependency Status](https://dependencyci.com/github/JavaWebinar/topjava09/badge)](https://dependencyci.com/github/JavaWebinar/topjava09) + Java Enterprise Online Project =============================== From bfa67013dbce98d492989c60b9fa5945ce48061a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D1=8B=20=D0=B2=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5?= Date: Thu, 29 Dec 2016 18:15:26 +0300 Subject: [PATCH 31/63] Update README.md --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0f9e22b9..c31d821b 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,9 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Поддержка HSQLDB - Домашнее задание (HW4 + Optional) -#### 05.01: 5-е занятие +#### 05.01: Вебинар, 16.00 (GMT+3.00) + +#### 12.01: 5-е занятие - Разбор вопросов - Разбор домашнего задания HW4 + Optional - Транзакции @@ -69,7 +71,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Spring кэш - Домашнее задание (HW5 + Optional) -#### 12.01: 6-е занятие +#### 19.01: 6-е занятие - Разбор домашнего задания HW5 + Optional - Кэш Hibernate - Spring Web @@ -80,7 +82,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Spring Internationalization - Домашнее задание (HW6 + Optional) -#### 19.01: 7-е занятие +#### 26.01: 7-е занятие - Разбор домашнего задания HW6 + Optional - Автогенерация DDL по модели - Тестирование Spring MVC @@ -90,7 +92,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Тестирование через SoapUi. UTF-8 - Домашнее задание (HW7 + Optional) -#### 26.01: 8-е занятие +#### 02.02: 8-е занятие - Разбор домашнего задания HW7 + Optional - WebJars. jQuery и JavaScript frameworks - Bootstrap @@ -99,7 +101,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Добавление Spring Security - Домашнее задание (HW8 + Optional) -#### 02.02: 9-е занятие +#### 09.02: 9-е занятие - Разбор домашнего задания HW8 + Optional - Spring Binding - Spring Validation @@ -111,7 +113,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Cookie. Session - Домашнее задание (HW9 + Optional) -#### 09.02: 10-е занятие +#### 16.02: 10-е занятие - Разбор домашнего задания HW10 + Optional - Spring Security Taglib. Method Security Expressions - Интерсепторы. Редактирование профиля. JSP tag files @@ -121,7 +123,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Защита от межсайтовой подделки запросов (CSRF) - Домашнее задание (HW10) -#### 16.02: 11-е занятие +#### 23.02: 11-е занятие - Разбор домашнего задания HW10 + Optional - Рефакторинг - Фильтруем JSON через @JsonView From bf97154111e11702fa6e151a68ba0eedb41e5e3f Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 04:54:53 +0300 Subject: [PATCH 32/63] 4_0_fix --- .../topjava/repository/jdbc/JdbcUserRepositoryImpl.java | 2 +- src/test/java/ru/javawebinar/topjava/SpringMain.java | 4 +++- .../topjava/repository/mock/InMemoryMealRepositoryImpl.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java index 563e58ed..8ed10405 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -35,7 +35,7 @@ public class JdbcUserRepositoryImpl implements UserRepository { @Autowired public JdbcUserRepositoryImpl(DataSource dataSource) { this.insertUser = new SimpleJdbcInsert(dataSource) - .withTableName("USERS") + .withTableName("users") .usingGeneratedKeyColumns("id"); } diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index d659837a..c3bfa8fc 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -2,6 +2,8 @@ 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.to.MealWithExceed; import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; @@ -22,7 +24,7 @@ public static void main(String[] args) { 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(UserTestData.USER); + adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); System.out.println(); MealRestController mealController = appCtx.getBean(MealRestController.class); diff --git a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index c11ea2b4..549eeb5d 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -44,12 +44,12 @@ public class InMemoryMealRepositoryImpl implements MealRepository { public Meal save(Meal meal, int userId) { Objects.requireNonNull(meal); - Map meals = repository.computeIfAbsent(userId, ConcurrentHashMap::new); if (meal.isNew()) { meal.setId(counter.incrementAndGet()); } else if (get(meal.getId(), userId) == null) { return null; } + Map meals = repository.computeIfAbsent(userId, ConcurrentHashMap::new); meals.put(meal.getId(), meal); return meal; } From d3446b8e10050786aacb5f14aae03ce471f5bd93 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 04:55:28 +0300 Subject: [PATCH 33/63] 4_1_HW3 --- .../ru/javawebinar/topjava/model/Meal.java | 21 ++++++- .../jdbc/JdbcMealRepositoryImpl.java | 62 +++++++++++++++++-- src/main/resources/db/initDB.sql | 11 ++++ src/main/resources/db/populateDB.sql | 11 ++++ 4 files changed, 96 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 74e0de04..41796d38 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -9,11 +9,14 @@ * 11.01.2015. */ public class Meal extends BaseEntity { - 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); @@ -46,6 +49,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/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java index 129c4263..3995520d 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -1,9 +1,18 @@ 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; +import javax.sql.DataSource; import java.time.LocalDateTime; import java.util.List; @@ -15,28 +24,69 @@ @Repository public class JdbcMealRepositoryImpl implements MealRepository { + private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private SimpleJdbcInsert insertMeal; + + @Autowired + public JdbcMealRepositoryImpl(DataSource dataSource) { + this.insertMeal = new SimpleJdbcInsert(dataSource) + .withTableName("meals") + .usingGeneratedKeyColumns("id"); + } + @Override - public Meal save(Meal meal, int userId) { - return null; + public Meal save(Meal userMeal, int userId) { + MapSqlParameterSource map = new MapSqlParameterSource() + .addValue("id", userMeal.getId()) + .addValue("description", userMeal.getDescription()) + .addValue("calories", userMeal.getCalories()) + .addValue("date_time", userMeal.getDateTime()) + .addValue("user_id", userId); + + if (userMeal.isNew()) { + Number newId = insertMeal.executeAndReturnKey(map); + userMeal.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 userMeal; } @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 getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { - return null; + return jdbcTemplate.query( + "SELECT * FROM meals WHERE user_id=? AND date_time BETWEEN ? AND ? ORDER BY date_time DESC", + ROW_MAPPER, userId, startDate, endDate); } } diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql index a0631c79..aede3736 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; @@ -23,3 +24,13 @@ CREATE TABLE user_roles CONSTRAINT user_roles_idx UNIQUE (user_id, role), FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); + +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 199e2ace..0fbffd0d 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; @@ -11,3 +12,13 @@ VALUES ('Admin', 'admin@gmail.com', 'admin'); INSERT INTO user_roles (role, user_id) VALUES ('ROLE_USER', 100000), ('ROLE_ADMIN', 100001); + +INSERT INTO meals (date_time, description, calories, user_id) VALUES + ('2015-05-30 10:00:00', 'Завтрак', 500, 100000), + ('2015-05-30 13:00:00', 'Обед', 1000, 100000), + ('2015-05-30 20:00:00', 'Ужин', 500, 100000), + ('2015-05-31 10:00:00', 'Завтрак', 500, 100000), + ('2015-05-31 13:00:00', 'Обед', 1000, 100000), + ('2015-05-31 20:00:00', 'Ужин', 510, 100000), + ('2015-06-01 14:00:00', 'Админ ланч', 510, 100001), + ('2015-06-01 21:00:00', 'Админ ужин', 1500, 100001); From ae0740beac608ed3b4417f46986b06a757d5cc3e Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 04:59:21 +0300 Subject: [PATCH 34/63] 4_2_HW3_optional --- .../javawebinar/topjava/util/MealsUtil.java | 18 ---- .../ru/javawebinar/topjava/MealTestData.java | 28 ++++++ .../mock/InMemoryMealRepositoryImpl.java | 12 --- .../topjava/service/MealServiceTest.java | 90 +++++++++++++++++++ 4 files changed, 118 insertions(+), 30 deletions(-) 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 55533aef..97b68b61 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -4,9 +4,7 @@ import ru.javawebinar.topjava.to.MealWithExceed; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.Month; import java.util.*; import java.util.stream.Collectors; @@ -15,24 +13,8 @@ * 31.05.2015. */ public class MealsUtil { - public static final List MEALS = Arrays.asList( - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), - new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), - new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) - ); - public static final int DEFAULT_CALORIES_PER_DAY = 2000; - public static void main(String[] args) { - List mealsWithExceeded = getFilteredWithExceeded(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); - mealsWithExceeded.forEach(System.out::println); - - System.out.println(getFilteredWithExceededByCycle(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY)); - } - public static List getWithExceeded(Collection meals, int caloriesPerDay) { return getFilteredWithExceeded(meals, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); } diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index c05cd451..c5c224f3 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -3,6 +3,13 @@ import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.Meal; +import java.time.Month; +import java.util.Arrays; +import java.util.List; + +import static java.time.LocalDateTime.of; +import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; + /** * GKislin * 13.03.2015. @@ -11,4 +18,25 @@ public class MealTestData { public static final ModelMatcher MATCHER = new ModelMatcher<>(); + public static final int MEAL1_ID = START_SEQ + 2; + public static final int ADMIN_MEAL_ID = START_SEQ + 8; + + public static final Meal MEAL1 = new Meal(MEAL1_ID, of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500); + public static final Meal MEAL2 = new Meal(MEAL1_ID + 1, of(2015, Month.MAY, 30, 13, 0), "Обед", 1000); + public static final Meal MEAL3 = new Meal(MEAL1_ID + 2, of(2015, Month.MAY, 30, 20, 0), "Ужин", 500); + public static final Meal MEAL4 = new Meal(MEAL1_ID + 3, of(2015, Month.MAY, 31, 10, 0), "Завтрак", 500); + public static final Meal MEAL5 = new Meal(MEAL1_ID + 4, of(2015, Month.MAY, 31, 13, 0), "Обед", 1000); + public static final Meal MEAL6 = new Meal(MEAL1_ID + 5, of(2015, Month.MAY, 31, 20, 0), "Ужин", 510); + public static final Meal ADMIN_MEAL1 = new Meal(ADMIN_MEAL_ID, of(2015, Month.JUNE, 1, 14, 0), "Админ ланч", 510); + public static final Meal ADMIN_MEAL2 = new Meal(ADMIN_MEAL_ID + 1, of(2015, Month.JUNE, 1, 21, 0), "Админ ужин", 1500); + + public static final List MEALS = Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1); + + public static Meal getCreated() { + return new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Созданный ужин", 300); + } + + public static Meal getUpdated() { + return new Meal(MEAL1_ID, MEAL1.getDateTime(), "Обновленный завтрак", 200); + } } diff --git a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index 549eeb5d..990730af 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -4,10 +4,8 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.DateTimeUtil; -import ru.javawebinar.topjava.util.MealsUtil; import java.time.LocalDateTime; -import java.time.Month; import java.util.Collection; import java.util.Comparator; import java.util.Map; @@ -17,9 +15,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; -import static ru.javawebinar.topjava.UserTestData.USER_ID; - /** * GKislin * 15.09.2015. @@ -33,13 +28,6 @@ public class InMemoryMealRepositoryImpl implements MealRepository { private Map> repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); - { - MealsUtil.MEALS.forEach(um -> save(um, 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, int userId) { Objects.requireNonNull(meal); 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..7338fde6 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -0,0 +1,90 @@ +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.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.util.DbPopulator; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.time.LocalDate; +import java.time.Month; +import java.util.Arrays; + +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(SpringJUnit4ClassRunner.class) +public class MealServiceTest { + + @Autowired + private MealService service; + + @Autowired + private DbPopulator dbPopulator; + + @Before + public void setUp() throws Exception { + dbPopulator.execute(); + } + + @Test + public void testDelete() throws Exception { + service.delete(MEAL1_ID, USER_ID); + MATCHER.assertCollectionEquals(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2), service.getAll(USER_ID)); + } + + @Test(expected = NotFoundException.class) + public void testDeleteNotFound() throws Exception { + service.delete(MEAL1_ID, 1); + } + + @Test + public void testSave() throws Exception { + Meal created = getCreated(); + service.save(created, USER_ID); + MATCHER.assertCollectionEquals(Arrays.asList(created, MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), service.getAll(USER_ID)); + } + + @Test + public void testGet() throws Exception { + Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); + MATCHER.assertEquals(ADMIN_MEAL1, actual); + } + + @Test(expected = NotFoundException.class) + public void testGetNotFound() throws Exception { + service.get(MEAL1_ID, ADMIN_ID); + } + + @Test + public void testUpdate() throws Exception { + Meal updated = getUpdated(); + service.update(updated, USER_ID); + MATCHER.assertEquals(updated, service.get(MEAL1_ID, USER_ID)); + } + + @Test(expected = NotFoundException.class) + public void testUpdateNotFound() throws Exception { + service.update(MEAL1, ADMIN_ID); + } + + @Test + public void testGetAll() throws Exception { + MATCHER.assertCollectionEquals(MEALS, service.getAll(USER_ID)); + } + + @Test + public void testGetBetween() throws Exception { + MATCHER.assertCollectionEquals(Arrays.asList(MEAL3, MEAL2, MEAL1), + service.getBetweenDates(LocalDate.of(2015, Month.MAY, 30), LocalDate.of(2015, Month.MAY, 30), USER_ID)); + } +} \ No newline at end of file From 7a8fdabd4706eb42c08295f10d0b5768bc15edfa Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 04:59:38 +0300 Subject: [PATCH 35/63] 4_3_improve_code --- .travis.yml | 13 +++++++++++++ .../topjava/service/MealServiceImpl.java | 5 +++++ .../topjava/service/UserServiceImpl.java | 4 ++++ .../ru/javawebinar/topjava/util/DateTimeUtil.java | 3 +++ .../javawebinar/topjava/util/ValidationUtil.java | 7 ++++++- .../ru/javawebinar/topjava/web/MealServlet.java | 2 +- .../topjava/web/user/AbstractUserController.java | 14 +++++++------- .../javawebinar/topjava/matcher/ModelMatcher.java | 8 ++++---- .../mock/InMemoryMealRepositoryImpl.java | 5 ----- .../mock/InMemoryUserRepositoryImpl.java | 3 --- 10 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..6b280b2d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +# https://docs.travis-ci.com/user/languages/java/ +language: java +jdk: oraclejdk8 + +#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 \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java index 1f7e42a8..f8be8121 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -32,6 +33,8 @@ public void delete(int id, int userId) { @Override public Collection getBetweenDateTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + Assert.notNull(startDateTime, "startDateTime must not be null"); + Assert.notNull(endDateTime, "endDateTime must not be null"); return repository.getBetween(startDateTime, endDateTime, userId); } @@ -42,11 +45,13 @@ public Collection getAll(int userId) { @Override public Meal update(Meal meal, int userId) { + Assert.notNull(meal, "meal must not be null"); return checkNotFoundWithId(repository.save(meal, userId), meal.getId()); } @Override public Meal save(Meal meal, int userId) { + Assert.notNull(meal, "meal must not be null"); return repository.save(meal, userId); } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index 4cddb8a4..572bd013 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -23,6 +24,7 @@ public class UserServiceImpl implements UserService { @Override public User save(User user) { + Assert.notNull(user, "user must not be null"); return repository.save(user); } @@ -38,6 +40,7 @@ public User get(int id) throws NotFoundException { @Override public User getByEmail(String email) throws NotFoundException { + Assert.notNull(email, "email must not be null"); return checkNotFound(repository.getByEmail(email), "email=" + email); } @@ -48,6 +51,7 @@ public List getAll() { @Override public void update(User user) { + Assert.notNull(user, "user must not be null"); repository.save(user); } } diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 43055dcc..37ccc717 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -17,6 +17,9 @@ public class DateTimeUtil { public static final LocalDate MIN_DATE = LocalDate.of(1, 1, 1); public static final LocalDate MAX_DATE = LocalDate.of(3000, 1, 1); + private DateTimeUtil() { + } + public static > boolean isBetween(T value, T start, T end) { return value.compareTo(start) >= 0 && 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 d52d906e..96167601 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -9,6 +9,9 @@ * Date: 14.05.2014 */ public class ValidationUtil { + private ValidationUtil() { + } + public static void checkNotFoundWithId(boolean found, int id) { checkNotFound(found, "id=" + id); } @@ -23,7 +26,9 @@ public static T checkNotFound(T object, String msg) { } public static void checkNotFound(boolean found, String msg) { - if (!found) throw new NotFoundException("Not found entity with " + msg); + if (!found) { + throw new NotFoundException("Not found entity with " + msg); + } } public static void checkNew(BaseEntity entity) { diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 8a4615c2..2306395b 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -88,7 +88,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t response.sendRedirect("meals"); } else if ("create".equals(action) || "update".equals(action)) { - final Meal meal = action.equals("create") ? + final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : mealController.get(getId(request)); request.setAttribute("meal", meal); 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 7cc2ea5c..26719137 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -15,40 +15,40 @@ * User: gkislin */ public abstract class AbstractUserController { - protected final Logger LOG = LoggerFactory.getLogger(getClass()); + protected final Logger log = LoggerFactory.getLogger(getClass()); @Autowired private UserService service; public List getAll() { - LOG.info("getAll"); + log.info("getAll"); return service.getAll(); } public User get(int id) { - LOG.info("get " + id); + log.info("get " + id); return service.get(id); } public User create(User user) { checkNew(user); - LOG.info("create " + user); + log.info("create " + user); return service.save(user); } public void delete(int id) { - LOG.info("delete " + id); + log.info("delete " + id); service.delete(id); } public void update(User user, int id) { checkIdConsistent(user, id); - LOG.info("update " + user); + log.info("update " + user); service.update(user); } public User getByMail(String email) { - LOG.info("getByEmail " + email); + log.info("getByEmail " + email); return service.getByEmail(email); } } diff --git a/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java index e3f4df2e..a9f3ac85 100644 --- a/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java +++ b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java @@ -16,15 +16,15 @@ * @param : Entity */ public class ModelMatcher { - public interface Comparator { - boolean compare(T expected, T actual); - } - private static final Comparator DEFAULT_COMPARATOR = (Object expected, Object actual) -> expected == actual || String.valueOf(expected).equals(String.valueOf(actual)); private Comparator comparator; + public interface Comparator { + boolean compare(T expected, T actual); + } + public ModelMatcher() { this((Comparator) DEFAULT_COMPARATOR); } diff --git a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index 990730af..c871b64d 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -9,7 +9,6 @@ import java.util.Collection; import java.util.Comparator; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -30,8 +29,6 @@ public class InMemoryMealRepositoryImpl implements MealRepository { @Override public Meal save(Meal meal, int userId) { - Objects.requireNonNull(meal); - if (meal.isNew()) { meal.setId(counter.incrementAndGet()); } else if (get(meal.getId(), userId) == null) { @@ -61,8 +58,6 @@ public Collection getAll(int userId) { @Override public Collection getBetween(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - Objects.requireNonNull(startDateTime); - Objects.requireNonNull(endDateTime); return getAllAsStream(userId) .filter(um -> DateTimeUtil.isBetween(um.getDateTime(), startDateTime, endDateTime)) .collect(Collectors.toList()); diff --git a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java index 8fbe7a93..77125080 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -11,7 +11,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -31,7 +30,6 @@ public class InMemoryUserRepositoryImpl implements UserRepository { @Override public User save(User user) { - Objects.requireNonNull(user); if (user.isNew()) { user.setId(counter.incrementAndGet()); } @@ -68,7 +66,6 @@ public List getAll() { @Override public User getByEmail(String email) { - Objects.requireNonNull(email); return repository.values().stream() .filter(u -> email.equals(u.getEmail())) .findFirst() From 59ac6c4ba8bffbd35ddae48b4b4f18caba85cd69 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 04:59:51 +0300 Subject: [PATCH 36/63] 4_4_init_and_populate_db --- .../javawebinar/topjava/util/DbPopulator.java | 28 ------------------- src/main/resources/db/postgres.properties | 2 ++ src/main/resources/spring/spring-db.xml | 13 +++++---- .../topjava/service/MealServiceTest.java | 13 ++------- .../topjava/service/UserServiceTest.java | 13 ++------- 5 files changed, 16 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/util/DbPopulator.java diff --git a/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java b/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java deleted file mode 100644 index 8d4a6cc3..00000000 --- a/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.javawebinar.topjava.util; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; -import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; -import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; - -import javax.sql.DataSource; - -/** - * User: gkislin - * Date: 26.08.2014 - */ -public class DbPopulator extends ResourceDatabasePopulator { - private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); - - @Autowired - private DataSource dataSource; - - public DbPopulator(String scriptLocation) { - super(RESOURCE_LOADER.getResource(scriptLocation)); - } - - public void execute() { - DatabasePopulatorUtils.execute(this, dataSource); - } -} diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 44dd64eb..d1aecfa9 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -5,3 +5,5 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=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 051f2253..22ad2723 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,14 +1,17 @@ + xmlns:jdbc="http://www.springframework.org/schema/jdbc" + 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"> - - - + + + + Date: Thu, 29 Dec 2016 05:00:03 +0300 Subject: [PATCH 37/63] 4_5_create_mock_test_ctx --- src/main/resources/spring/spring-app.xml | 2 -- src/main/resources/spring/spring-db.xml | 2 ++ src/test/java/ru/javawebinar/topjava/SpringMain.java | 2 +- .../topjava/web/InMemoryAdminRestControllerSpringTest.java | 2 +- .../topjava/web/InMemoryAdminRestControllerTest.java | 2 +- src/test/resources/spring/mock.xml | 7 +++++++ 6 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/spring/mock.xml diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 04810da8..eff98648 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -14,8 +14,6 @@ - - diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 22ad2723..c239b013 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -8,6 +8,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index c3bfa8fc..1cb3684c 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -21,7 +21,7 @@ public class SpringMain { public static void main(String[] args) { // java 7 Automatic resource management - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml")) { + try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml","spring/mock.xml")) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); diff --git a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java index 9396810a..a9125c6e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java @@ -22,7 +22,7 @@ * GKislin * 13.03.2015. */ -@ContextConfiguration("classpath:spring/spring-app.xml") +@ContextConfiguration({"classpath:spring/spring-app.xml", "classpath:spring/mock.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class InMemoryAdminRestControllerSpringTest { diff --git a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java index becff75a..f7006200 100644 --- a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java @@ -21,7 +21,7 @@ public class InMemoryAdminRestControllerTest { @BeforeClass public static void beforeClass() { - appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/mock.xml"); System.out.println("\n" + Arrays.toString(appCtx.getBeanDefinitionNames()) + "\n"); controller = appCtx.getBean(AdminRestController.class); } diff --git a/src/test/resources/spring/mock.xml b/src/test/resources/spring/mock.xml new file mode 100644 index 00000000..c7c9f62c --- /dev/null +++ b/src/test/resources/spring/mock.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file From 65f6573d9d4a2cd2e0b468ac77b71ad5a6f942e0 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 05:00:15 +0300 Subject: [PATCH 38/63] 4_6_add_jpa --- pom.xml | 29 ++++++++- .../javawebinar/topjava/model/BaseEntity.java | 7 ++ .../ru/javawebinar/topjava/model/Meal.java | 13 ++++ .../topjava/model/NamedEntity.java | 8 +++ .../ru/javawebinar/topjava/model/User.java | 22 +++++++ .../repository/jpa/JpaMealRepositoryImpl.java | 42 ++++++++++++ .../repository/jpa/JpaUserRepositoryImpl.java | 65 +++++++++++++++++++ src/main/resources/db/postgres.properties | 6 +- src/main/resources/spring/spring-db.xml | 26 +++++++- 9 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java diff --git a/pom.xml b/pom.xml index df6995e6..c5bc19e0 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,10 @@ 9.4.1212 4.12 + + + 5.2.5.Final + 5.3.4.Final @@ -89,7 +93,7 @@ org.springframework - spring-jdbc + spring-orm ${spring.version} @@ -100,6 +104,28 @@ ${postgresql.version} + + org.hibernate + hibernate-core + ${hibernate.version} + + + javax.transaction + jta + 1.1 + runtime + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + javax.el + javax.el-api + 2.2.5 + + javax.servlet @@ -127,7 +153,6 @@ ${spring.version} test - diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index 2aa1a14c..3a7ac61b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -1,12 +1,19 @@ package ru.javawebinar.topjava.model; +import javax.persistence.*; + /** * User: gkislin * Date: 22.08.2014 */ +@MappedSuperclass +@Access(AccessType.FIELD) public class BaseEntity { public static final int START_SEQ = 100000; + @Id + @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") protected Integer id; public BaseEntity() { diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 41796d38..6e3c3b79 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; @@ -15,6 +17,9 @@ public class Meal extends BaseEntity { private int calories; + @ManyToOne(fetch = FetchType.LAZY) + private User user; + public Meal() { } @@ -61,6 +66,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/NamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java index ca92e394..e593d379 100644 --- a/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java @@ -1,11 +1,19 @@ package ru.javawebinar.topjava.model; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.persistence.Column; +import javax.persistence.MappedSuperclass; + /** * User: gkislin * Date: 22.08.2014 */ +@MappedSuperclass public class NamedEntity extends BaseEntity { + @NotEmpty + @Column(name = "name", nullable = false) protected String name; public NamedEntity() { diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 09d5eeff..71073c48 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -2,6 +2,12 @@ import ru.javawebinar.topjava.util.MealsUtil; +import org.hibernate.validator.constraints.Email; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.persistence.*; +import javax.validation.constraints.Digits; import java.util.Date; import java.util.EnumSet; import java.util.Set; @@ -10,18 +16,34 @@ * User: gkislin * Date: 22.08.2014 */ +@Entity +@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) public class User extends NamedEntity { + @Column(name = "email", nullable = false, unique = true) + @Email + @NotEmpty private String email; + @Column(name = "password", nullable = false) + @NotEmpty + @Length(min = 5) private String password; + @Column(name = "enabled", nullable = false) private boolean enabled = true; + @Column(name = "registered", columnDefinition = "timestamp default now()") private Date registered = new Date(); + @Enumerated(EnumType.STRING) + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "role") + @ElementCollection(fetch = FetchType.EAGER) private Set roles; + @Column(name = "calories_per_day", columnDefinition = "int default 2000") + @Digits(fraction = 0, integer = 4) private int caloriesPerDay = MealsUtil.DEFAULT_CALORIES_PER_DAY; public User() { diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java new file mode 100644 index 00000000..552cc073 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java @@ -0,0 +1,42 @@ +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; + +/** + * User: gkisline + * Date: 26.08.2014 + */ + +@Repository +public class JpaMealRepositoryImpl 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 getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java new file mode 100644 index 00000000..b662bea9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java @@ -0,0 +1,65 @@ +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; + +/** + * User: gkislin + * Date: 29.08.2014 + */ +@Repository +public class JpaUserRepositoryImpl 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 d1aecfa9..0293ef30 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -6,4 +6,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 c239b013..4c72d7b6 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,5 +1,6 @@ - + @@ -24,6 +25,28 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file From 831e2ce6d7e85ea5bf7105519bf77a13d1400284 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 29 Dec 2016 05:00:33 +0300 Subject: [PATCH 39/63] 4_7_add_named_query_and_transaction --- .../java/ru/javawebinar/topjava/model/User.java | 9 +++++++++ .../repository/jpa/JpaUserRepositoryImpl.java | 17 ++++++++++++----- src/main/resources/spring/spring-db.xml | 10 +++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 71073c48..3030a762 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -16,10 +16,19 @@ * User: gkislin * Date: 22.08.2014 */ +@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 NamedEntity { + public static final String DELETE = "User.delete"; + public static final String ALL_SORTED = "User.getAllSorted"; + public static final String BY_EMAIL = "User.getByEmail"; + @Column(name = "email", nullable = false, unique = true) @Email @NotEmpty diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java index b662bea9..30a5fc45 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java @@ -1,12 +1,13 @@ 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; /** @@ -14,6 +15,7 @@ * Date: 29.08.2014 */ @Repository +@Transactional(readOnly = true) public class JpaUserRepositoryImpl implements UserRepository { /* @@ -29,6 +31,7 @@ private Session openSession() { private EntityManager em; @Override + @Transactional public User save(User user) { if (user.isNew()) { em.persist(user); @@ -44,22 +47,26 @@ 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"); + + 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 4c72d7b6..7e4ca66b 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"> @@ -45,6 +47,12 @@ + + + + + + + - + - + From 430f568f53f63004f0e77c9474dbd23b334cb422 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:28:57 +0300 Subject: [PATCH 41/63] 5_0_refactoring --- .../javawebinar/topjava/model/NamedEntity.java | 4 ++-- .../java/ru/javawebinar/topjava/model/User.java | 9 ++++----- .../repository/jdbc/JdbcMealRepositoryImpl.java | 16 ++++++++-------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java index e593d379..f59fed81 100644 --- a/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.model; -import org.hibernate.validator.constraints.NotEmpty; +import org.hibernate.validator.constraints.NotBlank; import javax.persistence.Column; import javax.persistence.MappedSuperclass; @@ -12,7 +12,7 @@ @MappedSuperclass public class NamedEntity extends BaseEntity { - @NotEmpty + @NotBlank @Column(name = "name", nullable = false) protected String name; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 3030a762..e76eb003 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,10 +1,9 @@ package ru.javawebinar.topjava.model; -import ru.javawebinar.topjava.util.MealsUtil; - import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.NotEmpty; +import org.hibernate.validator.constraints.NotBlank; +import ru.javawebinar.topjava.util.MealsUtil; import javax.persistence.*; import javax.validation.constraints.Digits; @@ -31,11 +30,11 @@ public class User extends NamedEntity { @Column(name = "email", nullable = false, unique = true) @Email - @NotEmpty + @NotBlank private String email; @Column(name = "password", nullable = false) - @NotEmpty + @NotBlank @Length(min = 5) private String password; diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java index 3995520d..f531dc87 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -42,17 +42,17 @@ public JdbcMealRepositoryImpl(DataSource dataSource) { } @Override - public Meal save(Meal userMeal, int userId) { + public Meal save(Meal meal, int userId) { MapSqlParameterSource map = new MapSqlParameterSource() - .addValue("id", userMeal.getId()) - .addValue("description", userMeal.getDescription()) - .addValue("calories", userMeal.getCalories()) - .addValue("date_time", userMeal.getDateTime()) + .addValue("id", meal.getId()) + .addValue("description", meal.getDescription()) + .addValue("calories", meal.getCalories()) + .addValue("date_time", meal.getDateTime()) .addValue("user_id", userId); - if (userMeal.isNew()) { + if (meal.isNew()) { Number newId = insertMeal.executeAndReturnKey(map); - userMeal.setId(newId.intValue()); + meal.setId(newId.intValue()); } else { if (namedParameterJdbcTemplate.update("" + "UPDATE meals " + @@ -62,7 +62,7 @@ public Meal save(Meal userMeal, int userId) { return null; } } - return userMeal; + return meal; } @Override From ceb133ced460a225cfa6c82aa30f3e462d9fc9cd Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:29:18 +0300 Subject: [PATCH 42/63] 5_1_HW4 --- .../ru/javawebinar/topjava/model/Meal.java | 29 +++++++++++++- .../repository/jpa/JpaMealRepositoryImpl.java | 38 ++++++++++++++++--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 6e3c3b79..f355d5fd 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,7 +1,10 @@ package ru.javawebinar.topjava.model; -import javax.persistence.FetchType; -import javax.persistence.ManyToOne; +import org.hibernate.validator.constraints.NotBlank; +import org.hibernate.validator.constraints.Range; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -10,14 +13,36 @@ * GKislin * 11.01.2015. */ +@SuppressWarnings("JpaQlInspection") +@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 BETWEEN :startDate AND :endDate 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 BaseEntity { + 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 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) private User user; public Meal() { diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java index 552cc073..200a2c01 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepositoryImpl.java @@ -1,9 +1,13 @@ 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; @@ -13,30 +17,54 @@ */ @Repository +@Transactional(readOnly = true) public class JpaMealRepositoryImpl implements MealRepository { + @PersistenceContext + private EntityManager em; + @Override + @Transactional public Meal save(Meal meal, int userId) { - return null; + if (!meal.isNew() && get(meal.getId(), userId) == null) { + return null; + } + meal.setUser(em.getReference(User.class, userId)); + if (meal.isNew()) { + em.persist(meal); + return meal; + } else { + 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 getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { - return null; + return em.createNamedQuery(Meal.GET_BETWEEN, Meal.class) + .setParameter("userId", userId) + .setParameter("startDate", startDate) + .setParameter("endDate", endDate).getResultList(); } } \ No newline at end of file From 99bf6cdc34094896139bb24fafc5a987454ca603 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:30:28 +0300 Subject: [PATCH 43/63] 5_2_fix_hibernate_issue --- .../java/ru/javawebinar/topjava/model/BaseEntity.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index 3a7ac61b..a4bfd321 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -7,6 +7,7 @@ * Date: 22.08.2014 */ @MappedSuperclass +// http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) public class BaseEntity { public static final int START_SEQ = 100000; @@ -14,6 +15,8 @@ public class BaseEntity { @Id @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") + // PROPERTY access for id due to bug: https://hibernate.atlassian.net/browse/HHH-3718 + @Access(value = AccessType.PROPERTY) protected Integer id; public BaseEntity() { @@ -32,7 +35,7 @@ public Integer getId() { } public boolean isNew() { - return (this.id == null); + return (getId() == null); } @Override @@ -44,12 +47,12 @@ public boolean equals(Object o) { return false; } BaseEntity that = (BaseEntity) o; - return id != null && id.equals(that.id); + return getId() != null && getId().equals(that.getId()); } @Override public int hashCode() { - return (id == null) ? 0 : id; + return (getId() == null) ? 0 : getId(); } } From 0a36766f6292089c5b111265ab2e5ceee7018cf7 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:30:42 +0300 Subject: [PATCH 44/63] 5_3_HW4_optional --- .../topjava/service/MealServiceTest.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 33cc7cc2..053dbe62 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -1,7 +1,14 @@ package ru.javawebinar.topjava.service; +import org.junit.AfterClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.Stopwatch; +import org.junit.runner.Description; import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; @@ -13,6 +20,7 @@ import java.time.LocalDate; import java.time.Month; import java.util.Arrays; +import java.util.concurrent.TimeUnit; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; @@ -25,6 +33,31 @@ @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class MealServiceTest { + private static final Logger LOG = LoggerFactory.getLogger(MealServiceTest.class); + private static StringBuilder results = new StringBuilder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @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("%-25s %7d", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); + results.append(result).append('\n'); + LOG.info(result + " ms\n"); + } + }; + + @AfterClass + public static void printResult() { + LOG.info("\n---------------------------------" + + "\nTest Duration, ms" + + "\n---------------------------------\n" + + results + + "---------------------------------\n"); + } @Autowired private MealService service; @@ -35,8 +68,9 @@ public void testDelete() throws Exception { MATCHER.assertCollectionEquals(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2), service.getAll(USER_ID)); } - @Test(expected = NotFoundException.class) + @Test public void testDeleteNotFound() throws Exception { + thrown.expect(NotFoundException.class); service.delete(MEAL1_ID, 1); } @@ -53,8 +87,9 @@ public void testGet() throws Exception { MATCHER.assertEquals(ADMIN_MEAL1, actual); } - @Test(expected = NotFoundException.class) + @Test public void testGetNotFound() throws Exception { + thrown.expect(NotFoundException.class); service.get(MEAL1_ID, ADMIN_ID); } @@ -65,8 +100,10 @@ public void testUpdate() throws Exception { MATCHER.assertEquals(updated, service.get(MEAL1_ID, USER_ID)); } - @Test(expected = NotFoundException.class) + @Test public void testUpdateNotFound() throws Exception { + thrown.expect(NotFoundException.class); + thrown.expectMessage("Not found entity with id=" + MEAL1_ID); service.update(MEAL1, ADMIN_ID); } From 52ad026f5697138a81f64ce90239bb0f1fde1276 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:32:27 +0300 Subject: [PATCH 45/63] 5_4_profiles_connection_pool --- pom.xml | 42 +++++++++++++------ .../java/ru/javawebinar/topjava/Profiles.java | 15 +++++++ src/main/resources/db/hsqldb.properties | 1 - src/main/resources/db/postgres.properties | 1 - src/main/resources/spring/spring-db.xml | 37 +++++++++------- .../topjava/service/MealServiceTest.java | 3 ++ .../topjava/service/UserServiceTest.java | 3 ++ 7 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/Profiles.java diff --git a/pom.xml b/pom.xml index eceddda2..6e22821c 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ UTF-8 UTF-8 + 8.5.5 4.3.4.RELEASE @@ -97,19 +98,6 @@ ${spring.version} - - - org.postgresql - postgresql - ${postgresql.version} - - - org.hsqldb - hsqldb - 2.3.4 - - - org.hibernate hibernate-core @@ -162,6 +150,34 @@ + + hsqldb + + + org.hsqldb + hsqldb + 2.3.4 + + + + + postgres + + + org.postgresql + postgresql + ${postgresql.version} + + + org.apache.tomcat + tomcat-jdbc + ${tomcat.version} + + + + true + + diff --git a/src/main/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java new file mode 100644 index 00000000..9826ef55 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -0,0 +1,15 @@ +package ru.javawebinar.topjava; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class Profiles { + public static final String + POSTGRES = "postgres", + HSQLDB = "hsqldb", + JDBC = "jdbc", + JPA = "jpa"; + + public static final String ACTIVE_DB = POSTGRES; +} diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties index e879739f..aeeb5746 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 diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index 51be31f5..8d4b6806 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -5,7 +5,6 @@ database.url=jdbc:postgresql://localhost:5432/topjava database.username=user database.password=password -database.driverClassName=org.postgresql.Driver database.init=true diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 8569a84b..d26c20cc 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -9,11 +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"> - - - - - @@ -21,15 +16,6 @@ - - - - - - - - @@ -66,4 +52,27 @@ --> + + + + + + + + + + + + + \ 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/MealServiceTest.java index 053dbe62..51432622 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -10,10 +10,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +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.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -32,6 +34,7 @@ }) @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.ACTIVE_DB) public class MealServiceTest { private static final Logger LOG = LoggerFactory.getLogger(MealServiceTest.class); private static StringBuilder results = new StringBuilder(); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index ce4a1b2a..bd592013 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.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -24,6 +26,7 @@ }) @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.ACTIVE_DB) public class UserServiceTest { @Autowired From 733c26109b6a934b31e8211e7711e4bf1d3d5009 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 02:46:02 +0300 Subject: [PATCH 46/63] 5_5_spring_data_jpa --- pom.xml | 16 +++++-- .../javawebinar/topjava/model/BaseEntity.java | 19 ++++++-- .../datajpa/CrudMealRepository.java | 11 +++++ .../datajpa/CrudUserRepository.java | 37 +++++++++++++++ .../datajpa/DataJpaMealRepositoryImpl.java | 45 ++++++++++++++++++ .../datajpa/DataJpaUserRepositoryImpl.java | 47 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 16 ++++++- 7 files changed, 182 insertions(+), 9 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/DataJpaMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java diff --git a/pom.xml b/pom.xml index 6e22821c..e54f98b1 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 8.5.5 4.3.4.RELEASE + 1.10.6.RELEASE 1.1.7 @@ -93,9 +94,9 @@ - org.springframework - spring-orm - ${spring.version} + org.springframework.data + spring-data-jpa + ${spring-data-jpa.version} @@ -181,5 +182,14 @@ + + + org.springframework + spring-framework-bom + ${spring.version} + pom + import + + diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index a4bfd321..2a922420 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -1,15 +1,20 @@ package ru.javawebinar.topjava.model; +import org.hibernate.Hibernate; +import org.springframework.data.domain.Persistable; + import javax.persistence.*; /** - * User: gkislin - * Date: 22.08.2014 + * Do not manipulate new (transient) entries in HashSet/HashMap without overriding hashCode + * http://stackoverflow.com/questions/5031614 + * + * @see org.springframework.data.jpa.domain.AbstractPersistable */ @MappedSuperclass // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) -public class BaseEntity { +public class BaseEntity implements Persistable { public static final int START_SEQ = 100000; @Id @@ -30,10 +35,12 @@ public void setId(Integer id) { this.id = id; } + @Override public Integer getId() { return id; } + @Override public boolean isNew() { return (getId() == null); } @@ -43,7 +50,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; } BaseEntity that = (BaseEntity) o; @@ -55,4 +62,8 @@ public int hashCode() { return (getId() == null) ? 0 : getId(); } + @Override + public String toString() { + return String.format("Entity of type %s with id: %s", getClass().getName(), getId()); + } } 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..e32c99ae --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -0,0 +1,11 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.javawebinar.topjava.model.Meal; + +/** + * gkislin + * 02.10.2016 + */ +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..4467eabe --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -0,0 +1,37 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.data.domain.Sort; +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; + +import java.util.List; + +/** + * gkislin + * 02.10.2016 + */ +@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); + + @Override + @Transactional + User save(User user); + + @Override + User findOne(Integer id); + + @Override + List findAll(Sort sort); + + User getByEmail(String email); + +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java new file mode 100644 index 00000000..4fb669ec --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java @@ -0,0 +1,45 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.beans.factory.annotation.Autowired; +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; + +/** + * GKislin + * 27.03.2015. + */ +@Repository +public class DataJpaMealRepositoryImpl implements MealRepository { + + @Autowired + private CrudMealRepository 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 getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java new file mode 100644 index 00000000..ae1da9c3 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java @@ -0,0 +1,47 @@ +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; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.List; + +/** + * GKislin + * 27.03.2015. + */ + +@Repository +public class DataJpaUserRepositoryImpl implements UserRepository { + private static final Sort SORT_NAME_EMAIL = new Sort("name", "email"); + + @Autowired + private CrudUserRepository 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.findOne(id); + } + + @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 d26c20cc..ac3cb108 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"> - + + + + @@ -75,4 +80,11 @@ p:username="${database.username}" p:password="${database.password}"/> + + + + + + + \ No newline at end of file From 8e927ac7e25d6401dac3a899853e1e94c3e60a47 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Thu, 12 Jan 2017 07:25:08 +0300 Subject: [PATCH 47/63] 5_6_spring_cache --- pom.xml | 11 +- .../topjava/service/UserService.java | 2 + .../topjava/service/UserServiceImpl.java | 11 + src/main/resources/cache/ehcache.xml | 28 ++ src/main/resources/cache/ehcache.xsd | 419 ++++++++++++++++++ src/main/resources/spring/spring-app.xml | 1 + src/main/resources/spring/spring-tools.xml | 24 + .../topjava/service/UserServiceTest.java | 6 + 8 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/cache/ehcache.xml create mode 100644 src/main/resources/cache/ehcache.xsd create mode 100644 src/main/resources/spring/spring-tools.xml diff --git a/pom.xml b/pom.xml index e54f98b1..8175e045 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,9 @@ 5.2.5.Final 5.3.4.Final + + + 2.10.3 @@ -84,7 +87,7 @@ org.springframework - spring-context + spring-context-support ${spring.version} @@ -121,6 +124,12 @@ 2.2.5 + + net.sf.ehcache + ehcache + ${ehcache.version} + + 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 85c15e48..080b94b9 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -23,4 +23,6 @@ public interface UserService { List getAll(); void update(User user); + + void evictCache(); } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index 572bd013..e21b470e 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.service; import org.springframework.beans.factory.annotation.Autowired; +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; @@ -22,12 +24,14 @@ public class UserServiceImpl implements UserService { @Autowired private UserRepository repository; + @CacheEvict(value = "users", allEntries = true) @Override public User save(User user) { Assert.notNull(user, "user must not be null"); return repository.save(user); } + @CacheEvict(value = "users", allEntries = true) @Override public void delete(int id) { checkNotFoundWithId(repository.delete(id), id); @@ -44,14 +48,21 @@ public User getByEmail(String email) throws NotFoundException { return checkNotFound(repository.getByEmail(email), "email=" + email); } + @Cacheable("users") @Override public List getAll() { return repository.getAll(); } + @CacheEvict(value = "users", allEntries = true) @Override public void update(User user) { Assert.notNull(user, "user must not be null"); repository.save(user); } + + @CacheEvict(value = "users", allEntries = true) + @Override + public void evictCache() { + } } diff --git a/src/main/resources/cache/ehcache.xml b/src/main/resources/cache/ehcache.xml new file mode 100644 index 00000000..8d420010 --- /dev/null +++ b/src/main/resources/cache/ehcache.xml @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/src/main/resources/cache/ehcache.xsd b/src/main/resources/cache/ehcache.xsd new file mode 100644 index 00000000..bfc19ddb --- /dev/null +++ b/src/main/resources/cache/ehcache.xsd @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index eff98648..fbf54e78 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -11,6 +11,7 @@ --> + diff --git a/src/main/resources/spring/spring-tools.xml b/src/main/resources/spring/spring-tools.xml new file mode 100644 index 00000000..784de2d7 --- /dev/null +++ b/src/main/resources/spring/spring-tools.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ 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 bd592013..09c5c44a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -1,5 +1,6 @@ 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; @@ -32,6 +33,11 @@ public class UserServiceTest { @Autowired private UserService service; + @Before + public void setUp() throws Exception { + service.evictCache(); + } + @Test public void testSave() throws Exception { User newUser = new User(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); From b48c7670fc0b267693654983af760f5203ddec2e Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 21:57:21 +0300 Subject: [PATCH 48/63] 6_0_entity_fix --- .../java/ru/javawebinar/topjava/model/BaseEntity.java | 2 +- src/main/java/ru/javawebinar/topjava/model/Meal.java | 2 +- src/main/java/ru/javawebinar/topjava/model/User.java | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index 2a922420..ae45655a 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -18,7 +18,7 @@ public class BaseEntity implements Persistable { public static final int START_SEQ = 100000; @Id - @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1) + @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = 100000) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") // PROPERTY access for id due to bug: https://hibernate.atlassian.net/browse/HHH-3718 @Access(value = AccessType.PROPERTY) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index f355d5fd..8459101c 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -102,7 +102,7 @@ public void setUser(User user) { @Override public String toString() { return "Meal{" + - "id=" + id + + "id=" + getId() + ", dateTime=" + dateTime + ", description='" + description + '\'' + ", calories=" + calories + diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index e76eb003..6788cf66 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -3,10 +3,10 @@ import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; +import org.hibernate.validator.constraints.Range; import ru.javawebinar.topjava.util.MealsUtil; import javax.persistence.*; -import javax.validation.constraints.Digits; import java.util.Date; import java.util.EnumSet; import java.util.Set; @@ -38,7 +38,7 @@ public class User extends NamedEntity { @Length(min = 5) private String password; - @Column(name = "enabled", nullable = false) + @Column(name = "enabled", nullable = false, columnDefinition = "bool default true") private boolean enabled = true; @Column(name = "registered", columnDefinition = "timestamp default now()") @@ -51,7 +51,7 @@ public class User extends NamedEntity { private Set roles; @Column(name = "calories_per_day", columnDefinition = "int default 2000") - @Digits(fraction = 0, integer = 4) + @Range(min = 10, max = 10000) private int caloriesPerDay = MealsUtil.DEFAULT_CALORIES_PER_DAY; public User() { @@ -121,7 +121,7 @@ public String getPassword() { @Override public String toString() { return "User (" + - "id=" + id + + "id=" + getId() + ", email=" + email + ", name=" + name + ", enabled=" + enabled + From cffdf78189f4971c6e8e4769e405b002f6cda2f6 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 21:58:28 +0300 Subject: [PATCH 49/63] 6_01_HW5_data_jpa --- .../datajpa/CrudMealRepository.java | 24 +++++++++++++++++++ .../datajpa/DataJpaMealRepositoryImpl.java | 24 +++++++++++++------ src/test/resources/logback-test.xml | 2 +- 3 files changed, 42 insertions(+), 8 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 e32c99ae..b55c3e39 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -1,11 +1,35 @@ 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; + /** * gkislin * 02.10.2016 */ +@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); + + @Override + Meal save(Meal item); + + @Query("SELECT m FROM Meal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC") + List getAll(@Param("userId") int userId); + + @SuppressWarnings("JpaQlInspection") + @Query("SELECT m from Meal m WHERE m.user.id=:userId AND m.dateTime BETWEEN :startDate AND :endDate ORDER BY m.dateTime DESC") + List getBetween(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("userId") int userId); + } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java index 4fb669ec..e49aeb0a 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; @@ -16,30 +17,39 @@ public class DataJpaMealRepositoryImpl implements MealRepository { @Autowired - private CrudMealRepository crudRepository; + private CrudMealRepository crudMealRepository; + + @Autowired + private CrudUserRepository crudUserRepository; @Override - public Meal save(Meal Meal, int userId) { - return null; + @Transactional + public Meal save(Meal meal, int userId) { + 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; + Meal meal = crudMealRepository.findOne(id); + return meal != null && meal.getUser().getId() == userId ? meal : null; } @Override public List getAll(int userId) { - return null; + return crudMealRepository.getAll(userId); } @Override public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { - return null; + return crudMealRepository.getBetween(startDate, endDate, userId); } } diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 67eb98da..e7c74a4b 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -7,7 +7,7 @@ - + From 212bb7ffc1fc8fde15be0b097e568273c3d5b77a Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 21:58:57 +0300 Subject: [PATCH 50/63] 6_02_HW5_profiles --- .../java/ru/javawebinar/topjava/Profiles.java | 4 +- src/main/resources/spring/spring-db.xml | 84 ++++++++++--------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java index 9826ef55..4ce31d6f 100644 --- a/src/main/java/ru/javawebinar/topjava/Profiles.java +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -9,7 +9,9 @@ public class Profiles { POSTGRES = "postgres", HSQLDB = "hsqldb", JDBC = "jdbc", - JPA = "jpa"; + JPA = "jpa", + DATAJPA = "datajpa"; public static final String ACTIVE_DB = POSTGRES; + public static final String DB_IMPLEMENTATION = DATAJPA; } diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index ac3cb108..1e78287f 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -11,52 +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"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -82,9 +42,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 85da1ae807d1f1b7b61af56bb870eea9d32721c7 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:00:01 +0300 Subject: [PATCH 51/63] 6_03_HW5_tests --- ...Test.java => AbstractMealServiceTest.java} | 49 +-------------- .../topjava/service/AbstractServiceTest.java | 59 +++++++++++++++++++ ...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 ++++ 9 files changed, 121 insertions(+), 63 deletions(-) rename src/test/java/ru/javawebinar/topjava/service/{MealServiceTest.java => AbstractMealServiceTest.java} (54%) create mode 100644 src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java rename src/test/java/ru/javawebinar/topjava/service/{UserServiceTest.java => AbstractUserServiceTest.java} (78%) 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/test/java/ru/javawebinar/topjava/service/MealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java similarity index 54% rename from src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index 51432622..6ebbed88 100644 --- a/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -1,66 +1,19 @@ package ru.javawebinar.topjava.service; -import org.junit.AfterClass; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.Stopwatch; -import org.junit.runner.Description; -import org.junit.runner.RunWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -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.SpringJUnit4ClassRunner; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.time.LocalDate; import java.time.Month; import java.util.Arrays; -import java.util.concurrent.TimeUnit; 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(SpringJUnit4ClassRunner.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(Profiles.ACTIVE_DB) -public class MealServiceTest { - private static final Logger LOG = LoggerFactory.getLogger(MealServiceTest.class); - private static StringBuilder results = new StringBuilder(); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @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("%-25s %7d", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); - results.append(result).append('\n'); - LOG.info(result + " ms\n"); - } - }; - - @AfterClass - public static void printResult() { - LOG.info("\n---------------------------------" + - "\nTest Duration, ms" + - "\n---------------------------------\n" + - results + - "---------------------------------\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..3a5e07c1 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -0,0 +1,59 @@ +package ru.javawebinar.topjava.service; + +import org.junit.AfterClass; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.junit.rules.Stopwatch; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.SpringJUnit4ClassRunner; + +import java.util.concurrent.TimeUnit; + +import static ru.javawebinar.topjava.Profiles.ACTIVE_DB; + +/** + * User: gkislin + */ +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringJUnit4ClassRunner.class) +@ActiveProfiles(ACTIVE_DB) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +abstract public class AbstractServiceTest { + private static final Logger LOG = LoggerFactory.getLogger(AbstractServiceTest.class); + + private static StringBuilder results = new StringBuilder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @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"); + } + }; + + @AfterClass + public static void printResult() { + LOG.info("\n---------------------------------" + + "\nTest Duration, ms" + + "\n---------------------------------\n" + + results + + "---------------------------------\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 78% rename from src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 09c5c44a..efd5adca 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -2,15 +2,8 @@ import org.junit.Before; 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.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.SpringJUnit4ClassRunner; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -21,14 +14,7 @@ import static ru.javawebinar.topjava.UserTestData.*; -@ContextConfiguration({ - "classpath:spring/spring-app.xml", - "classpath:spring/spring-db.xml" -}) -@RunWith(SpringJUnit4ClassRunner.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) -@ActiveProfiles(Profiles.ACTIVE_DB) -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 6e7b801f5e753b2ab6e8466147af400acf7fc690 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:00:53 +0300 Subject: [PATCH 52/63] 6_04_HW5_optional_fix_jdbc_profiles --- .../jdbc/JdbcMealRepositoryImpl.java | 33 ++++++++++++++++--- .../javawebinar/topjava/web/MealServlet.java | 9 +++-- .../ru/javawebinar/topjava/SpringMain.java | 9 +++-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java index f531dc87..d2a5b360 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -1,6 +1,7 @@ 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,10 +10,12 @@ 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 javax.sql.DataSource; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; @@ -21,8 +24,7 @@ * Date: 26.08.2014 */ -@Repository -public class JdbcMealRepositoryImpl implements MealRepository { +public abstract class JdbcMealRepositoryImpl implements MealRepository { private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); @@ -34,20 +36,41 @@ public class JdbcMealRepositoryImpl implements MealRepository { private SimpleJdbcInsert insertMeal; + protected abstract T toDbDateTime(LocalDateTime ldt); + @Autowired - public JdbcMealRepositoryImpl(DataSource dataSource) { + private void setDataSource(DataSource dataSource) { this.insertMeal = new SimpleJdbcInsert(dataSource) .withTableName("meals") .usingGeneratedKeyColumns("id"); } + @Repository + @Profile(Profiles.POSTGRES) + public static class Java8JdbcMealRepositoryImpl extends JdbcMealRepositoryImpl { + @Override + protected LocalDateTime toDbDateTime(LocalDateTime ldt) { + return ldt; + } + } + + @Repository + @Profile(Profiles.HSQLDB) + public static class TimestampJdbcMealRepositoryImpl extends JdbcMealRepositoryImpl { + + @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()) { @@ -87,6 +110,6 @@ public List getAll(int userId) { public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { return jdbcTemplate.query( "SELECT * FROM meals WHERE user_id=? AND date_time BETWEEN ? AND ? ORDER BY date_time DESC", - ROW_MAPPER, userId, startDate, endDate); + ROW_MAPPER, userId, toDbDateTime(startDate), toDbDateTime(endDate)); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 2306395b..81a008ab 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,8 +2,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.DateTimeUtil; import ru.javawebinar.topjava.web.meal.MealRestController; @@ -27,13 +27,16 @@ public class MealServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); - private ConfigurableApplicationContext springContext; + private ClassPathXmlApplicationContext springContext; private MealRestController mealController; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - 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.ACTIVE_DB, Profiles.DB_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 1cb3684c..02b2bd27 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.MealWithExceed; @@ -21,7 +20,11 @@ public class SpringMain { public static void main(String[] args) { // java 7 Automatic resource management - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml","spring/mock.xml")) { + try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { + appCtx.getEnvironment().setActiveProfiles(Profiles.ACTIVE_DB, Profiles.DB_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", "password", Role.ROLE_ADMIN)); From eb04992c1ce0bbfaaf8ac6bb5d60e76409c1f1d9 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:01:56 +0300 Subject: [PATCH 53/63] 6_05_HW5_optional_fetch_join --- .../java/ru/javawebinar/topjava/model/User.java | 9 +++++++++ .../topjava/repository/MealRepository.java | 4 ++++ .../topjava/repository/UserRepository.java | 4 ++++ .../repository/datajpa/CrudMealRepository.java | 2 ++ .../repository/datajpa/CrudUserRepository.java | 2 ++ .../datajpa/DataJpaMealRepositoryImpl.java | 5 +++++ .../datajpa/DataJpaUserRepositoryImpl.java | 5 +++++ .../topjava/service/MealService.java | 2 ++ .../topjava/service/MealServiceImpl.java | 6 ++++++ .../topjava/service/UserService.java | 4 +++- .../topjava/service/UserServiceImpl.java | 6 ++++++ .../service/AbstractMealServiceTest.java | 2 +- .../service/AbstractUserServiceTest.java | 2 +- .../service/datajpa/DataJpaMealServiceTest.java | 17 +++++++++++++++++ .../service/datajpa/DataJpaUserServiceTest.java | 16 ++++++++++++++++ 15 files changed, 83 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 6788cf66..a09cfc24 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -9,6 +9,7 @@ import javax.persistence.*; import java.util.Date; import java.util.EnumSet; +import java.util.List; import java.util.Set; /** @@ -54,6 +55,10 @@ public class User extends NamedEntity { @Range(min = 10, max = 10000) private int caloriesPerDay = MealsUtil.DEFAULT_CALORIES_PER_DAY; + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") + @OrderBy("dateTime DESC") + protected List meals; + public User() { } @@ -118,6 +123,10 @@ public String getPassword() { return password; } + 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 a274d756..1645a2f9 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -24,4 +24,8 @@ public interface MealRepository { // ORDERED dateTime Collection getBetween(LocalDateTime startDate, LocalDateTime endDate, 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 24c1c45b..47ded4ce 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -21,4 +21,8 @@ public interface UserRepository { User getByEmail(String email); List getAll(); + + default User getWithMeals(int id){ + throw new UnsupportedOperationException(); + } } 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 b55c3e39..530a8108 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java @@ -32,4 +32,6 @@ public interface CrudMealRepository extends JpaRepository { @Query("SELECT m from Meal m WHERE m.user.id=:userId AND m.dateTime BETWEEN :startDate AND :endDate ORDER BY m.dateTime DESC") List getBetween(@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); } 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 4467eabe..b48ef906 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -34,4 +34,6 @@ public interface CrudUserRepository extends JpaRepository { 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/DataJpaMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java index e49aeb0a..05d335e9 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepositoryImpl.java @@ -52,4 +52,9 @@ public List getAll(int userId) { public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { return crudMealRepository.getBetween(startDate, endDate, 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/DataJpaUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java index ae1da9c3..aecf1330 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java @@ -44,4 +44,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); + } } diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index 36ff1dea..1dbdd716 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -28,4 +28,6 @@ default Collection getBetweenDates(LocalDate startDate, LocalDate endDate, Meal update(Meal meal, int userId) throws NotFoundException; Meal save(Meal meal, int userId); + + Meal getWithUser(int id, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java index f8be8121..7cdb31cf 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.util.Assert; 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.Collection; @@ -54,4 +55,9 @@ public Meal save(Meal meal, int userId) { Assert.notNull(meal, "meal must not be null"); return repository.save(meal, userId); } + + @Override + public Meal getWithUser(int id, int userId) { + return ValidationUtil.checkNotFoundWithId(repository.getWithUser(id, userId), id); + } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 080b94b9..aa06ef36 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -23,6 +23,8 @@ public interface UserService { List getAll(); void update(User user); - + void evictCache(); + + User getWithMeals(int id); } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index e21b470e..8f8123d4 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.util.Assert; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.ValidationUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.util.List; @@ -65,4 +66,9 @@ public void update(User user) { @Override public void evictCache() { } + + @Override + public User getWithMeals(int id) { + return ValidationUtil.checkNotFoundWithId(repository.getWithMeals(id), id); + } } diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index 6ebbed88..3be5746a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -16,7 +16,7 @@ public abstract class AbstractMealServiceTest extends AbstractServiceTest { @Autowired - private MealService service; + protected MealService service; @Test public void testDelete() throws Exception { diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index efd5adca..faed42c3 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -17,7 +17,7 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired - private UserService service; + protected UserService service; @Before public void setUp() throws Exception { 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..0a76c1c9 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,27 @@ package ru.javawebinar.topjava.service.datajpa; +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; @ActiveProfiles(DATAJPA) public class DataJpaMealServiceTest extends AbstractMealServiceTest { + @Test + public void testGetWithUser() throws Exception { + Meal adminMeal = service.getWithUser(ADMIN_MEAL_ID, ADMIN_ID); + MATCHER.assertEquals(ADMIN_MEAL1, adminMeal); + UserTestData.MATCHER.assertEquals(UserTestData.ADMIN, adminMeal.getUser()); + } + + @Test(expected = NotFoundException.class) + public void testGetWithUserNotFound() throws Exception { + service.getWithUser(MEAL1_ID, 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..999479ec 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,26 @@ package ru.javawebinar.topjava.service.datajpa; +import org.junit.Test; import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.MealTestData; +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.*; @ActiveProfiles(DATAJPA) public class DataJpaUserServiceTest extends AbstractUserServiceTest { + @Test + public void testGetWithMeals() throws Exception { + User user = service.getWithMeals(USER_ID); + MATCHER.assertEquals(USER, user); + MealTestData.MATCHER.assertCollectionEquals(MealTestData.MEALS, user.getMeals()); + } + + @Test(expected = NotFoundException.class) + public void testGetWithMealsNotFound() throws Exception { + service.getWithMeals(1); + } } \ No newline at end of file From d06cc4ae06bae50143b06c0fe3e685d1f7081ece Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:05:54 +0300 Subject: [PATCH 54/63] 6_06_HW5_graph_batch_size --- src/main/java/ru/javawebinar/topjava/model/User.java | 8 +++++++- .../topjava/repository/datajpa/CrudUserRepository.java | 6 ++++-- 2 files changed, 11 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 a09cfc24..11ed707c 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.model; +import org.hibernate.annotations.BatchSize; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; @@ -19,12 +20,15 @@ @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 +@NamedEntityGraph(name = User.GRAPH_WITH_MEALS, attributeNodes = {@NamedAttributeNode("meals")}) @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) public class User extends NamedEntity { + public static final String GRAPH_WITH_MEALS = "User.withMeals"; + public static final String DELETE = "User.delete"; public static final String ALL_SORTED = "User.getAllSorted"; public static final String BY_EMAIL = "User.getByEmail"; @@ -49,6 +53,8 @@ public class User extends NamedEntity { @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) @Column(name = "role") @ElementCollection(fetch = FetchType.EAGER) +// @Fetch(FetchMode.SUBSELECT) + @BatchSize(size = 200) private Set roles; @Column(name = "calories_per_day", 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 b48ef906..8f273215 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.repository.datajpa; import org.springframework.data.domain.Sort; +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; @@ -18,7 +19,6 @@ 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); @@ -34,6 +34,8 @@ public interface CrudUserRepository extends JpaRepository { User getByEmail(String email); - @Query("SELECT u FROM User u LEFT JOIN FETCH u.meals WHERE u.id = ?1") + // @Query("SELECT u FROM User u LEFT JOIN FETCH u.meals WHERE u.id = ?1") + @EntityGraph(value = User.GRAPH_WITH_MEALS) + @Query("SELECT u FROM User u WHERE u.id=?1") User getWithMeals(int id); } From 12637153a8f16be412478545fe1501394695e402 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:06:35 +0300 Subject: [PATCH 55/63] 6_07_add_test_validation --- pom.xml | 9 ++++++++ .../service/AbstractMealServiceTest.java | 10 +++++++++ .../topjava/service/AbstractServiceTest.java | 22 +++++++++++++++++++ .../service/AbstractUserServiceTest.java | 12 +++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8175e045..395449e0 100644 --- a/pom.xml +++ b/pom.xml @@ -118,10 +118,19 @@ hibernate-validator ${hibernate-validator.version} + + javax.el javax.el-api 2.2.5 + provided + + + org.glassfish.web + javax.el + 2.2.4 + provided diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java index 3be5746a..5225d018 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java @@ -5,10 +5,12 @@ 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 java.util.Arrays; +import static java.time.LocalDateTime.of; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_ID; @@ -73,4 +75,12 @@ public void testGetBetween() throws Exception { MATCHER.assertCollectionEquals(Arrays.asList(MEAL3, MEAL2, MEAL1), service.getBetweenDates(LocalDate.of(2015, Month.MAY, 30), LocalDate.of(2015, Month.MAY, 30), USER_ID)); } + + @Test + public void testValidation() throws Exception { + validateRootCause(() -> service.save(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID), ConstraintViolationException.class); + validateRootCause(() -> service.save(new Meal(null, null, "Description", 300), USER_ID), ConstraintViolationException.class); + validateRootCause(() -> service.save(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID), ConstraintViolationException.class); + validateRootCause(() -> service.save(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 5001), USER_ID), ConstraintViolationException.class); + } } \ 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 3a5e07c1..4bca6b27 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.service; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Rule; import org.junit.rules.ExpectedException; import org.junit.rules.Stopwatch; @@ -16,6 +17,7 @@ import java.util.concurrent.TimeUnit; +import static org.hamcrest.CoreMatchers.instanceOf; import static ru.javawebinar.topjava.Profiles.ACTIVE_DB; /** @@ -56,4 +58,24 @@ public static void printResult() { "---------------------------------\n"); results.setLength(0); } + + public static void validateRootCause(Runnable runnable, Class exceptionClass) { + try { + runnable.run(); + Assert.fail("Expected " + exceptionClass.getName()); + } catch (Exception e) { + Assert.assertThat(getRootCause(e), instanceOf(exceptionClass)); + } + } + + // http://stackoverflow.com/a/28565320/548473 + public static Throwable getRootCause(Throwable t) { + Throwable result = t; + Throwable cause; + + while (null != (cause = result.getCause()) && (result != cause)) { + result = cause; + } + return result; + } } \ 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 faed42c3..5223fa9a 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.util.exception.NotFoundException; +import javax.validation.ConstraintViolationException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -23,7 +24,7 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { public void setUp() throws Exception { service.evictCache(); } - + @Test public void testSave() throws Exception { User newUser = new User(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); @@ -79,4 +80,13 @@ public void testUpdate() throws Exception { service.update(updated); MATCHER.assertEquals(updated, service.get(USER_ID)); } + + @Test + public void testValidation() throws Exception { + validateRootCause(() -> service.save(new User(null, " ", "invalid@yandex.ru", "password", Role.ROLE_USER)), ConstraintViolationException.class); + validateRootCause(() -> service.save(new User(null, "User", " ", "password", Role.ROLE_USER)), ConstraintViolationException.class); + validateRootCause(() -> service.save(new User(null, "User", "invalid@yandex.ru", " ", Role.ROLE_USER)), ConstraintViolationException.class); + validateRootCause(() -> service.save(new User(null, "User", "invalid@yandex.ru", "password", 9, true, Collections.emptySet())), ConstraintViolationException.class); + validateRootCause(() -> service.save(new User(null, "User", "invalid@yandex.ru", "password", 10001, true, Collections.emptySet())), ConstraintViolationException.class); + } } \ No newline at end of file From 4fb92e7b6898f28834b247323a24b354c6bb1edc Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:07:35 +0300 Subject: [PATCH 56/63] 6_08_profile_resolver --- .../java/ru/javawebinar/topjava/Profiles.java | 23 ++++++++++++++----- .../javawebinar/topjava/web/MealServlet.java | 2 +- .../topjava/ActiveDbProfileResolver.java | 16 +++++++++++++ .../ru/javawebinar/topjava/SpringMain.java | 8 +++---- .../topjava/service/AbstractServiceTest.java | 4 ++-- 5 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java diff --git a/src/main/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java index 4ce31d6f..b479919f 100644 --- a/src/main/java/ru/javawebinar/topjava/Profiles.java +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -1,9 +1,7 @@ package ru.javawebinar.topjava; -/** - * User: gkislin - * Date: 19.08.2014 - */ +import org.springframework.util.ClassUtils; + public class Profiles { public static final String POSTGRES = "postgres", @@ -12,6 +10,19 @@ public class Profiles { JPA = "jpa", DATAJPA = "datajpa"; - public static final String ACTIVE_DB = POSTGRES; public static final String DB_IMPLEMENTATION = DATAJPA; -} + + public static String getActiveDbProfile() { + try { + Class.forName("org.postgresql.Driver", true, ClassUtils.getDefaultClassLoader()); + return Profiles.POSTGRES; + } catch (ClassNotFoundException ex) { + try { + Class.forName("org.hsqldb.jdbcDriver", true, ClassUtils.getDefaultClassLoader()); + return Profiles.HSQLDB; + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Could not resolve DB profile"); + } + } + } +} \ 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 81a008ab..4e326ad5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -35,7 +35,7 @@ public void init(ServletConfig config) throws ServletException { super.init(config); 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.ACTIVE_DB, Profiles.DB_IMPLEMENTATION); + springContext.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.DB_IMPLEMENTATION); springContext.refresh(); mealController = springContext.getBean(MealRestController.class); } 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..446bec52 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java @@ -0,0 +1,16 @@ +package ru.javawebinar.topjava; + +import org.springframework.test.context.ActiveProfilesResolver; + +/** + * gkislin + * 18.01.2017 + */ +//http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver +public class ActiveDbProfileResolver implements ActiveProfilesResolver { + + @Override + public String[] resolve(Class aClass) { + return new String[]{Profiles.getActiveDbProfile()}; + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index 02b2bd27..4c4065ef 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -1,8 +1,6 @@ package ru.javawebinar.topjava; import org.springframework.context.support.GenericXmlApplicationContext; -import ru.javawebinar.topjava.model.Role; -import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.MealWithExceed; import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; @@ -13,6 +11,8 @@ import java.util.Arrays; import java.util.List; +import static ru.javawebinar.topjava.UserTestData.USER_ID; + /** * User: gkislin * Date: 22.08.2014 @@ -21,13 +21,13 @@ public class SpringMain { public static void main(String[] args) { // java 7 Automatic resource management try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { - appCtx.getEnvironment().setActiveProfiles(Profiles.ACTIVE_DB, Profiles.DB_IMPLEMENTATION); + appCtx.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.DB_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", "password", Role.ROLE_ADMIN)); + adminUserController.get(USER_ID); System.out.println(); MealRestController mealController = appCtx.getBean(MealRestController.class); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index 4bca6b27..38900ffe 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -14,11 +14,11 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.ActiveDbProfileResolver; import java.util.concurrent.TimeUnit; import static org.hamcrest.CoreMatchers.instanceOf; -import static ru.javawebinar.topjava.Profiles.ACTIVE_DB; /** * User: gkislin @@ -28,7 +28,7 @@ "classpath:spring/spring-db.xml" }) @RunWith(SpringJUnit4ClassRunner.class) -@ActiveProfiles(ACTIVE_DB) +@ActiveProfiles(resolver = ActiveDbProfileResolver.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) abstract public class AbstractServiceTest { private static final Logger LOG = LoggerFactory.getLogger(AbstractServiceTest.class); From a1423e7a891fb243005ca44c24e721fc4266c4d2 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:07:55 +0300 Subject: [PATCH 57/63] 6_09_hibernate_cache --- pom.xml | 5 ++++ .../ru/javawebinar/topjava/model/User.java | 4 +++ .../topjava/repository/JpaUtil.java | 25 +++++++++++++++++++ src/main/resources/cache/ehcache.xml | 3 +++ src/main/resources/spring/spring-db.xml | 8 ++++++ .../service/AbstractUserServiceTest.java | 5 ++++ 6 files changed, 50 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java diff --git a/pom.xml b/pom.xml index 395449e0..3f3fe730 100644 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,11 @@ 2.2.4 provided + + org.hibernate + hibernate-ehcache + ${hibernate.version} + net.sf.ehcache diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 11ed707c..5d6fa999 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.model; import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; @@ -17,6 +19,7 @@ * User: gkislin * Date: 22.08.2014 */ +@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"), @@ -49,6 +52,7 @@ public class User extends NamedEntity { @Column(name = "registered", columnDefinition = "timestamp default now()") private Date registered = new Date(); + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Enumerated(EnumType.STRING) @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) @Column(name = "role") 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..75b3d82b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/JpaUtil.java @@ -0,0 +1,25 @@ +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().evictQueryRegions(); + sf.getCache().evictDefaultQueryRegion(); + sf.getCache().evictCollectionRegions(); + sf.getCache().evictEntityRegions(); + } +} diff --git a/src/main/resources/cache/ehcache.xml b/src/main/resources/cache/ehcache.xml index 8d420010..b0275e58 100644 --- a/src/main/resources/cache/ehcache.xml +++ b/src/main/resources/cache/ehcache.xml @@ -25,4 +25,7 @@ diskExpiryThreadIntervalSeconds="1" memoryStoreEvictionPolicy="LRU"/> + + diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 1e78287f..e37d7143 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -64,6 +64,12 @@ + + + + + + @@ -79,6 +85,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 5223fa9a..6cb8c530 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -6,6 +6,7 @@ import org.springframework.dao.DataAccessException; 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; @@ -20,9 +21,13 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { @Autowired protected UserService service; + @Autowired + protected JpaUtil jpaUtil; + @Before public void setUp() throws Exception { service.evictCache(); + jpaUtil.clear2ndLevelHibernateCache(); } @Test From e1f0871cbc6528526c27f1400f061b298e937962 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:08:12 +0300 Subject: [PATCH 58/63] 6_10_cascade_ddl --- src/main/java/ru/javawebinar/topjava/model/BaseEntity.java | 1 + src/main/java/ru/javawebinar/topjava/model/Meal.java | 3 +++ src/main/java/ru/javawebinar/topjava/model/User.java | 2 +- src/main/resources/spring/spring-db.xml | 7 ++++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index ae45655a..a111a6b8 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -19,6 +19,7 @@ public class BaseEntity implements Persistable { @Id @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = 100000) +// @Column(name = "id", unique = true, nullable = false, columnDefinition = "integer default nextval('global_seq')") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") // PROPERTY access for id due to bug: https://hibernate.atlassian.net/browse/HHH-3718 @Access(value = AccessType.PROPERTY) diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 8459101c..692f73a9 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.NotBlank; import org.hibernate.validator.constraints.Range; @@ -43,6 +45,7 @@ public class Meal extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private User user; public Meal() { diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 5d6fa999..752dc812 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -65,7 +65,7 @@ public class User extends NamedEntity { @Range(min = 10, max = 10000) private int caloriesPerDay = MealsUtil.DEFAULT_CALORIES_PER_DAY; - @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") + @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) @OrderBy("dateTime DESC") protected List meals; diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index e37d7143..1f4794fd 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -69,7 +69,12 @@ - + From 582c6175f6fe37c819657b1e6390994c1e3fff05 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:11:21 +0300 Subject: [PATCH 59/63] 6_11_spring_web --- pom.xml | 18 +++++++++++++++--- .../javawebinar/topjava/web/MealServlet.java | 16 +++------------- src/main/webapp/WEB-INF/web.xml | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 3f3fe730..0b9a4142 100644 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,18 @@ ${spring-data-jpa.version} + + org.springframework + spring-web + ${spring.version} + + + commons-logging + commons-logging + + + + org.hibernate hibernate-core @@ -146,9 +158,9 @@ - javax.servlet - javax.servlet-api - 3.1.0 + org.apache.tomcat + tomcat-servlet-api + ${tomcat.version} provided diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 4e326ad5..40347161 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,8 +2,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.support.ClassPathXmlApplicationContext; -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.util.DateTimeUtil; import ru.javawebinar.topjava.web.meal.MealRestController; @@ -27,25 +27,15 @@ public class MealServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); - private ClassPathXmlApplicationContext springContext; private MealRestController mealController; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - 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.DB_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/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 686b79f8..9e0d0ade 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -5,6 +5,22 @@ version="3.1"> 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 8ba833266a7268027e330ed5197b25d8be47498d Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:11:37 +0300 Subject: [PATCH 60/63] 6_12_jsp_jstl_i18n --- .../javawebinar/topjava/web/UserServlet.java | 16 +++++++- src/main/resources/messages/app.properties | 15 +++++++ src/main/resources/messages/app_ru.properties | 15 +++++++ src/main/webapp/css/style.css | 8 ++++ src/main/webapp/fragments/bodyHeader.jsp | 5 +++ src/main/webapp/fragments/footer.jsp | 5 +++ src/main/webapp/fragments/headTag.jsp | 10 +++++ src/main/webapp/index.html | 19 --------- src/main/webapp/index.jsp | 26 ++++++++++++ src/main/webapp/meals.jsp | 3 +- src/main/webapp/users.jsp | 40 ++++++++++++++++--- 11 files changed, 135 insertions(+), 27 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/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 97e0c674..98374168 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,8 +1,12 @@ 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.AuthorizedUser; +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; @@ -19,6 +23,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); + } + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int userId = Integer.valueOf(request.getParameter("userId")); AuthorizedUser.setId(userId); @@ -27,7 +40,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/resources/messages/app.properties b/src/main/resources/messages/app.properties new file mode 100644 index 00000000..f2e54cdf --- /dev/null +++ b/src/main/resources/messages/app.properties @@ -0,0 +1,15 @@ +app.title=Calories management +app.home=Home +app.footer=Project Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery application +app.login=Login as + +users.title=User list +users.name=Name +users.email=Email +users.roles=Roles +users.active=Active +users.registered=Registered + +meals.title=Meals list + +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..c7e811d4 --- /dev/null +++ b/src/main/resources/messages/app_ru.properties @@ -0,0 +1,15 @@ +app.title=\u041F\u043E\u0434\u0441\u0447\u0435\u0442 \u043A\u0430\u043B\u043E\u0440\u0438\u0439 +app.home=\u0413\u043B\u0430\u0432\u043D\u0430\u044F +app.footer=\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0443 Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery +app.login=\u0417\u0430\u0439\u0442\u0438 \u043A\u0430\u043A + +users.title=\u0421\u043F\u0438\u0441\u043E\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 +users.name=\u0418\u043C\u044F +users.email=\u041F\u043E\u0447\u0442\u0430 +users.roles=\u0420\u043E\u043B\u0438 +users.active=\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0439 +users.registered=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D + +meals.title=\u0421\u043F\u0438\u0441\u043E\u043A \u0435\u0434\u044B + +common.select=\u0412\u044B\u0431\u0440\u0430\u0442\u044C \ No newline at end of file diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css index 0c9fc667..36305de3 100644 --- a/src/main/webapp/css/style.css +++ b/src/main/webapp/css/style.css @@ -22,3 +22,11 @@ dd { .exceeded { color: red; } + +header, footer { + background: none repeat scroll 0 0 #A6C9E2; + color: #2E6E9E; + font-size: 20px; + padding: 5px 20px; + margin: 6px 0; +} \ No newline at end of file diff --git a/src/main/webapp/fragments/bodyHeader.jsp b/src/main/webapp/fragments/bodyHeader.jsp new file mode 100644 index 00000000..16432eca --- /dev/null +++ b/src/main/webapp/fragments/bodyHeader.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/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 f9289bfc..00000000 --- a/src/main/webapp/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Java Enterprise (Topjava) - - -

    Проект Java Enterprise (Topjava)

    -
    -
    - Meal list of  - - - - - diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 00000000..a6cccf49 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,26 @@ +<%@ 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" %> + + + + + + + +
    +
    + : + + +
      +
    • +
    • +
    +
    + + + \ No newline at end of file diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 090370b6..2266823c 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -9,8 +9,7 @@
    -

    Home

    -

    Meal list

    +

    Meal list

    From Date:
    diff --git a/src/main/webapp/users.jsp b/src/main/webapp/users.jsp index fbd40d3d..b3df4ac3 100644 --- a/src/main/webapp/users.jsp +++ b/src/main/webapp/users.jsp @@ -1,10 +1,40 @@ <%@ 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" %> + + + - - User list - + -

    Home

    -

    User list

    + + +
    +

    + +

    + + + + + + + + + + + + + + + + + + + +
    ${user.email}${user.roles}<%=user.isEnabled()%> +
    +
    + From 23187444ef02431099fc85d5d3eb45664727a874 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 18 Jan 2017 22:11:52 +0300 Subject: [PATCH 61/63] 6_13_tomcat_pool_jndi --- pom.xml | 64 +++++++++++++++++++++++++ src/main/resources/db/tomcat.properties | 7 +++ src/main/resources/spring/spring-db.xml | 9 +++- src/main/resources/tomcat/context.xml | 57 ++++++++++++++++++++++ 4 files changed, 136 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 0b9a4142..d3417773 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,70 @@ -Dfile.encoding=UTF-8 + + + + org.codehaus.cargo + cargo-maven2-plugin + 1.5.0 + + + tomcat8x + + UTF-8 + tomcat,datajpa + + + + org.postgresql + postgresql + + + + + + + src/main/resources/tomcat/context.xml + conf/Catalina/localhost/ + context.xml.default + + + + + + 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..09ed9c25 --- /dev/null +++ b/src/main/resources/db/tomcat.properties @@ -0,0 +1,7 @@ +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 1f4794fd..8ed64422 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"> @@ -41,6 +43,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 e1c2aad1258e81c2b2646e14e55ea0d3dffc1bbf Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Fri, 20 Jan 2017 23:33:30 +0300 Subject: [PATCH 62/63] 6_14_spring_webmvc --- pom.xml | 2 +- .../topjava/web/RootController.java | 39 +++++++++++++++++++ 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 src/main/webapp/{ => WEB-INF/jsp}/meal.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 | 17 ++++++++ src/main/webapp/{ => resources}/css/style.css | 0 14 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/RootController.java create mode 100644 src/main/resources/spring/spring-mvc.xml rename src/main/webapp/{ => WEB-INF/jsp}/fragments/bodyHeader.jsp (50%) 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}/meal.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 d3417773..8f43326e 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ org.springframework - spring-web + spring-webmvc ${spring.version} 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..f987016a --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -0,0 +1,39 @@ +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.service.UserService; + +import javax.servlet.http.HttpServletRequest; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +@Controller +public class RootController { + @Autowired + private UserService service; + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String root() { + return "index"; + } + + @RequestMapping(value = "/users", method = RequestMethod.GET) + public String users(Model model) { + model.addAttribute("users", service.getAll()); + return "users"; + } + + @RequestMapping(value = "/users", method = RequestMethod.POST) + public String setUser(HttpServletRequest request) { + int userId = Integer.valueOf(request.getParameter("userId")); + AuthorizedUser.setId(userId); + return "redirect:meals"; + } +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e9b900b2..8616ff8e 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 fbf54e78..c3e7587c 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -16,7 +16,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 50% rename from src/main/webapp/fragments/bodyHeader.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 16432eca..903b0ef0 100644 --- a/src/main/webapp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -2,4 +2,4 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> -
     | 
    \ No newline at end of file +
     | 
    \ 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/meal.jsp b/src/main/webapp/WEB-INF/jsp/meal.jsp similarity index 100% rename from src/main/webapp/meal.jsp rename to src/main/webapp/WEB-INF/jsp/meal.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 9e0d0ade..46061022 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -18,9 +18,25 @@ + org.springframework.web.context.ContextLoaderListener + + mvc-dispatcher + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + classpath:spring/spring-mvc.xml + + 1 + + + mvc-dispatcher + / + + + 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 f3b82b7f3bd58c0cf90b22bc386d0541d7cca4ce Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Fri, 20 Jan 2017 23:34:03 +0300 Subject: [PATCH 63/63] 6_15_spring_i18n --- .../messages/app.properties | 0 config/messages/app_ru.properties | 15 +++++++++++++++ src/main/resources/messages/app_ru.properties | 15 --------------- src/main/resources/spring/spring-mvc.xml | 18 ++++++++++++++++++ .../WEB-INF/jsp/fragments/bodyHeader.jsp | 1 - .../webapp/WEB-INF/jsp/fragments/footer.jsp | 1 - .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 1 - src/main/webapp/WEB-INF/jsp/index.jsp | 2 -- src/main/webapp/WEB-INF/jsp/users.jsp | 2 -- 9 files changed, 33 insertions(+), 22 deletions(-) rename {src/main/resources => config}/messages/app.properties (100%) create mode 100644 config/messages/app_ru.properties delete mode 100644 src/main/resources/messages/app_ru.properties 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/config/messages/app_ru.properties b/config/messages/app_ru.properties new file mode 100644 index 00000000..68fe5e63 --- /dev/null +++ b/config/messages/app_ru.properties @@ -0,0 +1,15 @@ +app.title=Подсчет калорий +app.home=Главная +app.footer=Приложение по проекту Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery +app.login=Зайти как + +users.title=Список пользователей +users.name=Имя +users.email=Почта +users.roles=Роли +users.active=Активный +users.registered=Зарегистрирован + +meals.title=Список еды + +common.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 deleted file mode 100644 index c7e811d4..00000000 --- a/src/main/resources/messages/app_ru.properties +++ /dev/null @@ -1,15 +0,0 @@ -app.title=\u041F\u043E\u0434\u0441\u0447\u0435\u0442 \u043A\u0430\u043B\u043E\u0440\u0438\u0439 -app.home=\u0413\u043B\u0430\u0432\u043D\u0430\u044F -app.footer=\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0443 Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery -app.login=\u0417\u0430\u0439\u0442\u0438 \u043A\u0430\u043A - -users.title=\u0421\u043F\u0438\u0441\u043E\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 -users.name=\u0418\u043C\u044F -users.email=\u041F\u043E\u0447\u0442\u0430 -users.roles=\u0420\u043E\u043B\u0438 -users.active=\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0439 -users.registered=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D - -meals.title=\u0421\u043F\u0438\u0441\u043E\u043A \u0435\u0434\u044B - -common.select=\u0412\u044B\u0431\u0440\u0430\u0442\u044C \ 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 7c71d5af..12ac233d 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 903b0ef0..f37d8bfd 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,5 +1,4 @@ <%@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/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index c59bff2e..75f21f87 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" %> -
    \ 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..4f44c6de 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.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="c" uri="http://java.sun.com/jstl/core" %> - diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index a6cccf49..6389c1a1 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -2,8 +2,6 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp index b3df4ac3..d2c72158 100644 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ b/src/main/webapp/WEB-INF/jsp/users.jsp @@ -2,8 +2,6 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - -