From 2c47cd7aca8063a80fbb1806f466e46b0918fa2f Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Wed, 26 Aug 2015 02:13:54 +0300 Subject: [PATCH 001/108] Add readme and project description --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++ description.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 README.md create mode 100644 description.md diff --git a/README.md b/README.md new file mode 100644 index 000000000000..b45455288acd --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +Java Enterprise Online Project +=============================== +Наиболее востребованные технологии /инструменты / фреймворки Java Enterprise: +Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + plugins. + +**Старт проекта 10.09.2015** + +Вводное занятие +=============== + +## Описание проекта + +#### Демо разрабатываемого приложения + +## Системы управления версиями. Git. + +- Система управления версиями. VCS/DVSC. +- Ресурсы: + - Интерактивная Git обучалка + - Основы Git + - Working with remote repositories + - Видео по обучению Git + - Как начать работать с GitHub: быстрый старт + - Справочник в графическом виде + - Git Overview + +## Работа с проектом (выполнять инструкции) +- **Prepare_ to_ HW0.patch (скачать и положить в каталог вашего проекта)** +- Доступно о Java 8 Lambda + +## Инструкция по шагам (из видео): +- Установить ПО (git, JDK8, IntelliJ IDEA, Maven) +- Создать аккаунт на GitHub +- Сделать Fork ЭТОГО проекта (https://github.com/JavaOPs/topjava) +- Сделать локальный репозиторий проекта: +
git clone https://github.com/[Ваш аккаунт]/topjava.git
+- Открыть и настроить проект в IDEA +- По ходу видео сделать Apply Patch... скаченного патча Prepare_ to_ HW0.patch +- Закоммитить и запушить изменения (commit + push) +- Сделать ветку домашнего задания +- Выполнить задание и залить на GitHub (commit + push) +- Отослать в skype мне ссылку вашей ветки GitHub (при оплате расширенного участия) +- Переключиться в основную ветку проекта master. + +### Вы готовы к участию в проекте! + +- Ресурсы + - Java 8: Lambda выражения + - Java 8 Date and Time API + - Java 8: Потоки + - Pуководство по Java 8 Stream + - Лямбда-выражения в Java 8 diff --git a/description.md b/description.md new file mode 100644 index 000000000000..c58f903605d4 --- /dev/null +++ b/description.md @@ -0,0 +1,64 @@ +#### Разработка полнофункционального Spring/JPA Enterprise приложения c авторизацией и правами доступа на основе ролей используя наиболее популярные инструменты и технологии Java: Maven, Spring, Security, JPA(Hibernate), REST(Jackson), Bootstrap, jQuery + plugins +- Основное внимание будет уделяться способам решения многочисленных проблем разработки в Spring/JPA, а также структурному (красивому и надежному) java кодированию и архитектуре приложения. +- Каждая итерация проекта в закрепляется домашним заданием по реализации схожей функциональности. Следующее занятие начинается с разбора домашних заданий. +- Большое внимание уделяется тестированию кода: в проекте ~ 85 JUnit тестов. +- Несмотря на относительно небольшой размер, приложение разрабатывается с нуля как большой проект (например мы используем кэш 2-го уровня Hibernate, настраиваем Jenkins для работы с ленивой загрузкой +Hibernate, делаем конверторы для типов LocalDateTime (Java 8 time API), которые еще не поддерживаются ни JPA/Hibernate, ни Jackson/json). + Разбираются архитектурные паттерны: слои приложения и как правильно разбивать логику по слоям, когда нужно применят Data Transfer Object. + Т.е на выходе получается не учебный проект, а хорошо маштабируемый шаблон для большого проекта на всех пройденных технологиях. +- Большое внимание уделяется деталям: популяция базы, использование транзакционности, тесты сервисов и REST + контроллеров, насторойка EntityManagerFactory, + выбор реализации пула коннектов. Особое внимание уделяется работе с базой: через Spring JDBC, Spring ORM и + Spring Data Jpa. +- Используются самые востребованные на сегодняшний момент фреймворки: Maven, Spring Security 4 + вместе с Spring Security Test, наиболее удобный для работы с базой проект Spring Data Jpa, библиотека логирования logback, реализующая SLF4J, повсеместно используемый Bootstrap и jQuery. + +## Архитектура проекта. Персистентность. +- Системы управления версиями +- Java 8: Lambda, Stream API +- Обзор используемых в проекте технологий и инструментов. +- Maven, другие инструменты сборки. +- WAR. Веб-контейнер Tomcat. Сервлеты. +- Логирование. +- Обзор стандартных библиотек. Apache Commons, Guava +- Слои приложения. Создание каркаса приложения. +- Обзор Spring Framework. Spring Context. +- Тестирование через JUnit. +- Spring Test +- Базы данных. PostgreSQL. Обзор NoSQL и Java persistence solution без ORM. +- Настройка Database в IDEA. +- Скрипты инициализации базы. Spring Jdbc Template. +- Spring: инициализация и популирование DB +- ORM. Hibernate. JPA. +- Поддержка HSQLDB +- Транзакции +- Профили Maven и Spring +- Пул коннектов +- Spring Data JPA +- Spring кэш +- Кэш Hibernate + +## Разработка WEB +- Spring Web +- JPS, JSTL, i18n +- Tomcat maven plugin. JNDI +- Spring Web MVC +- Spring Internationalization +- Тестирование Spring MVC +- REST контроллеры +- Тестирование REST контроллеров. Jackson. +- jackson-datatype-hibernate. Тестирование через матчеры. +- Тестирование через SoapUi. UTF-8 +- WebJars. Dandelion bundles +- Bootstrap. Datatables. +- AJAX. jQuery. Notifications. +- Spring Security +- Spring Binding/Validation +- Работа с Datatables через Ajax. +- Spring Security Test +- Encoding password +- CSRF +- form-login. Spring Security Taglib +- Handler interceptor +- Spring Exception Handling +- Деплой в Heroku From db1c94981aabaf06140f52688fb640f22197089c Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Tue, 3 Nov 2015 00:33:39 +0300 Subject: [PATCH 002/108] Update description --- README.md | 33 +++++++++++++++++++++++++++------ cv.md | 34 ++++++++++++++++++++++++++++++++++ description.md | 7 +++++-- 3 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 cv.md diff --git a/README.md b/README.md index b45455288acd..105a96be146a 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,19 @@ Java Enterprise Online Project Наиболее востребованные технологии /инструменты / фреймворки Java Enterprise: Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + plugins. -**Старт проекта 10.09.2015** - Вводное занятие =============== ## Описание проекта +### Демо разрабатываемого приложения +### Ваше резюме + +#### Wiki +#### Форум + -#### Демо разрабатываемого приложения -## Системы управления версиями. Git. +## ![video](http://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico) Системы управления версиями. Git. - Система управления версиями. VCS/DVSC. @@ -24,9 +27,10 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Как начать работать с GitHub: быстрый старт - Справочник в графическом виде - Git Overview + - Видеокурс по Git -## Работа с проектом (выполнять инструкции) -- **Prepare_ to_ HW0.patch (скачать и положить в каталог вашего проекта)** +## ![video](http://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico) Работа с проектом (выполнять инструкции) +- **Prepare_ to_ HW0.patch (скачать и положить в каталог вашего проекта)** - Доступно о Java 8 Lambda ## Инструкция по шагам (из видео): @@ -51,3 +55,20 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Java 8: Потоки - Pуководство по Java 8 Stream - Лямбда-выражения в Java 8 + - Time complexity + +## Домашнее задание HW0 + Реализовать UserMealsUtil.getFilteredMealsWithExceeded: + - должны возвращаться только записи между startTime и endTime + - поле UserMealWithExceed.exceed должно показывать, + превышает ли сумма калорий за весь день параметра метода caloriesPerDay + + Т.е UserMealWithExceed - это запись одной еды, но поле exceeded + будет одинаково для всех записей за этот день. + + - Проверте результат выполнения ДЗ! + - Оцените Time complexity вашего алгоритма. + +Optional + + Сделать реализация через Java 8 Stream API diff --git a/cv.md b/cv.md new file mode 100644 index 000000000000..a2ec06d4017d --- /dev/null +++ b/cv.md @@ -0,0 +1,34 @@ +## Составление резюме + +![cv](https://cloud.githubusercontent.com/assets/13649199/10877471/93ea86b8-8157-11e5-9bfa-95e3fba75c58.jpg) + +### Общие вещи: +- Как правильно составить резюме +- Резюме программистов. Часть 1 (плохие) +- Резюме программистов. Часть 2 (хорошие) +- Покритикуйте пожалуйста резюме (Junior java) +- Как составить резюме программисту? +- Мое (только как пример), linkedIn + +### От себя: +- email, skype - очень желательно, чтобы по ним вы были узнаваемы. Заведите рабочие, если не так. +- написать ВЕСЬ IT опыт (исключая опыт пользователя: Windows, MS Word, Photophop, Yandex disk, Google docs, ..): технологии, какие задачи решали (конкретные), какие инструменты использовали, VCS, DB, инструменты сборки, ... включая опыт в ВУЗе. +- на English иметь желательно. Если вакансия опублинована на Englsih - шлите на нем. Часто могут на нем попросить, если работодатель иностранный. +- формат: pdf или doc на мой взгляд не так важно. doc нарпимер в USA любят, я - нет:) На мой взгляд удобно иметь резюме где то в инете (hh, linkedin, google doc, чтобы им было удобно делиться). + +### Позиционирование проекта Topjava: +- После завершения проекта вы освоите все заявленные в нем технологии - вставьте в квалификацию (включая java 8 Stream and Time API) +- Делайте упор не на обучение, а на `участие в проекте`. Выполнение домашних заданий - это полноценное участие с написанием функционала по всем этим технологиям. Например: + + Участие в разработке Spring/JPA Enterprise приложения c авторизацией и правами доступа на основе ролей + на стеке Maven/ Spring MVC/ Security/ REST(Jackson)/ Java 8 Stream API: + - реализация сохранения в базы Postgres и HSQLDB на основе Spring JBDC, JPA(Hibernate) и Spring-Data-JPA + - реализация и тестирование REST и AJAX контроллеров + - реализация клиента на Bootstrap (css/js), datatables, jQuery + plugins. + - отлично будет, если вы его еще как то сами доработаете + + Ссылка на github и Heroku с задеплоенным приложением. + +- По поводу обучения на курсах- можно упомянуть в разделе образования. Но на собеседовании меньше смотрят на то, что вы заканчивали, больше - на ваш опыт и то что вы знаете. + +- В течении проекта заполните форму для работодателей diff --git a/description.md b/description.md index c58f903605d4..fcf8c9067227 100644 --- a/description.md +++ b/description.md @@ -1,4 +1,7 @@ -#### Разработка полнофункционального Spring/JPA Enterprise приложения c авторизацией и правами доступа на основе ролей используя наиболее популярные инструменты и технологии Java: Maven, Spring, Security, JPA(Hibernate), REST(Jackson), Bootstrap, jQuery + plugins +#### Старт проекта 26.11.2015 +Присылайте заявки в skype:grigory.kislin + +#### Разработка полнофункционального Spring/JPA Enterprise приложения c авторизацией и правами доступа на основе ролей используя наиболее популярные инструменты и технологии Java: Maven, Spring MVC, Security, JPA(Hibernate), REST(Jackson), Bootstrap (css,js), datatables, jQuery + plugins, Java 8 Stream and Time API - Основное внимание будет уделяться способам решения многочисленных проблем разработки в Spring/JPA, а также структурному (красивому и надежному) java кодированию и архитектуре приложения. - Каждая итерация проекта в закрепляется домашним заданием по реализации схожей функциональности. Следующее занятие начинается с разбора домашних заданий. - Большое внимание уделяется тестированию кода: в проекте ~ 85 JUnit тестов. @@ -49,7 +52,7 @@ Hibernate, делаем конверторы для типов LocalDateTime (Ja - Тестирование REST контроллеров. Jackson. - jackson-datatype-hibernate. Тестирование через матчеры. - Тестирование через SoapUi. UTF-8 -- WebJars. Dandelion bundles +- WebJars. - Bootstrap. Datatables. - AJAX. jQuery. Notifications. - Spring Security From 18097a2bb75fd0f68d230853656c5ba375f64cc7 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Sat, 7 Nov 2015 00:42:22 +0300 Subject: [PATCH 003/108] Update cv.md --- cv.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cv.md b/cv.md index a2ec06d4017d..76e60ced941e 100644 --- a/cv.md +++ b/cv.md @@ -31,4 +31,4 @@ - По поводу обучения на курсах- можно упомянуть в разделе образования. Но на собеседовании меньше смотрят на то, что вы заканчивали, больше - на ваш опыт и то что вы знаете. -- В течении проекта заполните форму для работодателей +- В течении проекта заполните форму соискателя "Разработчик Java" From de9672354315900a988f277e9a6487c9b0973178 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sun, 8 Nov 2015 22:48:46 +0300 Subject: [PATCH 004/108] 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 000000000000..fde5448b93d8 --- /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 { + protected final LocalDateTime dateTime; + + protected final String description; + + protected 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 000000000000..dab6c9468c5e --- /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 { + protected final LocalDateTime dateTime; + + protected final String description; + + protected final int calories; + + protected 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 000000000000..02399b7288cf --- /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 000000000000..2cb426400fa5 --- /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) + ); + getFilteredMealsWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); +// .toLocalDate(); +// .toLocalTime(); + } + + public static List getFilteredMealsWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + // TODO return filtered list with correctly exceeded field + return null; + } +} From 2cf50d53cd8d1d86232cadb830d005656987aa09 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 7 Dec 2015 17:34:37 +0300 Subject: [PATCH 005/108] HW0_1 --- .../topjava/model/UserMealWithExceed.java | 10 ++++++++++ .../topjava/util/UserMealsUtil.java | 20 +++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java index dab6c9468c5e..66c356922f6b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java @@ -21,4 +21,14 @@ public UserMealWithExceed(LocalDateTime dateTime, String description, int calori 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/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java index 2cb426400fa5..79d1bd2ce6fc 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -3,11 +3,14 @@ import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.model.UserMealWithExceed; +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 @@ -23,13 +26,18 @@ public static void main(String[] args) { new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) ); - getFilteredMealsWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); -// .toLocalDate(); -// .toLocalTime(); + List filteredMealsWithExceeded = getFilteredMealsWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + filteredMealsWithExceeded.forEach(System.out::println); } - public static List getFilteredMealsWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO return filtered list with correctly exceeded field - return null; + public static List getFilteredMealsWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + Map caloriesSumByDate = mealList.stream().collect(Collectors.groupingBy(um -> um.getDateTime().toLocalDate(), + Collectors.summingInt(UserMeal::getCalories))); + + return mealList.stream() + .filter(um->TimeUtil.isBetween(um.getDateTime().toLocalTime(), startTime, endTime)) + .map(um->new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), + caloriesSumByDate.get(um.getDateTime().toLocalDate())> caloriesPerDay)) + .collect(Collectors.toList()); } } From 4a882dee1b6a3fa7bbc8ca269e1cf21a6c0bb0de Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 7 Dec 2015 18:19:50 +0300 Subject: [PATCH 006/108] 1_3 Switch to war packaging patch --- pom.xml | 3 ++- src/main/webapp/WEB-INF/web.xml | 18 ++++++++++++++++++ src/main/webapp/index.html | 10 ++++++++++ src/main/webapp/userList.jsp | 9 +++++++++ 4 files changed, 39 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/userList.jsp diff --git a/pom.xml b/pom.xml index c8a1c78f3b29..3f1e3780563a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ ru.javawebinar topjava - jar + war 1.0-SNAPSHOT @@ -13,6 +13,7 @@ 1.8 + UTF-8 UTF-8 diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..edca9575b44b --- /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 000000000000..1937333f0dbe --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,10 @@ + + + + + Top Java. + + +

Приложение вебинара "Top Java."

+ + \ No newline at end of file diff --git a/src/main/webapp/userList.jsp b/src/main/webapp/userList.jsp new file mode 100644 index 000000000000..c6b12052a263 --- /dev/null +++ b/src/main/webapp/userList.jsp @@ -0,0 +1,9 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + User list + + +

User list

+ + From e560103bae68ba8a52cd60e6908cf2c4964a6ac2 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 7 Dec 2015 18:21:39 +0300 Subject: [PATCH 007/108] 1_4 Add servlet jsp html patch --- 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 3f1e3780563a..644419ce3d31 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,14 @@ + + + + javax.servlet + servlet-api + 2.5 + 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 000000000000..389759e504ad --- /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("/userList.jsp").forward(request, response); + } +} From 51b446bf2c7ff7edc9f12c8e48bb62b3ffea927b Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 7 Dec 2015 18:23:16 +0300 Subject: [PATCH 008/108] 1_5 Forward to redirect patch --- 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 644419ce3d31..9a400f588605 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,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 389759e504ad..70ce24c6c494 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("/userList.jsp").forward(request, response); +// request.getRequestDispatcher("/userList.jsp").forward(request, response); + response.sendRedirect("userList.jsp"); } } From c4f7153f10edb08cc0d98fd2f1762995b268f18b Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 7 Dec 2015 20:26:13 +0300 Subject: [PATCH 009/108] 1_6 Add logging patch --- config/tomcat/setenv.bat | 4 ++ pom.xml | 33 +++++++++ .../ru/javawebinar/topjava/LoggerWrapper.java | 72 +++++++++++++++++++ .../javawebinar/topjava/web/UserServlet.java | 5 ++ src/main/resources/logback.xml | 33 +++++++++ 5 files changed, 147 insertions(+) create mode 100644 config/tomcat/setenv.bat create mode 100644 src/main/java/ru/javawebinar/topjava/LoggerWrapper.java create mode 100644 src/main/resources/logback.xml diff --git a/config/tomcat/setenv.bat b/config/tomcat/setenv.bat new file mode 100644 index 000000000000..ebbd659386f6 --- /dev/null +++ b/config/tomcat/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/pom.xml b/pom.xml index 9a400f588605..6ae2feefbdc0 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,10 @@ UTF-8 UTF-8 + + + 1.1.3 + 1.7.13 @@ -35,6 +39,34 @@ + + + org.slf4j + slf4j-api + ${slf4j.version} + compile + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + runtime + + + + ch.qos.logback + logback-classic + ${logback.version} + runtime + @@ -43,6 +75,7 @@ 2.5 provided + diff --git a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java new file mode 100644 index 000000000000..d2862a1856e1 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java @@ -0,0 +1,72 @@ +package ru.javawebinar.topjava; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * User: gkislin + * Date: 22.01.14 + */ +public class LoggerWrapper { + + private Logger logger; + + public LoggerWrapper(Logger logger) { + this.logger = logger; + } + + public static LoggerWrapper get(Class aClass) { + return new LoggerWrapper(LoggerFactory.getLogger(aClass)); + } + + public void debug(String msg) { + logger.debug(msg); + } + + public void info(String msg, Object... arguments) { + logger.info(msg, arguments); + } + + public void warn(String msg) { + logger.warn(msg); + } + + public void warn(String msg, Throwable t) { + logger.warn(msg, t); + } + + public void error(String msg) { + logger.error(msg); + } + + public void error(String msg, Throwable t) { + logger.error(msg, t); + } + + public boolean isDebug() { + return logger.isDebugEnabled(); + } + + public IllegalStateException getIllegalStateException(String msg) { + return getIllegalStateException(msg, null); + } + + public IllegalStateException getIllegalStateException(String msg, Throwable e) { + logger.error(msg, e); + return new IllegalStateException(msg, e); + } + + public IllegalArgumentException getIllegalArgumentException(String msg) { + return getIllegalArgumentException(msg, null); + } + + public IllegalArgumentException getIllegalArgumentException(String msg, Throwable e) { + logger.error(msg, e); + return new IllegalArgumentException(msg, e); + } + + public UnsupportedOperationException getUnsupportedOperationException(String msg) { + logger.error(msg); + return new UnsupportedOperationException(msg); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 70ce24c6c494..eb2188bc8435 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.web; +import ru.javawebinar.topjava.LoggerWrapper; + import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -11,8 +13,11 @@ * Date: 19.08.2014 */ public class UserServlet extends HttpServlet { + private static final LoggerWrapper LOG = LoggerWrapper.get(UserServlet.class); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + LOG.debug("redirect to userList"); + // request.getRequestDispatcher("/userList.jsp").forward(request, response); response.sendRedirect("userList.jsp"); } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 000000000000..4ee2916e57d1 --- /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 0268fedbf89a8e938f7801d40b4ccbc75e470e5f Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 10 Dec 2015 21:48:50 +0300 Subject: [PATCH 010/108] add circle variant for UserMealsUtil --- .../topjava/util/UserMealsUtil.java | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java index 79d1bd2ce6fc..89f67f038d07 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.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; /** @@ -19,15 +17,17 @@ 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) + 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) ); List filteredMealsWithExceeded = getFilteredMealsWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); filteredMealsWithExceeded.forEach(System.out::println); + + System.out.println(getFilteredMealsWithExceededByCycle(mealList, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); } public static List getFilteredMealsWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { @@ -35,9 +35,29 @@ public static List getFilteredMealsWithExceeded(ListTimeUtil.isBetween(um.getDateTime().toLocalTime(), startTime, endTime)) - .map(um->new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), - caloriesSumByDate.get(um.getDateTime().toLocalDate())> caloriesPerDay)) + .filter(um -> TimeUtil.isBetween(um.getDateTime().toLocalTime(), startTime, endTime)) + .map(um -> new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), + caloriesSumByDate.get(um.getDateTime().toLocalDate()) > caloriesPerDay)) .collect(Collectors.toList()); } + + public static List getFilteredMealsWithExceededByCycle(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + + Map caloriesSumPerDate = new HashMap<>(); + for (UserMeal meal : mealList) { + LocalDate mealDate = meal.getDateTime().toLocalDate(); + caloriesSumPerDate.put(mealDate, caloriesSumPerDate.merge(mealDate, 0, (oldValue, newValue) -> oldValue + newValue)); + } + + List mealExceeded = new ArrayList<>(); + for (UserMeal meal : mealList) { + LocalDateTime dateTime = meal.getDateTime(); + if (TimeUtil.isBetween(dateTime.toLocalTime(), startTime, endTime)) { + mealExceeded.add(new UserMealWithExceed(dateTime, meal.getDescription(), meal.getCalories(), + caloriesSumPerDate.get(dateTime.toLocalDate()) > caloriesPerDay)); + } + } + return mealExceeded; + } + } From c113aba25a99e96268d24832b645021773ac0761 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 10 Dec 2015 22:02:27 +0300 Subject: [PATCH 011/108] 2_1 HW1 --- pom.xml | 6 +++ .../topjava/model/UserMealWithExceed.java | 16 +++++++ .../ru/javawebinar/topjava/util/TimeUtil.java | 8 ++++ .../topjava/util/UserMealsUtil.java | 46 +++++++++++-------- .../javawebinar/topjava/web/MealServlet.java | 25 ++++++++++ src/main/webapp/WEB-INF/web.xml | 10 ++++ src/main/webapp/index.html | 9 +++- src/main/webapp/mealList.jsp | 41 +++++++++++++++++ src/main/webapp/userList.jsp | 3 +- 9 files changed, 141 insertions(+), 23 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/MealServlet.java create mode 100644 src/main/webapp/mealList.jsp diff --git a/pom.xml b/pom.xml index 6ae2feefbdc0..4dfa22a09956 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,12 @@ provided + + javax.servlet + jstl + 1.2 + + diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java index 66c356922f6b..253c10383acf 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java @@ -22,6 +22,22 @@ public UserMealWithExceed(LocalDateTime dateTime, String description, int calori 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/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java index 02399b7288cf..115311fad6b7 100644 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java @@ -1,13 +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 TimeUtil { + public static final DateTimeFormatter DATE_TME_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_TME_FORMATTER); + } } diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java index 89f67f038d07..d6f5a49df23d 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -15,49 +15,55 @@ * 31.05.2015. */ public class UserMealsUtil { + public static final List MEAL_LIST = 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) + ); + 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) - ); - List filteredMealsWithExceeded = getFilteredMealsWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + List filteredMealsWithExceeded = getFilteredWithExceeded(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); filteredMealsWithExceeded.forEach(System.out::println); - System.out.println(getFilteredMealsWithExceededByCycle(mealList, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + System.out.println(getFilteredWithExceededByCycle(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + } + + public static List getWithExceeded(List mealList, int caloriesPerDay) { + return getFilteredWithExceeded(mealList, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); } - public static List getFilteredMealsWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - Map caloriesSumByDate = mealList.stream().collect(Collectors.groupingBy(um -> um.getDateTime().toLocalDate(), + public static UserMealWithExceed createWithExceed(UserMeal um, boolean exceeded) { + return new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), exceeded); + } + + public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + Map caloriesSumByDate = mealList.stream() + .collect(Collectors.groupingBy(um -> um.getDateTime().toLocalDate(), Collectors.summingInt(UserMeal::getCalories))); return mealList.stream() .filter(um -> TimeUtil.isBetween(um.getDateTime().toLocalTime(), startTime, endTime)) - .map(um -> new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), - caloriesSumByDate.get(um.getDateTime().toLocalDate()) > caloriesPerDay)) + .map(um -> createWithExceed(um, caloriesSumByDate.get(um.getDateTime().toLocalDate()) > caloriesPerDay)) .collect(Collectors.toList()); } - public static List getFilteredMealsWithExceededByCycle(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + public static List getFilteredWithExceededByCycle(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { Map caloriesSumPerDate = new HashMap<>(); for (UserMeal meal : mealList) { - LocalDate mealDate = meal.getDateTime().toLocalDate(); - caloriesSumPerDate.put(mealDate, caloriesSumPerDate.merge(mealDate, 0, (oldValue, newValue) -> oldValue + newValue)); + caloriesSumPerDate.merge(meal.getDateTime().toLocalDate(), meal.getCalories(), Integer::sum); } List mealExceeded = new ArrayList<>(); for (UserMeal meal : mealList) { LocalDateTime dateTime = meal.getDateTime(); if (TimeUtil.isBetween(dateTime.toLocalTime(), startTime, endTime)) { - mealExceeded.add(new UserMealWithExceed(dateTime, meal.getDescription(), meal.getCalories(), - caloriesSumPerDate.get(dateTime.toLocalDate()) > caloriesPerDay)); + mealExceeded.add(createWithExceed(meal, caloriesSumPerDate.get(dateTime.toLocalDate()) > caloriesPerDay)); } } return mealExceeded; } - } 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 000000000000..fbd7b0bfe274 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -0,0 +1,25 @@ +package ru.javawebinar.topjava.web; + +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.util.UserMealsUtil; + +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 LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + LOG.info("getAll"); + request.setAttribute("mealList", + UserMealsUtil.getWithExceeded(UserMealsUtil.MEAL_LIST, 2000)); + request.getRequestDispatcher("/mealList.jsp").forward(request, response); + } +} diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index edca9575b44b..6306688d0998 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 1937333f0dbe..a16fd7167274 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -5,6 +5,11 @@ Top Java. -

Приложение вебинара "Top Java."

+

Проект "Top Java"

+
+ - \ No newline at end of file + diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp new file mode 100644 index 000000000000..0707a7435082 --- /dev/null +++ b/src/main/webapp/mealList.jsp @@ -0,0 +1,41 @@ +<%@ page import="ru.javawebinar.topjava.util.TimeUtil" %> +<%@ 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" %> + + + Meal list + + + +
+

Home

+

Meal list

+
+ + + + + + + + + + + + + + + + +
DateDescriptionCalories
+ <%----%> + <%----%> + <%=TimeUtil.toString(meal.getDateTime())%> + ${meal.description}${meal.calories}
+
+ + \ No newline at end of file diff --git a/src/main/webapp/userList.jsp b/src/main/webapp/userList.jsp index c6b12052a263..eae4726d4400 100644 --- a/src/main/webapp/userList.jsp +++ b/src/main/webapp/userList.jsp @@ -4,6 +4,7 @@ User list -

User list

+

Home

+

User list

From eb3dc8ce0a173fe576787347894d4b9c903b05cb Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 10 Dec 2015 22:17:15 +0300 Subject: [PATCH 012/108] 2_2 HW1 Optional Add CRUD in memory --- .../javawebinar/topjava/model/UserMeal.java | 29 ++++++++++ .../topjava/model/UserMealWithExceed.java | 14 ++++- .../InMemoryUserMealRepositoryImpl.java | 46 ++++++++++++++++ .../repository/UserMealRepository.java | 19 +++++++ .../topjava/util/UserMealsUtil.java | 12 +++-- .../javawebinar/topjava/web/MealServlet.java | 54 +++++++++++++++++-- src/main/webapp/mealEdit.jsp | 51 ++++++++++++++++++ src/main/webapp/mealList.jsp | 5 ++ 8 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java create mode 100644 src/main/webapp/mealEdit.jsp diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java index fde5448b93d8..13662581d16e 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -7,6 +7,8 @@ * 11.01.2015. */ public class UserMeal { + protected Integer id; + protected final LocalDateTime dateTime; protected final String description; @@ -14,11 +16,20 @@ public class UserMeal { protected final int calories; public UserMeal(LocalDateTime dateTime, String description, int calories) { + this(null, dateTime, description, calories); + } + + public UserMeal(Integer id, LocalDateTime dateTime, String description, int calories) { + this.id = id; this.dateTime = dateTime; this.description = description; this.calories = calories; } + public void setId(int id) { + this.id = id; + } + public LocalDateTime getDateTime() { return dateTime; } @@ -30,4 +41,22 @@ public String getDescription() { public int getCalories() { return calories; } + + public Integer getId() { + return id; + } + + public boolean isNew() { + return id == null; + } + + @Override + public String toString() { + return "UserMeal{" + + "id=" + id + + ", dateTime=" + dateTime + + ", description='" + description + '\'' + + ", calories=" + calories + + '}'; + } } diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java index 253c10383acf..9fd22fa22f71 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java @@ -7,6 +7,8 @@ * 11.01.2015. */ public class UserMealWithExceed { + protected final Integer id; + protected final LocalDateTime dateTime; protected final String description; @@ -16,12 +18,21 @@ public class UserMealWithExceed { protected final boolean exceed; public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this(null, dateTime, description, calories, exceed); + } + + public UserMealWithExceed(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; } @@ -41,7 +52,8 @@ public boolean isExceed() { @Override public String toString() { return "UserMealWithExceed{" + - "dateTime=" + dateTime + + "id=" + id + + ", dateTime=" + dateTime + ", description='" + description + '\'' + ", calories=" + calories + ", exceed=" + exceed + diff --git a/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java new file mode 100644 index 000000000000..1a9cffcd867f --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java @@ -0,0 +1,46 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.util.UserMealsUtil; + +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 InMemoryUserMealRepositoryImpl implements UserMealRepository { + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + { + UserMealsUtil.MEAL_LIST.forEach(this::save); + } + + @Override + public UserMeal save(UserMeal userMeal) { + if (userMeal.isNew()) { + userMeal.setId(counter.incrementAndGet()); + } + return repository.put(userMeal.getId(), userMeal); + } + + @Override + public void delete(int id) { + repository.remove(id); + } + + @Override + public UserMeal get(int id) { + return repository.get(id); + } + + @Override + public Collection getAll() { + return repository.values(); + } +} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java new file mode 100644 index 000000000000..ad656ea6cd19 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java @@ -0,0 +1,19 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.UserMeal; + +import java.util.Collection; + +/** + * GKislin + * 06.03.2015. + */ +public interface UserMealRepository { + UserMeal save(UserMeal userMeal); + + void delete(int id); + + UserMeal get(int id); + + Collection getAll(); +} diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java index d6f5a49df23d..f04d58f113c5 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -24,22 +24,24 @@ public class UserMealsUtil { new UserMeal(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 filteredMealsWithExceeded = getFilteredWithExceeded(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + List filteredMealsWithExceeded = getFilteredWithExceeded(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY); filteredMealsWithExceeded.forEach(System.out::println); - System.out.println(getFilteredWithExceededByCycle(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + System.out.println(getFilteredWithExceededByCycle(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY)); } - public static List getWithExceeded(List mealList, int caloriesPerDay) { + public static List getWithExceeded(Collection mealList, int caloriesPerDay) { return getFilteredWithExceeded(mealList, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); } public static UserMealWithExceed createWithExceed(UserMeal um, boolean exceeded) { - return new UserMealWithExceed(um.getDateTime(), um.getDescription(), um.getCalories(), exceeded); + return new UserMealWithExceed(um.getId(), um.getDateTime(), um.getDescription(), um.getCalories(), exceeded); } - public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + public static List getFilteredWithExceeded(Collection mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { Map caloriesSumByDate = mealList.stream() .collect(Collectors.groupingBy(um -> um.getDateTime().toLocalDate(), Collectors.summingInt(UserMeal::getCalories))); diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index fbd7b0bfe274..f6e8bd64e054 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,13 +1,19 @@ package ru.javawebinar.topjava.web; import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.repository.InMemoryUserMealRepositoryImpl; +import ru.javawebinar.topjava.repository.UserMealRepository; import ru.javawebinar.topjava.util.UserMealsUtil; +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.util.Objects; /** * User: gkislin @@ -16,10 +22,50 @@ public class MealServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); + private UserMealRepository repository; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + repository = new InMemoryUserMealRepositoryImpl(); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { + request.setCharacterEncoding("UTF-8"); + String id = request.getParameter("id"); + UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id), + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); + LOG.info(userMeal.isNew() ? "Create {}" : "Update {}", userMeal); + repository.save(userMeal); + response.sendRedirect("meals"); + } + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - LOG.info("getAll"); - request.setAttribute("mealList", - UserMealsUtil.getWithExceeded(UserMealsUtil.MEAL_LIST, 2000)); - request.getRequestDispatcher("/mealList.jsp").forward(request, response); + String action = request.getParameter("action"); + + if (action == null) { + LOG.info("getAll"); + request.setAttribute("mealList", + UserMealsUtil.getWithExceeded(repository.getAll(), UserMealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("/mealList.jsp").forward(request, response); + } else if (action.equals("delete")) { + int id = getId(request); + LOG.info("Delete {}", id); + repository.delete(id); + response.sendRedirect("meals"); + } else { + final UserMeal meal = action.equals("create") ? + new UserMeal(LocalDateTime.now(), "", 1000) : + repository.get(getId(request)); + request.setAttribute("meal", meal); + request.getRequestDispatcher("mealEdit.jsp").forward(request, response); + } + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.valueOf(paramId); } } diff --git a/src/main/webapp/mealEdit.jsp b/src/main/webapp/mealEdit.jsp new file mode 100644 index 000000000000..443933549130 --- /dev/null +++ b/src/main/webapp/mealEdit.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

+

Edit meal

+
+ +
+ +
+
DateTime:
+
+
+
+
Description:
+
+
+
+
Calories:
+
+
+ + +
+
+ + diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp index 0707a7435082..dbf36345b502 100644 --- a/src/main/webapp/mealList.jsp +++ b/src/main/webapp/mealList.jsp @@ -14,6 +14,7 @@

Home

Meal list

+ Add Meal
@@ -21,6 +22,8 @@ + + @@ -33,6 +36,8 @@ + +
Date Description Calories
${meal.description} ${meal.calories}UpdateDelete
From 5f858bb125f896f3f3f3b115d6a1436834e6bea8 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sun, 13 Dec 2015 13:46:37 +0300 Subject: [PATCH 013/108] 2_3 Add Application layers --- .../ru/javawebinar/topjava/LoggedUser.java | 18 ++++ .../ru/javawebinar/topjava/LoggerWrapper.java | 8 +- .../javawebinar/topjava/model/BaseEntity.java | 53 ++++++++++ .../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/UserMealService.java | 8 ++ .../topjava/service/UserMealServiceImpl.java | 13 +++ .../topjava/service/UserService.java | 26 +++++ .../topjava/service/UserServiceImpl.java | 41 ++++++++ .../topjava/util/exception/ExceptionUtil.java | 29 ++++++ .../util/exception/NotFoundException.java | 11 +++ .../web/meal/UserMealRestController.java | 12 +++ .../web/user/AbstractUserController.java | 48 +++++++++ .../topjava/web/user/AdminRestController.java | 36 +++++++ .../web/user/ProfileRestController.java | 23 +++++ 17 files changed, 488 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/javawebinar/topjava/LoggedUser.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/UserMealService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.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/exception/ExceptionUtil.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/UserMealRestController.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/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java new file mode 100644 index 000000000000..eb68d99a0d6e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -0,0 +1,18 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.util.UserMealsUtil; + +/** + * GKislin + * 06.03.2015. + */ +public class LoggedUser { + + public static int id() { + return 1; + } + + public static int getCaloriesPerDay() { + return UserMealsUtil.DEFAULT_CALORIES_PER_DAY; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java index d2862a1856e1..642bb6c4c7ea 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java +++ b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.util.exception.NotFoundException; /** * User: gkislin @@ -69,4 +70,9 @@ public UnsupportedOperationException getUnsupportedOperationException(String msg logger.error(msg); return new UnsupportedOperationException(msg); } -} + + public NotFoundException getNotFoundException(String reason) { + logger.error("No data found"); + return new NotFoundException(reason); + } +} \ No newline at end of file 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 000000000000..e8f1a2ce9223 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -0,0 +1,53 @@ +package ru.javawebinar.topjava.model; + +import ru.javawebinar.topjava.LoggerWrapper; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class BaseEntity { + protected static final LoggerWrapper LOG = LoggerWrapper.get(BaseEntity.class); + + 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); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BaseEntity that = (BaseEntity) o; + if (id == null || that.id == null) { + throw LOG.getIllegalStateException("Equals '" + this + "' and '" + that + "' with null id"); + } + return id.equals(that.id); + } + + @Override + public int hashCode() { + return (id == null) ? 0 : id; + } + +} 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 000000000000..ca92e394dc98 --- /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 000000000000..f0de2b4c4a81 --- /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 000000000000..34b5977a5917 --- /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.UserMealsUtil; + +import java.util.Date; +import java.util.EnumSet; +import java.util.Set; + +/** + * User: gkislin + * Date: 22.08.2014 + */ +public class User extends NamedEntity { + + protected String email; + + protected String password; + + protected boolean enabled = true; + + protected Date registered = new Date(); + + protected Set roles; + + protected int caloriesPerDay = UserMealsUtil.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, UserMealsUtil.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 000000000000..24c1c45beca6 --- /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/UserMealService.java b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java new file mode 100644 index 000000000000..b17fec811065 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java @@ -0,0 +1,8 @@ +package ru.javawebinar.topjava.service; + +/** + * GKislin + * 15.06.2015. + */ +public interface UserMealService { +} diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java new file mode 100644 index 000000000000..5730b8bfb130 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java @@ -0,0 +1,13 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.repository.UserMealRepository; + +/** + * GKislin + * 06.03.2015. + */ +public class UserMealServiceImpl implements UserMealService { + + private UserMealRepository 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 000000000000..85c15e48f951 --- /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 000000000000..27a4b171c406 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -0,0 +1,41 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.ExceptionUtil; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +/** + * GKislin + * 06.03.2015. + */ +public class UserServiceImpl implements UserService { + + private UserRepository repository; + + public User save(User user) { + return repository.save(user); + } + + public void delete(int id) { + ExceptionUtil.check(repository.delete(id), id); + } + + public User get(int id) throws NotFoundException { + return ExceptionUtil.check(repository.get(id), id); + } + + public User getByEmail(String email) throws NotFoundException { + return ExceptionUtil.check(repository.getByEmail(email), "email=" + email); + } + + public List getAll() { + return repository.getAll(); + } + + public void update(User user) { + repository.save(user); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java b/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java new file mode 100644 index 000000000000..295aadab347f --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java @@ -0,0 +1,29 @@ +package ru.javawebinar.topjava.util.exception; + + +import ru.javawebinar.topjava.LoggerWrapper; + +/** + * User: gkislin + * Date: 14.05.2014 + */ +public class ExceptionUtil { + private static final LoggerWrapper LOG = LoggerWrapper.get(ExceptionUtil.class); + + public static void check(boolean found, int id) { + check(found, "id=" + id); + } + + public static T check(T object, int id) { + return check(object, "id=" + id); + } + + public static T check(T object, String msg) { + check(object != null, msg); + return object; + } + + public static void check(boolean found, String msg) { + if (!found) throw LOG.getNotFoundException("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 000000000000..7a770f0db834 --- /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/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java new file mode 100644 index 000000000000..51e64a2712c8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -0,0 +1,12 @@ +package ru.javawebinar.topjava.web.meal; + +import ru.javawebinar.topjava.service.UserMealServiceImpl; + +/** + * GKislin + * 06.03.2015. + */ +public class UserMealRestController { + private UserMealServiceImpl 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 000000000000..765c3ed7e113 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -0,0 +1,48 @@ +package ru.javawebinar.topjava.web.user; + +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; + +import java.util.List; + +/** + * User: gkislin + */ +public abstract class AbstractUserController { + protected final LoggerWrapper LOG = LoggerWrapper.get(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 000000000000..cdd1e6bf11f8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -0,0 +1,36 @@ +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 { + + public List getAll() { + return super.getAll(); + } + + public User get(int id) { + return super.get(id); + } + + public User create(User user) { + return super.create(user); + } + + public void delete(int id) { + super.delete(id); + } + + public void update(User user, int id) { + super.update(user, id); + } + + 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 000000000000..6f0c970152af --- /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.LoggedUser; +import ru.javawebinar.topjava.model.User; + +/** + * GKislin + * 06.03.2015. + */ +public class ProfileRestController extends AbstractUserController { + + public User get() { + return super.get(LoggedUser.id()); + } + + public void delete() { + super.delete(LoggedUser.id()); + } + + public void update(User user) { + super.update(user, LoggedUser.id()); + } +} \ No newline at end of file From ede4ee9a066080dec6532b1f81b386ca69c9e67f Mon Sep 17 00:00:00 2001 From: m1nd Date: Sun, 13 Dec 2015 20:44:38 +0300 Subject: [PATCH 014/108] 2_4 Add Spring Context and main --- pom.xml | 14 ++++++ .../ru/javawebinar/topjava/SpringMain.java | 21 +++++++++ .../InMemoryUserMealRepositoryImpl.java | 3 +- .../mock/MockUserRepositoryImpl.java | 46 +++++++++++++++++++ .../javawebinar/topjava/web/MealServlet.java | 2 +- src/main/resources/spring/spring-app.xml | 8 ++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/SpringMain.java rename src/main/java/ru/javawebinar/topjava/repository/{ => mock}/InMemoryUserMealRepositoryImpl.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 4dfa22a09956..bae4d0d90899 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,8 @@ UTF-8 UTF-8 + 4.2.3.RELEASE + 1.1.3 1.7.13 @@ -82,6 +84,18 @@ 1.2 + + + org.springframework + spring-context + ${spring.version} + + + commons-logging + commons-logging + + + 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 000000000000..720e8cc8b3d4 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -0,0 +1,21 @@ +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(Arrays.toString(appCtx.getBeanDefinitionNames())); + UserRepository userRepository = (UserRepository) appCtx.getBean("mockUserRepository"); + userRepository = appCtx.getBean(UserRepository.class); + appCtx.close(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java similarity index 90% rename from src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java rename to src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java index 1a9cffcd867f..98ef588bbc72 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java @@ -1,6 +1,7 @@ -package ru.javawebinar.topjava.repository; +package ru.javawebinar.topjava.repository.mock; import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.repository.UserMealRepository; import ru.javawebinar.topjava.util.UserMealsUtil; 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 000000000000..6805a9fcd10e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java @@ -0,0 +1,46 @@ +package ru.javawebinar.topjava.repository.mock; + +import ru.javawebinar.topjava.LoggerWrapper; +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 LoggerWrapper LOG = LoggerWrapper.get(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 f6e8bd64e054..31db7c5a1754 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,7 +2,7 @@ import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.repository.InMemoryUserMealRepositoryImpl; +import ru.javawebinar.topjava.repository.mock.InMemoryUserMealRepositoryImpl; import ru.javawebinar.topjava.repository.UserMealRepository; import ru.javawebinar.topjava.util.UserMealsUtil; diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml new file mode 100644 index 000000000000..19632d5645ac --- /dev/null +++ b/src/main/resources/spring/spring-app.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file From 2f53b74072dd18309d43d6ddc026f43f786bb529 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sun, 13 Dec 2015 20:46:18 +0300 Subject: [PATCH 015/108] 2_5 Add Dependency Injection --- src/main/java/ru/javawebinar/topjava/SpringMain.java | 8 +++++--- .../ru/javawebinar/topjava/service/UserServiceImpl.java | 4 ++++ src/main/resources/spring/spring-app.xml | 6 ++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 720e8cc8b3d4..063894391dcb 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -2,7 +2,9 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; import java.util.Arrays; @@ -14,8 +16,8 @@ public class SpringMain { public static void main(String[] args) { ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); System.out.println(Arrays.toString(appCtx.getBeanDefinitionNames())); - UserRepository userRepository = (UserRepository) appCtx.getBean("mockUserRepository"); - userRepository = appCtx.getBean(UserRepository.class); + UserService userService = appCtx.getBean(UserService.class); + System.out.println(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 27a4b171c406..066ae95037be 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -15,6 +15,10 @@ public class UserServiceImpl implements UserService { private UserRepository repository; + public void setRepository(UserRepository repository) { + this.repository = repository; + } + 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 19632d5645ac..7b4fa1689425 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -1,8 +1,10 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> + + + \ No newline at end of file From 41bf67fa811b95e46c4dd485a5b016d112ed080c Mon Sep 17 00:00:00 2001 From: m1nd Date: Sun, 13 Dec 2015 20:48:05 +0300 Subject: [PATCH 016/108] 2_6 Add Annotation Processing --- .../ru/javawebinar/topjava/SpringMain.java | 13 ++++++----- .../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(+), 15 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 063894391dcb..9b14ec645a08 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -4,7 +4,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.web.user.AdminRestController; import java.util.Arrays; @@ -14,10 +14,11 @@ */ public class SpringMain { public static void main(String[] args) { - ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); - System.out.println(Arrays.toString(appCtx.getBeanDefinitionNames())); - UserService userService = appCtx.getBean(UserService.class); - System.out.println(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(Arrays.toString(appCtx.getBeanDefinitionNames())); + AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); + System.out.println(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 6805a9fcd10e..cf2649b7bf46 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.mock; +import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; @@ -11,6 +12,7 @@ * GKislin * 15.06.2015. */ +@Repository public class MockUserRepositoryImpl implements UserRepository { private static final LoggerWrapper LOG = LoggerWrapper.get(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 066ae95037be..f83d039b7fbc 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.ExceptionUtil; @@ -11,14 +13,12 @@ * GKislin * 06.03.2015. */ +@Service public class UserServiceImpl implements UserService { + @Autowired private UserRepository repository; - public void setRepository(UserRepository repository) { - this.repository = repository; - } - 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 765c3ed7e113..98e0401f4523 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 ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; @@ -12,6 +13,7 @@ public abstract class AbstractUserController { protected final LoggerWrapper LOG = LoggerWrapper.get(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 cdd1e6bf11f8..d6b8bb420803 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 { public List getAll() { 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 6f0c970152af..1114e57eeb1e 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.LoggedUser; 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 7b4fa1689425..c285b264141d 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 1fc50f6c5563849c219eaf3ce7c7abe8525355b3 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 14 Dec 2015 22:18:05 +0300 Subject: [PATCH 017/108] HW2_Edit InMemoryUserRepositoryImpl --- .../mock/InMemoryUserRepositoryImpl.java | 72 +++++++++++++++++++ .../mock/MockUserRepositoryImpl.java | 48 ------------- 2 files changed, 72 insertions(+), 48 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/mock/InMemoryUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java new file mode 100644 index 000000000000..870bcb4a79c0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -0,0 +1,72 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * GKislin + * 15.06.2015. + */ +@Repository +public class InMemoryUserRepositoryImpl implements UserRepository { + private static final LoggerWrapper LOG = LoggerWrapper.get(InMemoryUserRepositoryImpl.class); + + private List repository = Arrays.asList(); + private AtomicInteger counter = new AtomicInteger(0); + + public static final List USER_LIST = Arrays.asList( + new User(), + new User(), + new User() + ); + + { + USER_LIST.forEach(this::save); + } + + @Override + public boolean delete(int id) { + LOG.info("delete " + id); + return repository.remove(this.get(id)); + } + + @Override + public User save(User user) { + LOG.info("save " + user); + if (user.isNew()) { + user.setId(counter.incrementAndGet()); + } + return repository.get(user.getId()); + } + + @Override + public User get(int id) { + LOG.info("get " + id); + return repository.get(id); + } + + @Override + public List getAll() { + LOG.info("getAll"); + return repository; + } + + @Override + public User getByEmail(String email) { + LOG.info("getByEmail " + email); + repository.stream() + .filter(us->us.getEmail().equals(email)) + + + return + } +} 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 cf2649b7bf46..000000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.LoggerWrapper; -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 LoggerWrapper LOG = LoggerWrapper.get(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; - } -} From 89c9981af0cae5b553be685408c9439e7e955f15 Mon Sep 17 00:00:00 2001 From: m1nd Date: Wed, 16 Dec 2015 19:15:24 +0300 Subject: [PATCH 018/108] HW2_Add some test code --- .../topjava/service/UserMealService.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java index b17fec811065..176c88ee6522 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java @@ -1,8 +1,25 @@ package ru.javawebinar.topjava.service; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + /** * GKislin * 15.06.2015. */ public interface UserMealService { + + UserMeal save(UserMeal userMeal); + + void delete(int id) throws NotFoundException; + + UserMeal get(int id) throws NotFoundException; + + UserMeal getByUserId(int userId) throws NotFoundException; + + List getAll(); + } From dca80f63ac4d0e46ab1d4df5b079bd081eb26fee Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 17 Dec 2015 09:29:26 +0300 Subject: [PATCH 019/108] HW2 Repository --- .../repository/UserMealRepository.java | 16 +++-- .../mock/InMemoryUserMealRepositoryImpl.java | 66 +++++++++++++++---- .../mock/InMemoryUserRepositoryImpl.java | 64 ++++++++++++++++++ .../mock/MockUserRepositoryImpl.java | 48 -------------- .../ru/javawebinar/topjava/util/TimeUtil.java | 4 ++ .../javawebinar/topjava/web/MealServlet.java | 11 ++-- 6 files changed, 138 insertions(+), 71 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/UserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java index ad656ea6cd19..646b908602a4 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java @@ -2,6 +2,7 @@ import ru.javawebinar.topjava.model.UserMeal; +import java.time.LocalDateTime; import java.util.Collection; /** @@ -9,11 +10,18 @@ * 06.03.2015. */ public interface UserMealRepository { - UserMeal save(UserMeal userMeal); + // null if updated meal do not belong to userId + UserMeal save(UserMeal userMeal, int userId); - void delete(int id); + // false if meal do not belong to userId + boolean delete(int id, int userId); - UserMeal get(int id); + // null if meal do not belong to userId + UserMeal get(int id, int userId); - Collection getAll(); + // ORDERED DATE, TIME + Collection getAll(int userId); + + // ORDERED DATE, TIME + Collection getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java index 98ef588bbc72..ebfdfb644527 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java @@ -1,47 +1,85 @@ package ru.javawebinar.topjava.repository.mock; +import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; +import ru.javawebinar.topjava.util.TimeUtil; import ru.javawebinar.topjava.util.UserMealsUtil; -import java.util.Collection; -import java.util.Map; +import java.time.LocalDateTime; +import java.time.Month; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.ADMIN_ID; +import static ru.javawebinar.topjava.repository.mock.InMemoryUserRepositoryImpl.USER_ID; /** * GKislin * 15.09.2015. */ +@Repository public class InMemoryUserMealRepositoryImpl implements UserMealRepository { - private Map repository = new ConcurrentHashMap<>(); + + public static final Comparator USER_MEAL_COMPARATOR = (um1, um2) -> um2.getDateTime().compareTo(um1.getDateTime()); + + // Map userId -> (mealId-> meal) + private Map> repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); { - UserMealsUtil.MEAL_LIST.forEach(this::save); + UserMealsUtil.MEAL_LIST.forEach(um -> save(um, USER_ID)); + + save(new UserMeal(LocalDateTime.of(2015, Month.JUNE, 1, 14, 0), "Админ ланч", 510), ADMIN_ID); + save(new UserMeal(LocalDateTime.of(2015, Month.JUNE, 1, 21, 0), "Админ ужин", 1500), ADMIN_ID); } @Override - public UserMeal save(UserMeal userMeal) { + public UserMeal save(UserMeal userMeal, int userId) { + Objects.requireNonNull(userMeal); + + Integer mealId = userMeal.getId(); + if (userMeal.isNew()) { - userMeal.setId(counter.incrementAndGet()); + mealId = counter.incrementAndGet(); + userMeal.setId(mealId); + } else if (get(mealId, userId) == null) { + return null; } - return repository.put(userMeal.getId(), userMeal); + Map userMeals = repository.computeIfAbsent(userId, ConcurrentHashMap::new); + userMeals.put(mealId, userMeal); + return userMeal; + } @Override - public void delete(int id) { - repository.remove(id); + public boolean delete(int id, int userId) { + Map userMeals = repository.get(userId); + return userMeals != null && userMeals.remove(id) != null; } @Override - public UserMeal get(int id) { - return repository.get(id); + public UserMeal get(int id, int userId) { + Map userMeals = repository.get(userId); + return userMeals == null ? null : userMeals.get(id); } @Override - public Collection getAll() { - return repository.values(); + public Collection getAll(int userId) { + Map userMeals = repository.get(userId); + return userMeals == null ? + Collections.emptyList() : + userMeals.values().stream().sorted(USER_MEAL_COMPARATOR).collect(Collectors.toList()); } -} + @Override + public Collection getBetween(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + Objects.requireNonNull(startDateTime); + Objects.requireNonNull(endDateTime); + return getAll(userId).stream() + .filter(um -> TimeUtil.isBetween(um.getDateTime(), startDateTime, endDateTime)) + .collect(Collectors.toList()); + } +} 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 000000000000..6f40cd7addbd --- /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.Role; +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); + + public static final Comparator USER_COMPARATOR = (u1, u2) -> u1.getName().compareTo(u2.getName()); + + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + + { + save(new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER)); + save(new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN)); + } + + @Override + public User save(User user) { + Objects.requireNonNull(user); + if (user.isNew()) { + user.setId(counter.incrementAndGet()); + } + return repository.put(user.getId(), 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 cf2649b7bf46..000000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.LoggerWrapper; -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 LoggerWrapper LOG = LoggerWrapper.get(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/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java index 115311fad6b7..4546d5b3b010 100644 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java @@ -15,6 +15,10 @@ public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime end return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; } + public static boolean isBetween(LocalDateTime ldt, LocalDateTime startDateTime, LocalDateTime endDateTime) { + return ldt.compareTo(startDateTime) >= 0 && ldt.compareTo(endDateTime) <= 0; + } + public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TME_FORMATTER); } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 31db7c5a1754..a32d94ab4677 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,9 +1,10 @@ package ru.javawebinar.topjava.web; +import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.repository.mock.InMemoryUserMealRepositoryImpl; import ru.javawebinar.topjava.repository.UserMealRepository; +import ru.javawebinar.topjava.repository.mock.InMemoryUserMealRepositoryImpl; import ru.javawebinar.topjava.util.UserMealsUtil; import javax.servlet.ServletConfig; @@ -38,7 +39,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); LOG.info(userMeal.isNew() ? "Create {}" : "Update {}", userMeal); - repository.save(userMeal); + repository.save(userMeal, LoggedUser.id()); response.sendRedirect("meals"); } @@ -48,17 +49,17 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t if (action == null) { LOG.info("getAll"); request.setAttribute("mealList", - UserMealsUtil.getWithExceeded(repository.getAll(), UserMealsUtil.DEFAULT_CALORIES_PER_DAY)); + UserMealsUtil.getWithExceeded(repository.getAll(LoggedUser.id()), UserMealsUtil.DEFAULT_CALORIES_PER_DAY)); request.getRequestDispatcher("/mealList.jsp").forward(request, response); } else if (action.equals("delete")) { int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id); + repository.delete(id, LoggedUser.id()); response.sendRedirect("meals"); } else { final UserMeal meal = action.equals("create") ? new UserMeal(LocalDateTime.now(), "", 1000) : - repository.get(getId(request)); + repository.get(getId(request), LoggedUser.id()); request.setAttribute("meal", meal); request.getRequestDispatcher("mealEdit.jsp").forward(request, response); } From e66d40f75a1850b0f8ebe1236d96e77f0172c4cf Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 17 Dec 2015 09:31:46 +0300 Subject: [PATCH 020/108] HW2 Layers --- .../ru/javawebinar/topjava/SpringMain.java | 14 +++++ .../topjava/service/UserMealService.java | 23 ++++++++ .../topjava/service/UserMealServiceImpl.java | 38 ++++++++++++ .../{model => to}/UserMealWithExceed.java | 2 +- .../topjava/util/UserMealsUtil.java | 2 +- .../web/meal/UserMealRestController.java | 58 ++++++++++++++++++- src/main/webapp/mealList.jsp | 2 +- 7 files changed, 133 insertions(+), 6 deletions(-) rename src/main/java/ru/javawebinar/topjava/{model => to}/UserMealWithExceed.java (97%) diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 9b14ec645a08..a50dc23ecee7 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.UserMealWithExceed; +import ru.javawebinar.topjava.web.meal.UserMealRestController; 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(Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); System.out.println(adminUserController.create(new User(1, "userName", "email", "password", Role.ROLE_ADMIN))); + System.out.println(); + + UserMealRestController mealController = appCtx.getBean(UserMealRestController.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/service/UserMealService.java b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java index b17fec811065..9e4ea6413504 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java @@ -1,8 +1,31 @@ package ru.javawebinar.topjava.service; +import ru.javawebinar.topjava.model.UserMeal; +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 UserMealService { + UserMeal 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); + + UserMeal update(UserMeal meal, int userId) throws NotFoundException; + + UserMeal save(UserMeal meal, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java index 5730b8bfb130..fe2aa873de5c 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java @@ -1,13 +1,51 @@ package ru.javawebinar.topjava.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; +import ru.javawebinar.topjava.util.exception.ExceptionUtil; + +import java.time.LocalDateTime; +import java.util.Collection; /** * GKislin * 06.03.2015. */ +@Service public class UserMealServiceImpl implements UserMealService { + @Autowired private UserMealRepository repository; + @Override + public UserMeal get(int id, int userId) { + return ExceptionUtil.check(repository.get(id, userId), id); + } + + @Override + public void delete(int id, int userId) { + ExceptionUtil.check(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 UserMeal update(UserMeal meal, int userId) { + return ExceptionUtil.check(repository.save(meal, userId), meal.getId()); + } + + @Override + public UserMeal save(UserMeal meal, int userId) { + return repository.save(meal, userId); + } } diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java similarity index 97% rename from src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java rename to src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java index 9fd22fa22f71..94aa3fc4b679 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.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/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java index f04d58f113c5..8b20d9674b4e 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.util; import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.model.UserMealWithExceed; +import ru.javawebinar.topjava.to.UserMealWithExceed; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index 51e64a2712c8..60a843d9ecd2 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -1,12 +1,64 @@ package ru.javawebinar.topjava.web.meal; -import ru.javawebinar.topjava.service.UserMealServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.to.UserMealWithExceed; +import ru.javawebinar.topjava.service.UserMealService; +import ru.javawebinar.topjava.util.UserMealsUtil; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; /** * GKislin * 06.03.2015. */ +@Controller public class UserMealRestController { - private UserMealServiceImpl service; + private static final LoggerWrapper LOG = LoggerWrapper.get(UserMealRestController.class); + + @Autowired + private UserMealService service; + + public UserMeal get(int id) { + int userId = LoggedUser.id(); + LOG.info("get meal {} for User {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = LoggedUser.id(); + LOG.info("delete meal {} for User {}", id, userId); + service.delete(id, userId); + } + + public List getAll() { + int userId = LoggedUser.id(); + LOG.info("getAll for User {}", userId); + return UserMealsUtil.getWithExceeded(service.getAll(userId), LoggedUser.getCaloriesPerDay()); + } + + public void update(UserMeal meal) { + int userId = LoggedUser.id(); + LOG.info("update {} for User {}", meal, userId); + service.update(meal, userId); + } + + public UserMeal create(UserMeal meal) { + int userId = LoggedUser.id(); + LOG.info("create {} for User {}", meal, userId); + return service.save(meal, userId); + } -} + public List getBetween(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) { + int userId = LoggedUser.id(); + LOG.info("getBetween dates {} - {} for time {} - {} for User {}", startDate, endDate, startTime, endTime, userId); + return UserMealsUtil.getFilteredWithExceeded( + service.getBetweenDates(startDate, endDate, userId), startTime, endTime, LoggedUser.getCaloriesPerDay() + ); + } +} \ No newline at end of file diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp index dbf36345b502..e1387042875b 100644 --- a/src/main/webapp/mealList.jsp +++ b/src/main/webapp/mealList.jsp @@ -27,7 +27,7 @@ - + <%----%> From eca3a0fe17afe5df8f8312ccbd637b815924103d Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 17 Dec 2015 09:33:43 +0300 Subject: [PATCH 021/108] HW2 Optional MealServlet --- .../javawebinar/topjava/web/MealServlet.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index a32d94ab4677..8371fe2d0467 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,11 +1,10 @@ package ru.javawebinar.topjava.web; -import ru.javawebinar.topjava.LoggedUser; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.repository.UserMealRepository; -import ru.javawebinar.topjava.repository.mock.InMemoryUserMealRepositoryImpl; -import ru.javawebinar.topjava.util.UserMealsUtil; +import ru.javawebinar.topjava.web.meal.UserMealRestController; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -23,12 +22,20 @@ public class MealServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); - private UserMealRepository repository; + private ConfigurableApplicationContext springContext; + private UserMealRestController mealController; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - repository = new InMemoryUserMealRepositoryImpl(); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + mealController = springContext.getBean(UserMealRestController.class); + } + + @Override + public void destroy() { + springContext.close(); + super.destroy(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { @@ -38,8 +45,13 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) LocalDateTime.parse(request.getParameter("dateTime")), request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); - LOG.info(userMeal.isNew() ? "Create {}" : "Update {}", userMeal); - repository.save(userMeal, LoggedUser.id()); + if (userMeal.isNew()) { + LOG.info("Create {}", userMeal); + mealController.create(userMeal); + } else { + LOG.info("Update {}", userMeal); + mealController.update(userMeal); + } response.sendRedirect("meals"); } @@ -48,18 +60,17 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t if (action == null) { LOG.info("getAll"); - request.setAttribute("mealList", - UserMealsUtil.getWithExceeded(repository.getAll(LoggedUser.id()), UserMealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.setAttribute("mealList", mealController.getAll()); request.getRequestDispatcher("/mealList.jsp").forward(request, response); } else if (action.equals("delete")) { int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id, LoggedUser.id()); + mealController.delete(id); response.sendRedirect("meals"); } else { final UserMeal meal = action.equals("create") ? new UserMeal(LocalDateTime.now(), "", 1000) : - repository.get(getId(request), LoggedUser.id()); + mealController.get(getId(request)); request.setAttribute("meal", meal); request.getRequestDispatcher("mealEdit.jsp").forward(request, response); } From dd0bb8b95d352d4e0bd3ff20ca763300b7d884c0 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 17 Dec 2015 09:35:06 +0300 Subject: [PATCH 022/108] HW2 Optional Filter --- .../ru/javawebinar/topjava/util/TimeUtil.java | 15 ++++++- .../javawebinar/topjava/web/MealServlet.java | 43 +++++++++++++------ src/main/webapp/css/style.css | 24 +++++++++++ src/main/webapp/mealEdit.jsp | 19 +------- src/main/webapp/mealList.jsp | 25 +++++++++-- 5 files changed, 91 insertions(+), 35 deletions(-) create mode 100644 src/main/webapp/css/style.css diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java index 4546d5b3b010..d8342ec95a9e 100644 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java @@ -1,5 +1,8 @@ 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; @@ -10,6 +13,8 @@ */ public class TimeUtil { public static final DateTimeFormatter DATE_TME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + public static final LocalDate MIN_DATE = LocalDate.of(0, 1, 1); + public static final LocalDate MAX_DATE = LocalDate.of(3000, 1, 1); public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; @@ -22,4 +27,12 @@ public static boolean isBetween(LocalDateTime ldt, LocalDateTime startDateTime, public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TME_FORMATTER); } -} + + public static LocalDate parseLocalDate(String str, LocalDate def) { + return StringUtils.isEmpty(str) ? def : LocalDate.parse(str); + } + + public static LocalTime parseLocalTime(String str, LocalTime def) { + return StringUtils.isEmpty(str) ? def : LocalTime.parse(str); + } +} \ 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 8371fe2d0467..7f24c7af1602 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -4,6 +4,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.util.TimeUtil; import ru.javawebinar.topjava.web.meal.UserMealRestController; import javax.servlet.ServletConfig; @@ -12,7 +13,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.util.Objects; /** @@ -40,19 +43,35 @@ public void destroy() { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { request.setCharacterEncoding("UTF-8"); - String id = request.getParameter("id"); - UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id), - LocalDateTime.parse(request.getParameter("dateTime")), - request.getParameter("description"), - Integer.valueOf(request.getParameter("calories"))); - if (userMeal.isNew()) { - LOG.info("Create {}", userMeal); - mealController.create(userMeal); - } else { - LOG.info("Update {}", userMeal); - mealController.update(userMeal); + String action = request.getParameter("action"); + if (action == null) { + String id = request.getParameter("id"); + UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id), + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); + if (userMeal.isNew()) { + LOG.info("Create {}", userMeal); + mealController.create(userMeal); + } else { + LOG.info("Update {}", userMeal); + mealController.update(userMeal); + } + response.sendRedirect("meals"); + } else if (action.equals("filter")) { + LocalDate startDate = TimeUtil.parseLocalDate(resetParam("startDate", request), TimeUtil.MIN_DATE); + LocalDate endDate = TimeUtil.parseLocalDate(resetParam("endDate", request), TimeUtil.MAX_DATE); + LocalTime startTime = TimeUtil.parseLocalTime(resetParam("startTime", request), LocalTime.MIN); + LocalTime endTime = TimeUtil.parseLocalTime(resetParam("endTime", request), LocalTime.MAX); + request.setAttribute("mealList", mealController.getBetween(startDate, startTime, endDate, endTime)); + request.getRequestDispatcher("/mealList.jsp").forward(request, response); } - response.sendRedirect("meals"); + } + + private String resetParam(String param, HttpServletRequest request) { + String value = request.getParameter(param); + request.setAttribute(param, value); + return value; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css new file mode 100644 index 000000000000..0c9fc667371c --- /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/mealEdit.jsp b/src/main/webapp/mealEdit.jsp index 443933549130..d71aeb3a6bbd 100644 --- a/src/main/webapp/mealEdit.jsp +++ b/src/main/webapp/mealEdit.jsp @@ -4,24 +4,7 @@ Meal - +
diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp index e1387042875b..2887bbfb5820 100644 --- a/src/main/webapp/mealList.jsp +++ b/src/main/webapp/mealList.jsp @@ -5,15 +5,32 @@ Meal list - +

Home

Meal list

+
+
+
From Date:
+
+
+
+
To Date:
+
+
+
+
From Time:
+
+
+
+
To Time:
+
+
+ +
+
Add Meal
From 31d37fee75e226518f07daa92315d42d14aa328b Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 17 Dec 2015 09:36:35 +0300 Subject: [PATCH 023/108] HW2 Optional selectUser --- src/main/java/ru/javawebinar/topjava/LoggedUser.java | 7 ++++++- .../java/ru/javawebinar/topjava/web/UserServlet.java | 7 +++++++ src/main/webapp/index.html | 11 +++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java index eb68d99a0d6e..115866e29f99 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggedUser.java +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -7,9 +7,14 @@ * 06.03.2015. */ public class LoggedUser { + public static int id = 1; public static int id() { - return 1; + return id; + } + + public static void setId(int id) { + LoggedUser.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 eb2188bc8435..40adae5b88cc 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web; +import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.LoggerWrapper; import javax.servlet.ServletException; @@ -15,6 +16,12 @@ public class UserServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(UserServlet.class); + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + int userId = Integer.valueOf(request.getParameter("userId")); + LoggedUser.setId(userId); + response.sendRedirect("meals"); + } + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LOG.debug("redirect to userList"); diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index a16fd7167274..4991bcc6933a 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -7,9 +7,16 @@

Проект "Top Java"


+
+ Login as: + + From 1e8f78fff41b3dc1ab9b286eb3c062b2269389dd Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 18 Dec 2015 21:20:11 +0300 Subject: [PATCH 024/108] 3_06 Add Bean Life Cycle --- .../mock/InMemoryUserRepositoryImpl.java | 15 +++++++++++++++ 1 file changed, 15 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 6f40cd7addbd..3d1d357351f6 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -1,10 +1,13 @@ package ru.javawebinar.topjava.repository.mock; import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.model.Role; 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; @@ -19,6 +22,8 @@ */ @Repository public class InMemoryUserRepositoryImpl implements UserRepository { + private static final LoggerWrapper LOG = LoggerWrapper.get(InMemoryUserRepositoryImpl.class); + private Map repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); @@ -32,6 +37,16 @@ public class InMemoryUserRepositoryImpl implements UserRepository { save(new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN)); } + @PostConstruct + public void postConstruct() { + LOG.info("+++ PostConstruct"); + } + + @PreDestroy + public void preDestroy() { + LOG.info("+++ PreDestroy"); + } + @Override public User save(User user) { Objects.requireNonNull(user); From b97e7ecec7c9a446f777acb455de6352a3c33e65 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 18 Dec 2015 21:25:24 +0300 Subject: [PATCH 025/108] 3_07 Add JUnit --- pom.xml | 10 ++++ .../ru/javawebinar/topjava/LoggerWrapper.java | 2 +- .../ru/javawebinar/topjava/SpringMain.java | 0 .../ru/javawebinar/topjava/UserTestData.java | 16 ++++++ .../mock/InMemoryUserMealRepositoryImpl.java | 6 +- .../mock/InMemoryUserRepositoryImpl.java | 9 --- .../topjava/web/AdminInMemoryTest.java | 55 +++++++++++++++++++ 7 files changed, 85 insertions(+), 13 deletions(-) rename src/{main => test}/java/ru/javawebinar/topjava/SpringMain.java (100%) create mode 100644 src/test/java/ru/javawebinar/topjava/UserTestData.java rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java (94%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java (86%) create mode 100644 src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java diff --git a/pom.xml b/pom.xml index bae4d0d90899..eece1ca5492e 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,8 @@ 1.1.3 1.7.13 + + 4.12 @@ -96,6 +98,14 @@ + + + + junit + junit + ${junit.version} + test + diff --git a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java index 642bb6c4c7ea..8ab6153791d8 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java +++ b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java @@ -72,7 +72,7 @@ public UnsupportedOperationException getUnsupportedOperationException(String msg } public NotFoundException getNotFoundException(String reason) { - logger.error("No data found"); + logger.error(reason); return new NotFoundException(reason); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java similarity index 100% rename from src/main/java/ru/javawebinar/topjava/SpringMain.java rename to src/test/java/ru/javawebinar/topjava/SpringMain.java diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java new file mode 100644 index 000000000000..55a2c85f4770 --- /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(1, "User", "user@yandex.ru", "password", Role.ROLE_USER); + public static final User ADMIN = new User(2, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java similarity index 94% rename from src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java rename to src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java index ebfdfb644527..365dae177a3c 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserMealRepositoryImpl.java @@ -13,8 +13,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -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 @@ -82,4 +82,4 @@ public Collection getBetween(LocalDateTime startDateTime, LocalDateTim .filter(um -> TimeUtil.isBetween(um.getDateTime(), startDateTime, endDateTime)) .collect(Collectors.toList()); } -} +} \ No newline at end of file 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 86% 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 3d1d357351f6..1ef4884f32f3 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -2,7 +2,6 @@ import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.LoggerWrapper; -import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; @@ -29,14 +28,6 @@ public class InMemoryUserRepositoryImpl implements UserRepository { public static final Comparator USER_COMPARATOR = (u1, u2) -> u1.getName().compareTo(u2.getName()); - public static final int USER_ID = 1; - public static final int ADMIN_ID = 2; - - { - save(new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER)); - save(new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN)); - } - @PostConstruct public void postConstruct() { LOG.info("+++ PostConstruct"); diff --git a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java new file mode 100644 index 000000000000..df7f01da6bdf --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.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 AdminInMemoryTest { + 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); + } + + @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); + } + + @AfterClass + public static void afterClass() { + appCtx.close(); + } + + @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 3a7fa84e11f1a8d30c5e28ca0a68ae60e483430d Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 18 Dec 2015 21:46:11 +0300 Subject: [PATCH 026/108] 3_08 Add Spring Test --- pom.xml | 7 +++ .../topjava/web/SpringAdminInMemoryTest.java | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java diff --git a/pom.xml b/pom.xml index eece1ca5492e..26381ee46569 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,13 @@ ${junit.version} test + + org.springframework + spring-test + ${spring.version} + test + + diff --git a/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java new file mode 100644 index 000000000000..052f6817a7fb --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.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 SpringAdminInMemoryTest { + + @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 b7fe26d043d87b288d1ff747f3c4dd8a1d2f4219 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 22:27:00 +0300 Subject: [PATCH 027/108] 3_9_Add Postgres --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 26381ee46569..654c6bc0de3d 100644 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,13 @@ + + + org.postgresql + postgresql + 9.4-1206-jdbc42 + + junit From b5cd1f861310c42cbbf6557733c1f14dd186a4e7 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 22:45:29 +0300 Subject: [PATCH 028/108] 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 000000000000..620c77f27b0e --- /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 unique_email 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 000000000000..199e2ace615d --- /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 2d0b0cfcc657af934068f7db36273658d55fc252 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 23:10:24 +0300 Subject: [PATCH 029/108] 3_11 Impl JdbcUserRepository add spring-db --- pom.xml | 5 ++ .../jdbc/JdbcUserRepositoryImpl.java | 84 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 25 ++++++ 3 files changed, 114 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 654c6bc0de3d..692c8be1a149 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,11 @@ postgresql 9.4-1206-jdbc42 + + 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 000000000000..5646bad071ed --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -0,0 +1,84 @@ +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); + } + + @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 000000000000..a1f6b09a7464 --- /dev/null +++ b/src/main/resources/spring/spring-db.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c66096baab5508fd50d174356aefecb3350db82b Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 23:28:53 +0300 Subject: [PATCH 030/108] 3_12 Test UserService --- .../javawebinar/topjava/model/BaseEntity.java | 2 + .../ru/javawebinar/topjava/model/User.java | 4 + .../jdbc/JdbcUserMealRepositoryImpl.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 | 67 ++++++++++++- .../topjava/matcher/ModelMatcher.java | 34 +++++++ .../topjava/service/UserServiceTest.java | 96 +++++++++++++++++++ 10 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.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 e8f1a2ce9223..40581879f82f 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -9,6 +9,8 @@ public class BaseEntity { protected static final LoggerWrapper LOG = LoggerWrapper.get(BaseEntity.class); + public static final int START_SEQ = 100000; + protected Integer id; public BaseEntity() { diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 34b5977a5917..22d746307bf4 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, UserMealsUtil.DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java new file mode 100644 index 000000000000..77c0ba8f639d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava.repository.jdbc; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.repository.UserMealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * User: gkislin + * Date: 26.08.2014 + */ + +@Repository +public class JdbcUserMealRepositoryImpl implements UserMealRepository { + + @Override + public UserMeal save(UserMeal UserMeal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public UserMeal 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 000000000000..8d4a6cc3c719 --- /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 c285b264141d..756c97f42c35 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 a1f6b09a7464..051f225300cd 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 000000000000..ac44047caa52 --- /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.UserMeal; + +/** + * GKislin + * 13.03.2015. + */ +public class MealTestData { + + public static final ModelMatcher MATCHER = new ModelMatcher<>(UserMeal::toString); + +} diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 55a2c85f4770..c6855207f44e 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -1,16 +1,75 @@ package ru.javawebinar.topjava; +import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.UserMealsUtil; + +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; + +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<>(u -> ((u instanceof TestUser) ? (TestUser) u : new TestUser(u))); + + public static class TestUser extends User { + + public TestUser(User u) { + this(u.getId(), u.getName(), u.getEmail(), u.getPassword(), u.getCaloriesPerDay(), u.isEnabled(), u.getRoles()); + } + + public TestUser(String name, String email, String password, Role role, Role... roles) { + this(null, name, email, password, UserMealsUtil.DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); + } + + public TestUser(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) { + super(id, name, email, password, caloriesPerDay, enabled, roles); + } + + public User asUser() { + return new User(this); + } + + @Override + public String toString() { + return "User (" + + "id=" + id + + ", email=" + email + + ", name=" + name + + ", enabled=" + enabled + + ", password=" + password + + ", authorities=" + roles + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } - public static final User USER = new User(1, "User", "user@yandex.ru", "password", Role.ROLE_USER); - public static final User ADMIN = new User(2, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); + TestUser that = (TestUser) o; + return Objects.equals(this.password, that.password) + && Objects.equals(this.id, that.id) + && Objects.equals(this.name, that.name) + && Objects.equals(this.email, that.email) + && Objects.equals(this.caloriesPerDay, that.caloriesPerDay) + && Objects.equals(this.enabled, that.enabled); + } + } } 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 000000000000..f2b18b2e34bd --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java @@ -0,0 +1,34 @@ +package ru.javawebinar.topjava.matcher; + +import org.junit.Assert; + +import java.util.Collection; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * GKislin + * 06.01.2015. + * + * @param : entity + * @param : testEntity, converter result for compare + */ +public class ModelMatcher { + protected Function entityConverter; + + public ModelMatcher(Function entityConverter) { + this.entityConverter = entityConverter; + } + + public void assertEquals(T expected, T actual) { + Assert.assertEquals(entityConverter.apply(expected), entityConverter.apply(actual)); + } + + public void assertCollectionEquals(Collection expected, Collection actual) { + Assert.assertEquals(map(expected, entityConverter), map(actual, entityConverter)); + } + + public static Collection map(Collection collection, Function converter) { + return collection.stream().map(converter).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 000000000000..45e43b2968aa --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -0,0 +1,96 @@ +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.UserTestData.*; +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 + protected UserService service; + + @Autowired + private DbPopulator dbPopulator; + + @Before + public void setUp() throws Exception { + dbPopulator.execute(); + } + + @Test + public void testSave() throws Exception { + TestUser tu = new TestUser(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); + User created = service.save(tu.asUser()); + tu.setId(created.getId()); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, tu, USER), service.getAll()); + } + + @Test(expected = DataAccessException.class) + public void testDuplicateMailSave() throws Exception { + service.save(new TestUser("Duplicate", "user@yandex.ru", "newPass", Role.ROLE_USER).asUser()); + } + + @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 { + TestUser updated = new TestUser(USER); + updated.setName("UpdatedName"); + updated.setCaloriesPerDay(330); + service.update(updated.asUser()); + MATCHER.assertEquals(updated, service.get(USER_ID)); + } +} \ No newline at end of file From b6230e489e5459566aa3451a5e10f35619f75f58 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 23:32:12 +0300 Subject: [PATCH 031/108] 3_12 Test Logging --- src/test/resources/logback-test.xml | 21 +++++++++++++++++++++ 1 file changed, 21 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 000000000000..80c38026d409 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,21 @@ + + + + true + + + + + UTF-8 + %-5level %logger{0} - %msg%n + + + + + + + + + + + \ No newline at end of file From d25c135b892495e0acdff94c3a0dff4cd7486d53 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 19 Dec 2015 23:33:25 +0300 Subject: [PATCH 032/108] 3_14 Fix id --- src/main/java/ru/javawebinar/topjava/LoggedUser.java | 4 +++- src/main/java/ru/javawebinar/topjava/web/MealServlet.java | 2 +- src/main/webapp/index.html | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java index 115866e29f99..813658a2b4c9 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggedUser.java +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -2,12 +2,14 @@ import ru.javawebinar.topjava.util.UserMealsUtil; +import ru.javawebinar.topjava.model.BaseEntity; + /** * GKislin * 06.03.2015. */ public class LoggedUser { - 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 7f24c7af1602..e8fea2103130 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -31,7 +31,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(UserMealRestController.class); } diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 4991bcc6933a..4136c15ef689 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -9,8 +9,8 @@

Проект " Login as: From eac8fc6a1cd855e9868daa17448791c61d5f29c2 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 24 Dec 2015 19:58:11 +0300 Subject: [PATCH 033/108] 4_1 HW4 --- .../javawebinar/topjava/model/UserMeal.java | 25 ++++--- .../jdbc/JdbcUserMealRepositoryImpl.java | 67 ++++++++++++++++--- src/main/resources/db/initDB.sql | 13 +++- src/main/resources/db/populateDB.sql | 11 +++ 4 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java index 13662581d16e..5eb599b094b9 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -6,21 +6,20 @@ * GKislin * 11.01.2015. */ -public class UserMeal { - protected Integer id; +public class UserMeal extends BaseEntity { - protected final LocalDateTime dateTime; + protected LocalDateTime dateTime; - protected final String description; + protected String description; - protected final int calories; + protected int calories; public UserMeal(LocalDateTime dateTime, String description, int calories) { this(null, dateTime, description, calories); } public UserMeal(Integer id, LocalDateTime dateTime, String description, int calories) { - this.id = id; + super(id); this.dateTime = dateTime; this.description = description; this.calories = calories; @@ -46,8 +45,16 @@ public Integer getId() { return id; } - public boolean isNew() { - return id == null; + 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 @@ -59,4 +66,4 @@ public String toString() { ", calories=" + calories + '}'; } -} +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java index 77c0ba8f639d..3442b8151a96 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.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.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.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; +import javax.sql.DataSource; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; @@ -15,28 +24,68 @@ @Repository public class JdbcUserMealRepositoryImpl implements UserMealRepository { + private static final RowMapper ROW_MAPPER = + (rs, rowNum) -> + new UserMeal(rs.getInt("id"), rs.getTimestamp("date_time").toLocalDateTime(), + rs.getString("description"), rs.getInt("calories")); + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private SimpleJdbcInsert insertUserMeal; + + @Autowired + public JdbcUserMealRepositoryImpl(DataSource dataSource) { + this.insertUserMeal = new SimpleJdbcInsert(dataSource) + .withTableName("meals") + .usingGeneratedKeyColumns("id"); + } + @Override - public UserMeal save(UserMeal UserMeal, int userId) { - return null; + public UserMeal save(UserMeal userMeal, int userId) { + MapSqlParameterSource map = new MapSqlParameterSource() + .addValue("id", userMeal.getId()) + .addValue("description", userMeal.getDescription()) + .addValue("calories", userMeal.getCalories()) + .addValue("date_time", Timestamp.valueOf(userMeal.getDateTime())) + .addValue("user_id", userId); + + if (userMeal.isNew()) { + Number newId = insertUserMeal.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 UserMeal get(int id, int userId) { - return null; + List userMeals = jdbcTemplate.query( + "SELECT * FROM meals WHERE id = ? AND user_id = ?", ROW_MAPPER, id, userId); + return DataAccessUtils.singleResult(userMeals); } - - @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, Timestamp.valueOf(startDate), Timestamp.valueOf(endDate)); } -} +} \ No newline at end of file diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql index 620c77f27b0e..62eca097467b 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; @@ -14,7 +15,7 @@ CREATE TABLE users enabled BOOL DEFAULT TRUE, calories_per_day INTEGER DEFAULT 2000 NOT NULL ); -CREATE UNIQUE INDEX unique_email ON users (email); +CREATE UNIQUE INDEX users_unique_email_idx ON users (email); CREATE TABLE user_roles ( @@ -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, + description TEXT, + calories INT, + 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 199e2ace615d..0fbffd0dfb58 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 4872af60eca6cf0e5fa25d686877a54ad38fd82f Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 24 Dec 2015 19:59:30 +0300 Subject: [PATCH 034/108] 4_2 HW4 Optional --- .../ru/javawebinar/topjava/MealTestData.java | 29 ++++++ .../topjava/service/UserMealServiceTest.java | 94 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index ac44047caa52..173673299f57 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -3,6 +3,14 @@ import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.UserMeal; +import java.time.LocalDateTime; +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 +19,25 @@ public class MealTestData { public static final ModelMatcher MATCHER = new ModelMatcher<>(UserMeal::toString); + public static final int MEAL1_ID = START_SEQ + 2; + public static final int ADMIN_MEAL_ID = START_SEQ + 8; + + public static final UserMeal MEAL1 = new UserMeal(MEAL1_ID, LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500); + public static final UserMeal MEAL2 = new UserMeal(MEAL1_ID + 1, LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000); + public static final UserMeal MEAL3 = new UserMeal(MEAL1_ID + 2, LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500); + public static final UserMeal MEAL4 = new UserMeal(MEAL1_ID + 3, LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 500); + public static final UserMeal MEAL5 = new UserMeal(MEAL1_ID + 4, LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 1000); + public static final UserMeal MEAL6 = new UserMeal(MEAL1_ID + 5, LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510); + public static final UserMeal ADMIN_MEAL = new UserMeal(ADMIN_MEAL_ID, LocalDateTime.of(2015, Month.JUNE, 1, 14, 0), "Админ ланч", 510); + public static final UserMeal ADMIN_MEAL2 = new UserMeal(ADMIN_MEAL_ID + 1, LocalDateTime.of(2015, Month.JUNE, 1, 21, 0), "Админ ужин", 1500); + + public static final List USER_MEALS = Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1); + + public static UserMeal getCreated() { + return new UserMeal(null, of(2015, Month.JUNE, 1, 18, 0), "Созданный ужин", 300); + } + + public static UserMeal getUpdated() { + return new UserMeal(MEAL1_ID, MEAL1.getDateTime(), "Обновленный завтрак", 200); + } } diff --git a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java new file mode 100644 index 000000000000..b249c9af877e --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java @@ -0,0 +1,94 @@ +package ru.javawebinar.topjava.service; + +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.MealTestData; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.util.DbPopulator; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.time.LocalDate; +import java.time.Month; +import java.util.ArrayList; +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 UserMealServiceTest { + + @Autowired + protected UserMealService service; + + @Autowired + private DbPopulator dbPopulator; + + @Before + public void setUp() throws Exception { + dbPopulator.execute(); + } + + @Test + public void testDelete() throws Exception { + service.delete(MealTestData.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 { + UserMeal 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 { + UserMeal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); + MATCHER.assertEquals(ADMIN_MEAL, actual); + } + + @Test(expected = NotFoundException.class) + public void testGetNotFound() throws Exception { + service.get(MEAL1_ID, ADMIN_ID); + } + + @Test + public void testUpdate() throws Exception { + UserMeal updated = getUpdated(); + service.update(updated, USER_ID); + MATCHER.assertEquals(updated, service.get(MEAL1_ID, USER_ID)); + } + + @Test(expected = NotFoundException.class) + public void testNotFoundUpdate() throws Exception { + UserMeal item = service.get(MEAL1_ID, USER_ID); + service.update(item, ADMIN_ID); + } + + @Test + public void testGetAll() throws Exception { + MATCHER.assertCollectionEquals(USER_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 5a28db12a0d865b4eafb8f2b5311d56e462dde46 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 24 Dec 2015 22:05:34 +0300 Subject: [PATCH 035/108] 4_3 Init and Populate DB --- .../javawebinar/topjava/util/DbPopulator.java | 28 ------------------- src/main/resources/spring/spring-db.xml | 11 ++++---- .../topjava/service/UserMealServiceTest.java | 12 ++------ .../topjava/service/UserServiceTest.java | 13 ++------- 4 files changed, 12 insertions(+), 52 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 8d4a6cc3c719..000000000000 --- 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/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 051f225300cd..0e228352bad7 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,14 +1,15 @@ + 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, 24 Dec 2015 22:22:41 +0300 Subject: [PATCH 036/108] 4_4 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 +- .../java/ru/javawebinar/topjava/web/AdminInMemoryTest.java | 2 +- .../javawebinar/topjava/web/SpringAdminInMemoryTest.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 756c97f42c35..5ba07c0e0e8e 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 0e228352bad7..a2c9da98e9f8 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -6,6 +6,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index a50dc23ecee7..f14692b18323 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(Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); System.out.println(adminUserController.create(new User(1, "userName", "email", "password", Role.ROLE_ADMIN))); diff --git a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java index df7f01da6bdf..7c3c81e3f292 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java @@ -21,7 +21,7 @@ public class AdminInMemoryTest { @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/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java index 052f6817a7fb..62aa9e77acc6 100644 --- a/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.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 SpringAdminInMemoryTest { diff --git a/src/test/resources/spring/mock.xml b/src/test/resources/spring/mock.xml new file mode 100644 index 000000000000..c7c9f62cc5d8 --- /dev/null +++ b/src/test/resources/spring/mock.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file From 3ee53d1eaa38b3ef9b9747902057c3f77851376b Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 24 Dec 2015 22:31:55 +0300 Subject: [PATCH 037/108] 4_5 Make Entry, add JPA --- pom.xml | 22 ++++++- .../javawebinar/topjava/model/BaseEntity.java | 7 ++ .../topjava/model/NamedEntity.java | 8 +++ .../ru/javawebinar/topjava/model/User.java | 22 +++++++ .../javawebinar/topjava/model/UserMeal.java | 5 ++ .../jpa/JpaUserMealRepositoryImpl.java | 42 ++++++++++++ .../repository/jpa/JpaUserRepositoryImpl.java | 65 +++++++++++++++++++ src/main/resources/spring/spring-db.xml | 29 ++++++++- 8 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepositoryImpl.java diff --git a/pom.xml b/pom.xml index 692c8be1a149..c3e78a14ac1c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,10 @@ 1.7.13 4.12 + + + 5.0.6.Final + 5.1.3.Final @@ -107,9 +111,25 @@ org.springframework - spring-jdbc + spring-orm ${spring.version} + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + javax.transaction + jta + 1.1 + runtime + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index 40581879f82f..e5d8b6dd9971 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -2,15 +2,22 @@ import ru.javawebinar.topjava.LoggerWrapper; +import javax.persistence.*; + /** * User: gkislin * Date: 22.08.2014 */ +@MappedSuperclass +@Access(AccessType.FIELD) public class BaseEntity { protected static final LoggerWrapper LOG = LoggerWrapper.get(BaseEntity.class); 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/NamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java index ca92e394dc98..e593d37981e7 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 22d746307bf4..2b6e4e0c25d7 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.UserMealsUtil; +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 protected String email; + @Column(name = "password", nullable = false) + @NotEmpty + @Length(min = 5) protected String password; + @Column(name = "enabled", nullable = false) protected boolean enabled = true; + @Column(name = "registered", columnDefinition = "timestamp default now()") protected Date registered = new Date(); + @Enumerated(EnumType.STRING) + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "role") + @ElementCollection(fetch = FetchType.EAGER) protected Set roles; + @Column(name = "calories_per_day", columnDefinition = "default 2000") + @Digits(fraction = 0, integer = 4) protected int caloriesPerDay = UserMealsUtil.DEFAULT_CALORIES_PER_DAY; public User() { diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java index 5eb599b094b9..80f49ce6ce0d 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.model; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; import java.time.LocalDateTime; /** @@ -14,6 +16,9 @@ public class UserMeal extends BaseEntity { protected int calories; + @ManyToOne(fetch = FetchType.LAZY) + private User user; + public UserMeal(LocalDateTime dateTime, String description, int calories) { this(null, dateTime, description, calories); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java new file mode 100644 index 000000000000..647a24eef180 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava.repository.jpa; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.repository.UserMealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * User: gkisline + * Date: 26.08.2014 + */ + +@Repository +public class JpaUserMealRepositoryImpl implements UserMealRepository { + + @Override + public UserMeal save(UserMeal userMeal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public UserMeal 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 000000000000..b662bea948ba --- /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/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index a2c9da98e9f8..250ed31cbc13 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -1,12 +1,14 @@ - + @@ -22,6 +24,28 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file From 507862ebbdf3a1c7aedf2ea33f47ac5d65b8b3a3 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 25 Dec 2015 09:02:23 +0300 Subject: [PATCH 038/108] 4_6 JPA impl transaction --- .../ru/javawebinar/topjava/model/User.java | 9 +++++++ .../repository/jpa/JpaUserRepositoryImpl.java | 15 +++++++---- src/main/resources/spring/spring-db.xml | 27 ++++++++++++------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 2b6e4e0c25d7..8fc29ca0a4ce 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 b662bea948ba..2b4057f3a4b4 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,12 @@ package ru.javawebinar.topjava.repository.jpa; 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 +14,7 @@ * Date: 29.08.2014 */ @Repository +@Transactional(readOnly = true) public class JpaUserRepositoryImpl implements UserRepository { /* @@ -29,6 +30,7 @@ private Session openSession() { private EntityManager em; @Override + @Transactional public User save(User user) { if (user.isNew()) { em.persist(user); @@ -44,22 +46,25 @@ 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; + return em.createNamedQuery(User.BY_EMAIL, User.class).setParameter(1, email).getSingleResult(); } @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 250ed31cbc13..5b1431b7fb5b 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -3,8 +3,9 @@ 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/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"> @@ -44,14 +45,20 @@ - + + + + \ No newline at end of file From 79645bf04a55fde414668c7e368a20890f2e6344 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 25 Dec 2015 15:10:06 +0300 Subject: [PATCH 039/108] 4_7 HSQLDB support --- pom.xml | 6 ++++ src/main/resources/db/hsqldb.properties | 13 +++++++++ src/main/resources/db/initDB_hsql.sql | 37 +++++++++++++++++++++++++ src/main/resources/spring/spring-db.xml | 9 ++++-- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/db/hsqldb.properties create mode 100644 src/main/resources/db/initDB_hsql.sql diff --git a/pom.xml b/pom.xml index c3e78a14ac1c..ac6637e5ed00 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,12 @@ postgresql 9.4-1206-jdbc42 + + org.hsqldb + hsqldb + 2.3.2 + + org.springframework spring-orm diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties new file mode 100644 index 000000000000..560579e13f71 --- /dev/null +++ b/src/main/resources/db/hsqldb.properties @@ -0,0 +1,13 @@ +#database.url=jdbc:hsqldb:file:D:/temp/topjava +database.url=jdbc:hsqldb:mem:topjava + +database.username=sa +database.password= +database.driverClassName=org.hsqldb.jdbcDriver + +database.init=true + +jdbc.initLocation=initDB_hsql.sql +jpa.showSql=true +hibernate.format_sql=true +hibernate.use_sql_comments=true diff --git a/src/main/resources/db/initDB_hsql.sql b/src/main/resources/db/initDB_hsql.sql new file mode 100644 index 000000000000..a2c615d7b6e7 --- /dev/null +++ b/src/main/resources/db/initDB_hsql.sql @@ -0,0 +1,37 @@ +DROP TABLE user_roles IF EXISTS; +DROP TABLE meals IF EXISTS; +DROP TABLE users IF EXISTS; +DROP SEQUENCE global_seq IF EXISTS; + +CREATE SEQUENCE GLOBAL_SEQ AS INTEGER START WITH 100000; + +CREATE TABLE users +( + id INTEGER GENERATED BY DEFAULT AS SEQUENCE GLOBAL_SEQ PRIMARY KEY, + name VARCHAR(255), + email VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + registered TIMESTAMP DEFAULT now(), + enabled BOOLEAN 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(255), + CONSTRAINT user_roles_idx UNIQUE (user_id, role), + FOREIGN KEY ( user_id ) REFERENCES USERS ( id ) ON DELETE CASCADE +); + +CREATE TABLE meals +( + id INTEGER GENERATED BY DEFAULT AS SEQUENCE GLOBAL_SEQ PRIMARY KEY, + date_time TIMESTAMP, + description VARCHAR(255), + calories INT, + user_id INTEGER NOT NULL, + FOREIGN KEY ( user_id ) REFERENCES USERS ( id ) ON DELETE CASCADE +); +CREATE UNIQUE INDEX meals_unique_user_datetime_idx ON meals(user_id, date_time) \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 5b1431b7fb5b..ba580712d873 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -7,19 +7,22 @@ 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"> - + + + + - + - + From 9f1332d55da3fb6667e1842a86f113ede558a837 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 31 Dec 2015 18:22:33 +0300 Subject: [PATCH 040/108] 4_1 HW3 --- pom.xml | 5 +++ .../javawebinar/topjava/model/UserMeal.java | 42 ++++++++++++++++++- .../jpa/JpaUserMealRepositoryImpl.java | 41 +++++++++++++++--- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index ac6637e5ed00..f794cff2766d 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,11 @@ hibernate-entitymanager ${hibernate.version} + + org.hibernate + hibernate-java8 + ${hibernate.version} + javax.transaction jta diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java index 80f49ce6ce0d..e1423822dbd3 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -1,24 +1,54 @@ package ru.javawebinar.topjava.model; -import javax.persistence.FetchType; -import javax.persistence.ManyToOne; +import org.hibernate.validator.constraints.NotEmpty; +import org.hibernate.validator.constraints.Range; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; import java.time.LocalDateTime; /** * GKislin * 11.01.2015. */ +@NamedQueries({ + @NamedQuery(name = UserMeal.GET, query = "SELECT m FROM UserMeal m WHERE m.id=:id AND m.user.id=:userId"), + @NamedQuery(name = UserMeal.ALL_SORTED, query = "SELECT m FROM UserMeal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC"), + @NamedQuery(name = UserMeal.DELETE, query = "DELETE FROM UserMeal m WHERE m.id=:id AND m.user.id=:userId"), + @NamedQuery(name = UserMeal.GET_BETWEEN, + query = "SELECT m from UserMeal m WHERE m.user.id=:userId " + + " AND m.dateTime BETWEEN :startDate AND :endDate ORDER BY m.dateTime DESC"), + +// @NamedQuery(name = UserMeal.UPDATE, query = "UPDATE UserMeal 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 UserMeal extends BaseEntity { + public static final String GET = "UserMeal.get"; + public static final String ALL_SORTED = "UserMeal.getAll"; + public static final String DELETE = "UserMeal.delete"; + public static final String GET_BETWEEN = "UserMeal.getBetween"; + @Column(name = "date_time", nullable = false) + @NotNull protected LocalDateTime dateTime; + @Column(name = "description", nullable = false) + @NotEmpty protected String description; + @Column(name = "calories", nullable = false) + @Range(min = 10, max = 5000) protected int calories; @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) private User user; + public UserMeal() { + } + public UserMeal(LocalDateTime dateTime, String description, int calories) { this(null, dateTime, description, calories); } @@ -62,6 +92,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 "UserMeal{" + diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java index 647a24eef180..16b304c57eff 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserMealRepositoryImpl.java @@ -1,9 +1,14 @@ 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.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; import java.time.LocalDateTime; import java.util.List; @@ -13,30 +18,56 @@ */ @Repository +@Transactional(readOnly = true) public class JpaUserMealRepositoryImpl implements UserMealRepository { + @PersistenceContext + private EntityManager em; + @Override + @Transactional public UserMeal save(UserMeal userMeal, int userId) { - return null; + User ref = em.getReference(User.class, userId); + userMeal.setUser(ref); + + if (userMeal.isNew()) { + em.persist(userMeal); + return userMeal; + } else { + return get(userMeal.getId(), userId) == null ? null : em.merge(userMeal); + } } @Override + @Transactional public boolean delete(int id, int userId) { - return false; + return em.createNamedQuery(UserMeal.DELETE) + .setParameter("id", id) + .setParameter("userId", userId) + .executeUpdate() != 0; } @Override public UserMeal get(int id, int userId) { - return null; + List userMeals = em.createNamedQuery(UserMeal.GET, UserMeal.class) + .setParameter("id", id) + .setParameter("userId", userId) + .getResultList(); + return DataAccessUtils.singleResult(userMeals); } @Override public List getAll(int userId) { - return null; + return em.createNamedQuery(UserMeal.ALL_SORTED, UserMeal.class) + .setParameter("userId", userId) + .getResultList(); } @Override public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { - return null; + return em.createNamedQuery(UserMeal.GET_BETWEEN, UserMeal.class) + .setParameter("userId", userId) + .setParameter("startDate", startDate) + .setParameter("endDate", endDate).getResultList(); } } \ No newline at end of file From 671695ebea34e264c614aad14b072ce25d7b6c5d Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 31 Dec 2015 18:28:46 +0300 Subject: [PATCH 041/108] 5_2 HW4 Optional --- .../topjava/service/UserMealServiceTest.java | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java index 7b8152c0afb6..a578bb3552f0 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java @@ -1,22 +1,25 @@ package ru.javawebinar.topjava.service; -import org.junit.Assert; -import org.junit.Before; +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.springframework.beans.factory.annotation.Autowired; 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.LoggerWrapper; import ru.javawebinar.topjava.MealTestData; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.time.LocalDate; import java.time.Month; -import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.TimeUnit; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; @@ -29,6 +32,24 @@ @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) public class UserMealServiceTest { + private static final LoggerWrapper LOG = LoggerWrapper.get(UserMealServiceTest.class); + + @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() { + private void logInfo(Description description, long nanos) { + LOG.info(String.format("+++ Test %s spent %d microseconds", + description.getMethodName(), TimeUnit.NANOSECONDS.toMicros(nanos))); + } + + @Override + protected void finished(long nanos, Description description) { + logInfo(description, nanos); + } + }; @Autowired protected UserMealService service; @@ -39,8 +60,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); } @@ -57,8 +79,9 @@ public void testGet() throws Exception { MATCHER.assertEquals(ADMIN_MEAL, actual); } - @Test(expected = NotFoundException.class) + @Test public void testGetNotFound() throws Exception { + thrown.expect(NotFoundException.class); service.get(MEAL1_ID, ADMIN_ID); } @@ -69,9 +92,11 @@ public void testUpdate() throws Exception { MATCHER.assertEquals(updated, service.get(MEAL1_ID, USER_ID)); } - @Test(expected = NotFoundException.class) + @Test public void testNotFoundUpdate() throws Exception { UserMeal item = service.get(MEAL1_ID, USER_ID); + thrown.expect(NotFoundException.class); + thrown.expectMessage("Not found entity with id=" + MEAL1_ID); service.update(item, ADMIN_ID); } From 1944340c37cd3dd46a64d41381077cad08f63c7c Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 31 Dec 2015 20:07:45 +0300 Subject: [PATCH 042/108] 5_3 Profiles Connection Pool --- pom.xml | 44 ++++++++++++++----- .../java/ru/javawebinar/topjava/Profiles.java | 13 ++++++ src/main/resources/spring/spring-db.xml | 37 ++++++++++------ .../topjava/service/UserMealServiceTest.java | 3 ++ .../topjava/service/UserServiceTest.java | 3 ++ 5 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/Profiles.java diff --git a/pom.xml b/pom.xml index f794cff2766d..6d5777cd1d4d 100644 --- a/pom.xml +++ b/pom.xml @@ -13,12 +13,16 @@ 1.8 + 7.0.57 UTF-8 UTF-8 4.2.3.RELEASE + 9.4-1206-jdbc42 + 2.3.2 + 1.1.3 1.7.13 @@ -103,18 +107,6 @@ - - - org.postgresql - postgresql - 9.4-1206-jdbc42 - - - org.hsqldb - hsqldb - 2.3.2 - - org.springframework spring-orm @@ -159,6 +151,34 @@ + + hsqldb + + + org.hsqldb + hsqldb + ${hsqldb.version} + + + + + postgres + + + org.postgresql + postgresql + ${postgres.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 000000000000..23e905ed9c20 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -0,0 +1,13 @@ +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"; +} diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index ba580712d873..15606ab4a09c 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -7,11 +7,6 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 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"> - - - - - @@ -19,15 +14,6 @@ - - - - - - - - @@ -64,4 +50,27 @@ --> + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java index a578bb3552f0..7181dcd3d943 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java @@ -7,12 +7,14 @@ import org.junit.runner.Description; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.MealTestData; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -31,6 +33,7 @@ }) @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.POSTGRES) public class UserMealServiceTest { private static final LoggerWrapper LOG = LoggerWrapper.get(UserMealServiceTest.class); diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index f0fd6feddf4d..a9d87d6d6966 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.UserTestData.*; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -25,6 +27,7 @@ }) @RunWith(SpringJUnit4ClassRunner.class) @Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +@ActiveProfiles(Profiles.HSQLDB) public class UserServiceTest { @Autowired From 778822eaa87a03a958357204fbac53f618ab9d79 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 1 Jan 2016 14:19:09 +0300 Subject: [PATCH 043/108] 5_4 Spring Data JPA --- pom.xml | 16 +++++-- .../DataJpaUserMealRepositoryImpl.java | 40 ++++++++++++++++ .../datajpa/DataJpaUserRepositoryImpl.java | 47 +++++++++++++++++++ .../datajpa/ProxyUserRepository.java | 37 +++++++++++++++ src/main/resources/spring/spring-db.xml | 15 +++++- 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java diff --git a/pom.xml b/pom.xml index 6d5777cd1d4d..1f25e112ddf1 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ UTF-8 UTF-8 + 1.9.0.RELEASE 4.2.3.RELEASE 9.4-1206-jdbc42 @@ -108,9 +109,9 @@ - org.springframework - spring-orm - ${spring.version} + org.springframework.data + spring-data-jpa + ${spring-data-jpa.version} org.hibernate @@ -182,5 +183,14 @@ + + + org.springframework + spring-framework-bom + ${spring.version} + pom + import + + diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java new file mode 100644 index 000000000000..3069deb03861 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java @@ -0,0 +1,40 @@ +package ru.javawebinar.topjava.repository.datajpa; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.repository.UserMealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * GKislin + * 27.03.2015. + */ +@Repository +public class DataJpaUserMealRepositoryImpl implements UserMealRepository{ + @Override + public UserMeal save(UserMeal userMeal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public UserMeal 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 000000000000..139946a33baa --- /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 ProxyUserRepository proxy; + + @Override + public User save(User user) { + return proxy.save(user); + } + + @Override + public boolean delete(int id) { + return proxy.delete(id) != 0; + } + + @Override + public User get(int id) { + return proxy.findOne(id); + } + + @Override + public User getByEmail(String email) { + return proxy.getByEmail(email); + } + + @Override + public List getAll() { + return proxy.findAll(SORT_NAME_EMAIL); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java new file mode 100644 index 000000000000..092d847f2bb0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.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 + * 27.03.2015. + */ +@Transactional(readOnly = true) +public interface ProxyUserRepository 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/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 15606ab4a09c..3334aab49c1e 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -4,10 +4,14 @@ 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/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"> + + + + - @@ -73,4 +77,11 @@ p:username="${database.username}" p:password="${database.password}"/> + + + + + + + \ No newline at end of file From eee4da464ee7454cbf53de84a8a78a3ef6d33ba8 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 1 Jan 2016 14:37:44 +0300 Subject: [PATCH 044/108] 5_5 Spring Cache --- pom.xml | 11 +- .../topjava/service/UserService.java | 2 + .../topjava/service/UserServiceImpl.java | 14 + 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, 504 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 1f25e112ddf1..1856156d81ee 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,8 @@ 5.0.6.Final 5.1.3.Final + + 2.10.1 @@ -98,7 +100,7 @@ org.springframework - spring-context + spring-context-support ${spring.version} @@ -135,6 +137,13 @@ ${hibernate-validator.version} + + + net.sf.ehcache + ehcache + ${ehcache.version} + + junit diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 85c15e48f951..080b94b93c93 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 f83d039b7fbc..a5e150e635f0 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -1,12 +1,16 @@ 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 ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.util.exception.ExceptionUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; +import java.util.Collection; +import java.util.Objects; import java.util.List; /** @@ -19,10 +23,12 @@ public class UserServiceImpl implements UserService { @Autowired private UserRepository repository; + @CacheEvict(value = "users", allEntries = true) public User save(User user) { return repository.save(user); } + @CacheEvict(value = "users", allEntries = true) public void delete(int id) { ExceptionUtil.check(repository.delete(id), id); } @@ -32,14 +38,22 @@ public User get(int id) throws NotFoundException { } public User getByEmail(String email) throws NotFoundException { + Objects.requireNonNull(email, "Email must not be empty"); return ExceptionUtil.check(repository.getByEmail(email), "email=" + email); } + @Cacheable("users") public List getAll() { return repository.getAll(); } + @CacheEvict(value = "users", allEntries = true) public void update(User user) { 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 000000000000..8d420010d88d --- /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 000000000000..bfc19ddb1e7d --- /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 5ba07c0e0e8e..995ebf9a02b5 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 000000000000..784de2d79e20 --- /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 a9d87d6d6966..9534d4390148 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; @@ -33,6 +34,11 @@ public class UserServiceTest { @Autowired protected UserService service; + @Before + public void setUp() throws Exception { + service.evictCache(); + } + @Test public void testSave() throws Exception { TestUser tu = new TestUser(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); From e9c8f9a94631cc3fdab871aa0d959623b0f15044 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 12:54:05 +0300 Subject: [PATCH 045/108] 6_01 HW5 Data JPA --- .../DataJpaUserMealRepositoryImpl.java | 25 +++++++++++---- .../datajpa/ProxyUserMealRepository.java | 32 +++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java index 3069deb03861..520325efc76a 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.repository.datajpa; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; @@ -12,29 +14,40 @@ * 27.03.2015. */ @Repository -public class DataJpaUserMealRepositoryImpl implements UserMealRepository{ +public class DataJpaUserMealRepositoryImpl implements UserMealRepository { + @Autowired + private ProxyUserMealRepository proxy; + + @Autowired + private ProxyUserRepository userProxy; + @Override + @Transactional public UserMeal save(UserMeal userMeal, int userId) { - return null; + if (!userMeal.isNew() && get(userMeal.getId(), userId) == null) { + return null; + } + userMeal.setUser(userProxy.getOne(userId)); + return proxy.save(userMeal); } @Override public boolean delete(int id, int userId) { - return false; + return proxy.delete(id, userId) != 0; } @Override public UserMeal get(int id, int userId) { - return null; + return proxy.get(id, userId); } @Override public List getAll(int userId) { - return null; + return proxy.getAll(userId); } @Override public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { - return null; + return proxy.getBetween(startDate, endDate, userId); } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java new file mode 100644 index 000000000000..7ee6ca35cb91 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java @@ -0,0 +1,32 @@ +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.UserMeal; + +import java.time.LocalDateTime; +import java.util.List; + +@Transactional(readOnly = true) +public interface ProxyUserMealRepository extends JpaRepository { + + @Modifying + @Transactional + @Query("DELETE FROM UserMeal m WHERE m.id=:id AND m.user.id=:userId") + int delete(@Param("id") int id, @Param("userId") int userId); + + @Override + UserMeal save(UserMeal item); + + @Query("SELECT m FROM UserMeal m WHERE m.id=:id AND m.user.id=:userId") + UserMeal get(@Param("id") int id, @Param("userId") int userId); + + @Query("SELECT m FROM UserMeal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC") + List getAll(@Param("userId") int userId); + + @Query("SELECT m from UserMeal 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); +} From 3ed242820ad611be8493fc1b0682e80b2aa32fa1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 12:59:11 +0300 Subject: [PATCH 046/108] 6_02 HW5 Profiles --- .../java/ru/javawebinar/topjava/Profiles.java | 3 +- src/main/resources/spring/spring-db.xml | 83 +++++++++---------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java index 23e905ed9c20..a3f5266a97ec 100644 --- a/src/main/java/ru/javawebinar/topjava/Profiles.java +++ b/src/main/java/ru/javawebinar/topjava/Profiles.java @@ -9,5 +9,6 @@ public class Profiles { POSTGRES = "postgres", HSQLDB = "hsqldb", JDBC = "jdbc", - JPA = "jpa"; + JPA = "jpa", + DATAJPA = "datajpa"; } diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 3334aab49c1e..2fe5e369fb10 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -8,53 +8,11 @@ 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"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -79,9 +37,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 5a81a794ff83117dddfb02b4217f63a596631fd6 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 15:10:26 +0300 Subject: [PATCH 047/108] 6_03 HW5 tests and fix problems --- .../topjava/service/AbstractServiceTest.java | 50 +++++++++++++++++++ ....java => AbstractUserMealServiceTest.java} | 0 ...Test.java => AbstractUserServiceTest.java} | 0 .../datajpa/DataJpaUserMealServiceTest.java | 10 ++++ .../datajpa/DataJpaUserServiceTest.java | 10 ++++ .../service/jdbc/JdbcUserMealServiceTest.java | 10 ++++ .../service/jdbc/JdbcUserServiceTest.java | 10 ++++ .../service/jpa/JpaUserMealServiceTest.java | 10 ++++ .../service/jpa/JpaUserServiceTest.java | 10 ++++ src/test/resources/logback-test.xml | 3 ++ 10 files changed, 113 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java rename src/test/java/ru/javawebinar/topjava/service/{UserMealServiceTest.java => AbstractUserMealServiceTest.java} (100%) rename src/test/java/ru/javawebinar/topjava/service/{UserServiceTest.java => AbstractUserServiceTest.java} (100%) create mode 100644 src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.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/JdbcUserMealServiceTest.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/JpaUserMealServiceTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java 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 000000000000..f4466796d4b4 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -0,0 +1,50 @@ +package ru.javawebinar.topjava.service; + +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.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.LoggerWrapper; + +import java.util.concurrent.TimeUnit; + +import static ru.javawebinar.topjava.Profiles.POSTGRES; + +/** + * User: gkislin + */ +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringJUnit4ClassRunner.class) +@ActiveProfiles(POSTGRES) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +abstract public class AbstractServiceTest { + + private final LoggerWrapper LOG = LoggerWrapper.get(getClass()); + + + @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() { + private void logInfo(Description description, long nanos) { + LOG.info(String.format("+++ Test %s spent %d microseconds", + description.getMethodName(), TimeUnit.NANOSECONDS.toMicros(nanos))); + } + + @Override + protected void finished(long nanos, Description description) { + logInfo(description, nanos); + } + }; +} diff --git a/src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java similarity index 100% rename from src/test/java/ru/javawebinar/topjava/service/UserMealServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java similarity index 100% rename from src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java new file mode 100644 index 000000000000..b2252cc6cff4 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.datajpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.DATAJPA; + +@ActiveProfiles(DATAJPA) +public class DataJpaUserMealServiceTest extends AbstractUserMealServiceTest { +} \ No newline at end of file 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 000000000000..a0757e739339 --- /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/JdbcUserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserMealServiceTest.java new file mode 100644 index 000000000000..d310717c67a5 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jdbc; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.JDBC; + +@ActiveProfiles(JDBC) +public class JdbcUserMealServiceTest extends AbstractUserMealServiceTest { +} \ 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 000000000000..419f68ed1098 --- /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/JpaUserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserMealServiceTest.java new file mode 100644 index 000000000000..778235ed8ef0 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserMealServiceTest.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava.service.jpa; + +import org.springframework.test.context.ActiveProfiles; +import ru.javawebinar.topjava.service.AbstractUserMealServiceTest; + +import static ru.javawebinar.topjava.Profiles.JPA; + +@ActiveProfiles(JPA) +public class JpaUserMealServiceTest extends AbstractUserMealServiceTest { +} \ 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 000000000000..d1b3e4699785 --- /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 diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 80c38026d409..ac23a3f955ba 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -11,6 +11,9 @@ + + + From 5f48634877792f8c8bb51f076f1a02ec9c5ce737 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 16:15:43 +0300 Subject: [PATCH 048/108] 6_03 --- .../service/AbstractUserMealServiceTest.java | 40 +------------------ .../service/AbstractUserServiceTest.java | 18 +-------- 2 files changed, 3 insertions(+), 55 deletions(-) diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java index 7181dcd3d943..245f0190cda8 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserMealServiceTest.java @@ -1,58 +1,20 @@ package ru.javawebinar.topjava.service; -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.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.LoggerWrapper; import ru.javawebinar.topjava.MealTestData; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.UserMeal; 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.POSTGRES) -public class UserMealServiceTest { - private static final LoggerWrapper LOG = LoggerWrapper.get(UserMealServiceTest.class); - - @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() { - private void logInfo(Description description, long nanos) { - LOG.info(String.format("+++ Test %s spent %d microseconds", - description.getMethodName(), TimeUnit.NANOSECONDS.toMicros(nanos))); - } - - @Override - protected void finished(long nanos, Description description) { - logInfo(description, nanos); - } - }; +abstract public class AbstractUserMealServiceTest extends AbstractServiceTest { @Autowired protected UserMealService service; diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 9534d4390148..74bccf3707b1 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.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.UserTestData.*; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -22,14 +15,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.HSQLDB) -public class UserServiceTest { +abstract public class AbstractUserServiceTest extends AbstractServiceTest { @Autowired protected UserService service; @@ -38,7 +24,7 @@ public class UserServiceTest { public void setUp() throws Exception { service.evictCache(); } - + @Test public void testSave() throws Exception { TestUser tu = new TestUser(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); From 2ed790eaa31495bc271eadbca15cd6441ed6510c Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 17:18:03 +0300 Subject: [PATCH 049/108] 6_04 HW5 Optional Fix Servlet Main --- .../java/ru/javawebinar/topjava/web/MealServlet.java | 10 +++++++--- src/test/java/ru/javawebinar/topjava/SpringMain.java | 9 ++++++--- src/test/resources/logback-test.xml | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index e8fea2103130..f3a4f00e7e76 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,8 +1,8 @@ package ru.javawebinar.topjava.web; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.util.TimeUtil; import ru.javawebinar.topjava.web.meal.UserMealRestController; @@ -25,13 +25,17 @@ public class MealServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); - private ConfigurableApplicationContext springContext; + private ClassPathXmlApplicationContext springContext; private UserMealRestController 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 = new ClassPathXmlApplicationContext(); + springContext.getEnvironment().setActiveProfiles(Profiles.POSTGRES, Profiles.DATAJPA); +// springContext.setConfigLocations("spring/spring-app.xml", "spring/spring-db.xml"); + springContext.refresh(); mealController = springContext.getBean(UserMealRestController.class); } diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index f14692b18323..7f6917c78952 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.UserMealWithExceed; @@ -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.POSTGRES, Profiles.DATAJPA); + appCtx.load("spring/spring-app.xml", "spring/spring-db.xml"); + appCtx.refresh(); + System.out.println(Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); System.out.println(adminUserController.create(new User(1, "userName", "email", "password", Role.ROLE_ADMIN))); diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index ac23a3f955ba..ffe00fed77c7 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -14,7 +14,7 @@ - + From 9077daa5f8ba5914c7be9983bbf7295baff010c2 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 17:21:09 +0300 Subject: [PATCH 050/108] 6_05 HW5 Optional Fetch Join --- src/main/java/ru/javawebinar/topjava/model/User.java | 9 +++++++++ .../topjava/repository/UserMealRepository.java | 4 ++++ .../topjava/repository/UserRepository.java | 4 ++++ .../datajpa/DataJpaUserMealRepositoryImpl.java | 5 +++++ .../datajpa/DataJpaUserRepositoryImpl.java | 5 +++++ .../repository/datajpa/ProxyUserMealRepository.java | 3 +++ .../repository/datajpa/ProxyUserRepository.java | 3 +++ .../javawebinar/topjava/service/UserMealService.java | 2 ++ .../topjava/service/UserMealServiceImpl.java | 12 ++++++++++++ .../ru/javawebinar/topjava/service/UserService.java | 4 +++- .../javawebinar/topjava/service/UserServiceImpl.java | 5 +++++ .../service/datajpa/DataJpaUserMealServiceTest.java | 11 +++++++++++ .../service/datajpa/DataJpaUserServiceTest.java | 10 ++++++++++ 13 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 8fc29ca0a4ce..2a81b1b7cc73 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -10,6 +10,7 @@ import javax.validation.constraints.Digits; import java.util.Date; import java.util.EnumSet; +import java.util.List; import java.util.Set; /** @@ -55,6 +56,10 @@ public class User extends NamedEntity { @Digits(fraction = 0, integer = 4) protected int caloriesPerDay = UserMealsUtil.DEFAULT_CALORIES_PER_DAY; + @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "user") + @OrderBy("dateTime DESC") + protected List meals; + public User() { } @@ -119,6 +124,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/UserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java index 646b908602a4..afe485740dbe 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java @@ -24,4 +24,8 @@ public interface UserMealRepository { // ORDERED DATE, TIME Collection getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId); + + default UserMeal getWithUser(Integer id, Integer 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 24c1c45beca6..47ded4cec6b8 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/DataJpaUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java index 520325efc76a..5a633070298d 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserMealRepositoryImpl.java @@ -50,4 +50,9 @@ public List getAll(int userId) { public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { return proxy.getBetween(startDate, endDate, userId); } + + @Override + public UserMeal getWithUser(Integer id, Integer userId) { + return proxy.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 139946a33baa..422478684ec4 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 proxy.findAll(SORT_NAME_EMAIL); } + + @Override + public User getWithMeals(int id) { + return proxy.getWithMeals(id); + } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java index 7ee6ca35cb91..5139af76a0f5 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserMealRepository.java @@ -29,4 +29,7 @@ public interface ProxyUserMealRepository extends JpaRepository getBetween(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("userId") int userId); + + @Query("SELECT um FROM UserMeal um JOIN FETCH um.user WHERE um.id = ?1 and um.user.id = ?2") + UserMeal getWithUser(Integer id, Integer userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java index 092d847f2bb0..b3c497b46c31 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/datajpa/ProxyUserRepository.java @@ -34,4 +34,7 @@ public interface ProxyUserRepository extends JpaRepository { List findAll(Sort sort); User getByEmail(String email); + + @Query("SELECT u FROM User u LEFT JOIN FETCH u.meals WHERE u.id = ?1") + User getWithMeals(Integer id); } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java index 9e4ea6413504..9cfd2f128aeb 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java @@ -28,4 +28,6 @@ default Collection getBetweenDates(LocalDate startDate, LocalDate endD UserMeal update(UserMeal meal, int userId) throws NotFoundException; UserMeal save(UserMeal meal, int userId); + + UserMeal getWithUser(Integer id, Integer userId); } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java index fe2aa873de5c..746b0dd14543 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; import ru.javawebinar.topjava.util.exception.ExceptionUtil; @@ -48,4 +49,15 @@ public UserMeal update(UserMeal meal, int userId) { public UserMeal save(UserMeal meal, int userId) { return repository.save(meal, userId); } + +// @Autowired +// private UserRepository userRepository; + +// @Transactional + public UserMeal getWithUser(Integer id, Integer userId) { +// UserMeal meal = get(id, userId); +// meal.setUser(userRepository.get(userId)); +// return meal; + return repository.getWithUser(id, userId); + } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index 080b94b93c93..aa06ef3607f6 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 a5e150e635f0..2e090a8ed524 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -56,4 +56,9 @@ public void update(User user) { @Override public void evictCache() { } + + @Override + public User getWithMeals(int id) { + return repository.getWithMeals(id); + } } diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java index b2252cc6cff4..53531c437f28 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserMealServiceTest.java @@ -1,10 +1,21 @@ 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.UserMeal; import ru.javawebinar.topjava.service.AbstractUserMealServiceTest; +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 DataJpaUserMealServiceTest extends AbstractUserMealServiceTest { + @Test + public void testGetWithUser() throws Exception { + UserMeal adminMeal = service.getWithUser(ADMIN_MEAL_ID, ADMIN_ID); + MATCHER.assertEquals(ADMIN_MEAL, adminMeal); + UserTestData.MATCHER.assertEquals(UserTestData.ADMIN, adminMeal.getUser()); + } } \ No newline at end of file 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 a0757e739339..1b8097e5cf4b 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,20 @@ 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 static ru.javawebinar.topjava.Profiles.DATAJPA; +import static ru.javawebinar.topjava.UserTestData.*; @ActiveProfiles(DATAJPA) public class DataJpaUserServiceTest extends AbstractUserServiceTest { + @Test + public void testGetWithUser() throws Exception { + User user = service.getWithMeals(USER_ID); + MATCHER.assertEquals(USER, user); + MealTestData.MATCHER.assertCollectionEquals(MealTestData.USER_MEALS, user.getMeals()); + } } \ No newline at end of file From c6d8d85c7acc5ab11f7ff5ec13a34a37b54132e9 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 9 Jan 2016 17:40:30 +0300 Subject: [PATCH 051/108] 6_06 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 1856156d81ee..34c0756112a7 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,11 @@ hibernate-validator ${hibernate-validator.version} + + org.hibernate + hibernate-ehcache + ${hibernate.version} + diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 2a81b1b7cc73..39619698c5d8 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -2,6 +2,8 @@ import ru.javawebinar.topjava.util.UserMealsUtil; +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.NotEmpty; @@ -22,6 +24,7 @@ @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"), }) +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Entity @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "users_unique_email_idx")}) public class User extends NamedEntity { @@ -50,6 +53,7 @@ public class User extends NamedEntity { @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) @Column(name = "role") @ElementCollection(fetch = FetchType.EAGER) + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) protected Set roles; @Column(name = "calories_per_day", columnDefinition = "default 2000") 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 000000000000..75b3d82ba3cb --- /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 8d420010d88d..b0275e589a4a 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 2fe5e369fb10..24d9415ab9ba 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -58,6 +58,12 @@ + + + + + + @@ -73,6 +79,8 @@ + + diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 74bccf3707b1..649f58c964f4 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -7,6 +7,7 @@ import ru.javawebinar.topjava.UserTestData.*; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.JpaUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.util.Arrays; @@ -20,9 +21,13 @@ abstract public class AbstractUserServiceTest extends AbstractServiceTest { @Autowired protected UserService service; + @Autowired + protected JpaUtil jpaUtil; + @Before public void setUp() throws Exception { service.evictCache(); + jpaUtil.clear2ndLevelHibernateCache(); } @Test From 07e74e75325e35c180fb84340045e0c9ecb5c77a Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 11 Jan 2016 09:22:28 +0300 Subject: [PATCH 052/108] 6_7 Spring Web --- pom.xml | 18 +++++++++++++++--- .../javawebinar/topjava/web/MealServlet.java | 17 +++-------------- src/main/webapp/WEB-INF/web.xml | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 34c0756112a7..091a74b32b6f 100644 --- a/pom.xml +++ b/pom.xml @@ -85,12 +85,24 @@ - javax.servlet - servlet-api - 2.5 + org.apache.tomcat + tomcat-servlet-api + ${tomcat.version} provided + + org.springframework + spring-web + ${spring.version} + + + commons-logging + commons-logging + + + + javax.servlet jstl diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index f3a4f00e7e76..1faf69647ebe 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -1,8 +1,8 @@ package ru.javawebinar.topjava.web; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; import ru.javawebinar.topjava.LoggerWrapper; -import ru.javawebinar.topjava.Profiles; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.util.TimeUtil; import ru.javawebinar.topjava.web.meal.UserMealRestController; @@ -25,26 +25,15 @@ public class MealServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); - private ClassPathXmlApplicationContext springContext; private UserMealRestController 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 = new ClassPathXmlApplicationContext(); - springContext.getEnvironment().setActiveProfiles(Profiles.POSTGRES, Profiles.DATAJPA); -// springContext.setConfigLocations("spring/spring-app.xml", "spring/spring-db.xml"); - springContext.refresh(); + WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); mealController = springContext.getBean(UserMealRestController.class); } - @Override - public void destroy() { - springContext.close(); - super.destroy(); - } - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { request.setCharacterEncoding("UTF-8"); String action = request.getParameter("action"); diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 6306688d0998..e194d26d8fff 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -5,6 +5,22 @@ Topjava + + spring.profiles.default + postgres,datajpa + + + + contextConfigLocation + + classpath:spring/spring-app.xml + classpath:spring/spring-db.xml + + + + + org.springframework.web.context.ContextLoaderListener + userServlet ru.javawebinar.topjava.web.UserServlet From 5aed19ee71dfb3c90b5d5713a543250c75c37ecc Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 11 Jan 2016 10:45:15 +0300 Subject: [PATCH 053/108] 6_8 JSP JSTL --- .../javawebinar/topjava/web/UserServlet.java | 21 ++++++++-- src/main/resources/messages/app.properties | 2 + src/main/resources/messages/app_ru.properties | 2 + 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 | 22 ----------- src/main/webapp/index.jsp | 26 +++++++++++++ src/main/webapp/mealList.jsp | 2 +- src/main/webapp/userList.jsp | 38 ++++++++++++++++--- 11 files changed, 109 insertions(+), 32 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 40adae5b88cc..1e90d2c60d11 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.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.LoggerWrapper; +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; @@ -16,6 +20,15 @@ public class UserServlet extends HttpServlet { private static final LoggerWrapper LOG = LoggerWrapper.get(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")); LoggedUser.setId(userId); @@ -23,9 +36,9 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - LOG.debug("redirect to userList"); - -// request.getRequestDispatcher("/userList.jsp").forward(request, response); - response.sendRedirect("userList.jsp"); + LOG.debug("getAll"); + request.setAttribute("userList", adminController.getAll()); + request.getRequestDispatcher("/userList.jsp").forward(request, response); +// response.sendRedirect("userList.jsp"); } } diff --git a/src/main/resources/messages/app.properties b/src/main/resources/messages/app.properties new file mode 100644 index 000000000000..195b36b13dd3 --- /dev/null +++ b/src/main/resources/messages/app.properties @@ -0,0 +1,2 @@ +app.title=Calories management +app.footer=Project Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery application diff --git a/src/main/resources/messages/app_ru.properties b/src/main/resources/messages/app_ru.properties new file mode 100644 index 000000000000..461c4eeb279d --- /dev/null +++ b/src/main/resources/messages/app_ru.properties @@ -0,0 +1,2 @@ +app.title=\u041F\u043E\u0434\u0441\u0447\u0435\u0442 \u043A\u0430\u043B\u043E\u0440\u0438\u0439 +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 diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css index 0c9fc667371c..36305de387ee 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 000000000000..ad383f16f619 --- /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" %> + + +
diff --git a/src/main/webapp/fragments/footer.jsp b/src/main/webapp/fragments/footer.jsp new file mode 100644 index 000000000000..6df7790c26c3 --- /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" %> + +
+
diff --git a/src/main/webapp/fragments/headTag.jsp b/src/main/webapp/fragments/headTag.jsp new file mode 100644 index 000000000000..0238860698d7 --- /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"/> + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html deleted file mode 100644 index 4136c15ef689..000000000000 --- a/src/main/webapp/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Top Java. - - -

Проект "Top Java"

-
-
- Login as: - - - - - diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 000000000000..6177319f7252 --- /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" %> + + + + + + + +
+
+ Login as: + + + +
+ + + diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp index 2887bbfb5820..f4441cd469d3 100644 --- a/src/main/webapp/mealList.jsp +++ b/src/main/webapp/mealList.jsp @@ -9,7 +9,7 @@
-

Home

+

Home

Meal list

diff --git a/src/main/webapp/userList.jsp b/src/main/webapp/userList.jsp index eae4726d4400..215e82937d98 100644 --- a/src/main/webapp/userList.jsp +++ b/src/main/webapp/userList.jsp @@ -1,10 +1,38 @@ <%@ 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

+ + +
+

+ + + + + + + + + + + + + + + + + + + +
NameEmailRolesActiveRegistered
${user.email}${user.roles}<%=user.isEnabled()%> +
+
+ From 3f1ec911af02ec1b9036da42811681d8122ec595 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 11 Jan 2016 21:58:49 +0300 Subject: [PATCH 054/108] 6_09 Tomcat Pool --- pom.xml | 22 ++++++++++++++++++++++ src/main/resources/db/hsqldb.properties | 1 - src/main/resources/db/tomcat.properties | 7 +++++++ src/main/resources/spring/spring-db.xml | 19 ++++++++++++++----- 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/db/tomcat.properties diff --git a/pom.xml b/pom.xml index 091a74b32b6f..7a4af4511e22 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,28 @@ ${java.version} + + + org.apache.tomcat.maven + tomcat7-maven-plugin + 2.2 + + src/main/resources/tomcat/context.xml + + UTF-8 + tomcat,datajpa + + org.apache.coyote.http11.Http11NioProtocol + + + + org.postgresql + postgresql + ${postgres.version} + + + + diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties index 560579e13f71..a9449359016a 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/tomcat.properties b/src/main/resources/db/tomcat.properties new file mode 100644 index 000000000000..f84fd2bb0c9e --- /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 diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 24d9415ab9ba..97c9d7021d32 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -4,9 +4,13 @@ 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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> + 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/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> @@ -19,7 +23,7 @@ @@ -30,12 +34,17 @@ + + + + + From 061b349bf9cb87d89081efd8f89d80c786e0d4b7 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 11 Jan 2016 22:14:30 +0300 Subject: [PATCH 055/108] 6_10 Spring WebMVC --- pom.xml | 2 +- .../topjava/web/RootController.java | 39 +++++++++++++++++++ src/main/resources/spring/spring-app.xml | 3 -- src/main/resources/spring/spring-mvc.xml | 18 +++++++++ .../jsp}/fragments/bodyHeader.jsp | 0 .../{ => WEB-INF/jsp}/fragments/footer.jsp | 0 .../{ => WEB-INF/jsp}/fragments/headTag.jsp | 2 +- src/main/webapp/{ => WEB-INF/jsp}/index.jsp | 0 .../webapp/{ => WEB-INF/jsp}/mealEdit.jsp | 0 .../webapp/{ => WEB-INF/jsp}/mealList.jsp | 2 +- .../webapp/{ => WEB-INF/jsp}/userList.jsp | 0 src/main/webapp/WEB-INF/web.xml | 17 ++++++++ src/main/webapp/{ => resources}/css/style.css | 2 +- 13 files changed, 78 insertions(+), 7 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 (100%) 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}/mealEdit.jsp (100%) rename src/main/webapp/{ => WEB-INF/jsp}/mealList.jsp (99%) rename src/main/webapp/{ => WEB-INF/jsp}/userList.jsp (100%) rename src/main/webapp/{ => resources}/css/style.css (99%) diff --git a/pom.xml b/pom.xml index 7a4af4511e22..efe85c07d7be 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,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 000000000000..2134910b0078 --- /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.LoggedUser; +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 userList(Model model) { + model.addAttribute("userList", service.getAll()); + return "userList"; + } + + @RequestMapping(value = "/users", method = RequestMethod.POST) + public String setUser(HttpServletRequest request) { + int userId = Integer.valueOf(request.getParameter("userId")); + LoggedUser.setId(userId); + return "redirect:meals"; + } +} diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 995ebf9a02b5..687065f24bc9 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 000000000000..7c71d5af5939 --- /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 100% rename from src/main/webapp/fragments/bodyHeader.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp 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 0238860698d7..4fb114c10428 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"/> - + 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/mealEdit.jsp b/src/main/webapp/WEB-INF/jsp/mealEdit.jsp similarity index 100% rename from src/main/webapp/mealEdit.jsp rename to src/main/webapp/WEB-INF/jsp/mealEdit.jsp diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp similarity index 99% rename from src/main/webapp/mealList.jsp rename to src/main/webapp/WEB-INF/jsp/mealList.jsp index f4441cd469d3..eedb291996a9 100644 --- a/src/main/webapp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -60,4 +60,4 @@
- \ No newline at end of file + diff --git a/src/main/webapp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp similarity index 100% rename from src/main/webapp/userList.jsp rename to src/main/webapp/WEB-INF/jsp/userList.jsp diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index e194d26d8fff..330fb5bba850 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 99% rename from src/main/webapp/css/style.css rename to src/main/webapp/resources/css/style.css index 36305de387ee..d4807fb38efb 100644 --- a/src/main/webapp/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -29,4 +29,4 @@ header, footer { font-size: 20px; padding: 5px 20px; margin: 6px 0; -} \ No newline at end of file +} From 1175647ab25d23f6fa65f85c4727543e811681e1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 11 Jan 2016 22:20:31 +0300 Subject: [PATCH 056/108] 6_11 Spring i18n --- .../messages/app.properties | 0 config/messages/app_ru.properties | 2 ++ src/main/resources/messages/app_ru.properties | 2 -- 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/userList.jsp | 2 -- 9 files changed, 20 insertions(+), 9 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 000000000000..5ba0c2f2400d --- /dev/null +++ b/config/messages/app_ru.properties @@ -0,0 +1,2 @@ +app.title= +app.footer= Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery \ 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 461c4eeb279d..000000000000 --- a/src/main/resources/messages/app_ru.properties +++ /dev/null @@ -1,2 +0,0 @@ -app.title=\u041F\u043E\u0434\u0441\u0447\u0435\u0442 \u043A\u0430\u043B\u043E\u0440\u0438\u0439 -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 diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 7c71d5af5939..12ac233daa15 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 ad383f16f619..833a84d0b2a4 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" %> -
diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index 6df7790c26c3..997a1677296c 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" %> -
diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 4fb114c10428..fc2b58f65786 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 6177319f7252..74dd0c49a982 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/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 215e82937d98..2419b79b3167 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.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" %> - - From a497a757a968cca76119828724710ef942bfe6fd Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 17:16:59 +0300 Subject: [PATCH 057/108] 7_01_HW6 Fix Tests --- .../ru/javawebinar/topjava/SpringMain.java | 2 +- .../service/AbstractJpaUserServiceTest.java | 21 +++++++++++++++++++ .../service/AbstractUserServiceTest.java | 5 ----- .../datajpa/DataJpaUserServiceTest.java | 4 ++-- .../service/jpa/JpaUserServiceTest.java | 4 ++-- .../topjava/web/AdminInMemoryTest.java | 2 +- .../topjava/web/SpringAdminInMemoryTest.java | 2 +- 7 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/service/AbstractJpaUserServiceTest.java diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index 7f6917c78952..e4773b4b3b65 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -22,7 +22,7 @@ public static void main(String[] args) { // java 7 Automatic resource management try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { appCtx.getEnvironment().setActiveProfiles(Profiles.POSTGRES, Profiles.DATAJPA); - appCtx.load("spring/spring-app.xml", "spring/spring-db.xml"); + appCtx.load("spring/spring-app.xml", "spring/spring-db.xml", "spring/spring-mvc.xml"); appCtx.refresh(); System.out.println(Arrays.toString(appCtx.getBeanDefinitionNames())); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractJpaUserServiceTest.java new file mode 100644 index 000000000000..eb8f8e9ea70e --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractJpaUserServiceTest.java @@ -0,0 +1,21 @@ +package ru.javawebinar.topjava.service; + +import org.junit.Before; +import org.springframework.beans.factory.annotation.Autowired; +import ru.javawebinar.topjava.repository.JpaUtil; + +/** + * GKislin + * 07.04.2015. + */ +abstract public class AbstractJpaUserServiceTest extends AbstractUserServiceTest { + @SuppressWarnings("SpringJavaAutowiringInspection") + @Autowired + private JpaUtil jpaUtil; + + @Before + public void setUp() throws Exception { + super.setUp(); + jpaUtil.clear2ndLevelHibernateCache(); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java index 649f58c964f4..74bccf3707b1 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -7,7 +7,6 @@ import ru.javawebinar.topjava.UserTestData.*; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.JpaUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; import java.util.Arrays; @@ -21,13 +20,9 @@ abstract public class AbstractUserServiceTest extends AbstractServiceTest { @Autowired protected UserService service; - @Autowired - protected JpaUtil jpaUtil; - @Before public void setUp() throws Exception { service.evictCache(); - jpaUtil.clear2ndLevelHibernateCache(); } @Test diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java index 1b8097e5cf4b..8b11aebb6df2 100644 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java @@ -4,13 +4,13 @@ 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.service.AbstractJpaUserServiceTest; import static ru.javawebinar.topjava.Profiles.DATAJPA; import static ru.javawebinar.topjava.UserTestData.*; @ActiveProfiles(DATAJPA) -public class DataJpaUserServiceTest extends AbstractUserServiceTest { +public class DataJpaUserServiceTest extends AbstractJpaUserServiceTest { @Test public void testGetWithUser() throws Exception { User user = service.getWithMeals(USER_ID); diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java index d1b3e4699785..ffb1fea03c22 100644 --- a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java @@ -1,10 +1,10 @@ package ru.javawebinar.topjava.service.jpa; import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.service.AbstractUserServiceTest; +import ru.javawebinar.topjava.service.AbstractJpaUserServiceTest; import static ru.javawebinar.topjava.Profiles.JPA; @ActiveProfiles(JPA) -public class JpaUserServiceTest extends AbstractUserServiceTest { +public class JpaUserServiceTest extends AbstractJpaUserServiceTest { } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java index 7c3c81e3f292..24b3e6f47457 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java @@ -21,7 +21,7 @@ public class AdminInMemoryTest { @BeforeClass public static void beforeClass() { - appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml","spring/mock.xml"); + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml","spring/spring-mvc.xml", "spring/mock.xml"); System.out.println("\n" + Arrays.toString(appCtx.getBeanDefinitionNames()) + "\n"); controller = appCtx.getBean(AdminRestController.class); } diff --git a/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java index 62aa9e77acc6..8641f65db4c8 100644 --- a/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/SpringAdminInMemoryTest.java @@ -22,7 +22,7 @@ * GKislin * 13.03.2015. */ -@ContextConfiguration({"classpath:spring/spring-app.xml","classpath:spring/mock.xml"}) +@ContextConfiguration({"classpath:spring/spring-app.xml","classpath:spring/spring-mvc.xml", "classpath:spring/mock.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class SpringAdminInMemoryTest { From ae329dfeb7716ef76087e5f057f9188d2dad3218 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 17:36:28 +0300 Subject: [PATCH 058/108] 7_02_HW6 JSP i18 --- config/messages/app.properties | 7 +++++++ config/messages/app_ru.properties | 11 +++++++++-- .../javawebinar/topjava/web/RootController.java | 16 ++++++++++++++-- src/main/resources/logback.xml | 1 + src/main/webapp/WEB-INF/jsp/index.jsp | 8 ++++---- src/main/webapp/WEB-INF/jsp/mealEdit.jsp | 11 +++++------ src/main/webapp/WEB-INF/jsp/mealList.jsp | 12 ++++++------ src/main/webapp/WEB-INF/jsp/userList.jsp | 1 + 8 files changed, 47 insertions(+), 20 deletions(-) diff --git a/config/messages/app.properties b/config/messages/app.properties index 195b36b13dd3..8db5a6b72d32 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -1,2 +1,9 @@ app.title=Calories management app.footer=Project Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery application +app.login=Login as + +users.title=User list +users.edit=Edit user +meals.title=Meals list +meals.edit=Edit meal +common.select=Выбрать \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 5ba0c2f2400d..71fc8a65f02c 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -1,2 +1,9 @@ -app.title= -app.footer= Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery \ No newline at end of file +app.title=Подсчет калорий +app.footer=Приложение по вебинару Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery +app.login=Зайти как + +users.title=Список пользователей +users.edit=Радактировать пользователя +meals.title=Список еды +meals.edit=Редактировать еду +common.select=Select \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 2134910b0078..d54f6199c1b5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -6,7 +6,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.service.UserMealService; import ru.javawebinar.topjava.service.UserService; +import ru.javawebinar.topjava.util.UserMealsUtil; import javax.servlet.http.HttpServletRequest; @@ -17,7 +19,10 @@ @Controller public class RootController { @Autowired - private UserService service; + private UserService userService; + + @Autowired + private UserMealService mealService; @RequestMapping(value = "/", method = RequestMethod.GET) public String root() { @@ -26,10 +31,17 @@ public String root() { @RequestMapping(value = "/users", method = RequestMethod.GET) public String userList(Model model) { - model.addAttribute("userList", service.getAll()); + model.addAttribute("userList", userService.getAll()); return "userList"; } + @RequestMapping(value = "/meals", method = RequestMethod.GET) + public String mealList(Model model) { + model.addAttribute("mealList", + UserMealsUtil.getWithExceeded(mealService.getAll(LoggedUser.id()), LoggedUser.getCaloriesPerDay())); + return "mealList"; + } + @RequestMapping(value = "/users", method = RequestMethod.POST) public String setUser(HttpServletRequest request) { int userId = Integer.valueOf(request.getParameter("userId")); diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 4ee2916e57d1..519084a09ab2 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -24,6 +24,7 @@ + diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index 74dd0c49a982..0208cf97703b 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -8,15 +8,15 @@
- Login as: - +
diff --git a/src/main/webapp/WEB-INF/jsp/mealEdit.jsp b/src/main/webapp/WEB-INF/jsp/mealEdit.jsp index d71aeb3a6bbd..3df0c26c6dc9 100644 --- a/src/main/webapp/WEB-INF/jsp/mealEdit.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealEdit.jsp @@ -1,15 +1,13 @@ <%@ 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" %> - - Meal - - + +

Home

-

Edit meal

+


@@ -30,5 +28,6 @@
+ diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index eedb291996a9..377aefbcccf2 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -3,14 +3,13 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - Meal list - - + +
-

Home

-

Meal list

+ <%--http://stackoverflow.com/questions/10327390/how-should-i-get-root-folder-path-in-jsp-page--%> +

Home

+

From Date:
@@ -59,5 +58,6 @@
+ diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 2419b79b3167..e3b6ed640305 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -8,6 +8,7 @@
+

From 9e96f4b51cd9632fde30b5d431fc68fa3f17828c Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:11:00 +0300 Subject: [PATCH 059/108] 7_03_HW6 Meals --- .../javawebinar/topjava/web/MealServlet.java | 95 ------------------- .../topjava/web/RootController.java | 8 -- .../javawebinar/topjava/web/UserServlet.java | 44 --------- .../web/meal/AbstractUserMealController.java | 66 +++++++++++++ .../web/meal/JspUserMealController.java | 84 ++++++++++++++++ .../web/meal/UserMealRestController.java | 55 +---------- src/main/webapp/WEB-INF/jsp/mealList.jsp | 8 +- src/main/webapp/WEB-INF/web.xml | 22 ----- 8 files changed, 155 insertions(+), 227 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/web/MealServlet.java delete mode 100644 src/main/java/ru/javawebinar/topjava/web/UserServlet.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/AbstractUserMealController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java deleted file mode 100644 index 1faf69647ebe..000000000000 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ /dev/null @@ -1,95 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; -import ru.javawebinar.topjava.LoggerWrapper; -import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.util.TimeUtil; -import ru.javawebinar.topjava.web.meal.UserMealRestController; - -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.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.Objects; - -/** - * User: gkislin - * Date: 19.08.2014 - */ -public class MealServlet extends HttpServlet { - private static final LoggerWrapper LOG = LoggerWrapper.get(MealServlet.class); - - private UserMealRestController mealController; - - @Override - public void init(ServletConfig config) throws ServletException { - super.init(config); - WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); - mealController = springContext.getBean(UserMealRestController.class); - } - - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { - request.setCharacterEncoding("UTF-8"); - String action = request.getParameter("action"); - if (action == null) { - String id = request.getParameter("id"); - UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id), - LocalDateTime.parse(request.getParameter("dateTime")), - request.getParameter("description"), - Integer.valueOf(request.getParameter("calories"))); - if (userMeal.isNew()) { - LOG.info("Create {}", userMeal); - mealController.create(userMeal); - } else { - LOG.info("Update {}", userMeal); - mealController.update(userMeal); - } - response.sendRedirect("meals"); - } else if (action.equals("filter")) { - LocalDate startDate = TimeUtil.parseLocalDate(resetParam("startDate", request), TimeUtil.MIN_DATE); - LocalDate endDate = TimeUtil.parseLocalDate(resetParam("endDate", request), TimeUtil.MAX_DATE); - LocalTime startTime = TimeUtil.parseLocalTime(resetParam("startTime", request), LocalTime.MIN); - LocalTime endTime = TimeUtil.parseLocalTime(resetParam("endTime", request), LocalTime.MAX); - request.setAttribute("mealList", mealController.getBetween(startDate, startTime, endDate, endTime)); - request.getRequestDispatcher("/mealList.jsp").forward(request, response); - } - } - - private String resetParam(String param, HttpServletRequest request) { - String value = request.getParameter(param); - request.setAttribute(param, value); - return value; - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String action = request.getParameter("action"); - - if (action == null) { - LOG.info("getAll"); - request.setAttribute("mealList", mealController.getAll()); - request.getRequestDispatcher("/mealList.jsp").forward(request, response); - } else if (action.equals("delete")) { - int id = getId(request); - LOG.info("Delete {}", id); - mealController.delete(id); - response.sendRedirect("meals"); - } else { - final UserMeal meal = action.equals("create") ? - new UserMeal(LocalDateTime.now(), "", 1000) : - mealController.get(getId(request)); - request.setAttribute("meal", meal); - request.getRequestDispatcher("mealEdit.jsp").forward(request, response); - } - } - - private int getId(HttpServletRequest request) { - String paramId = Objects.requireNonNull(request.getParameter("id")); - return Integer.valueOf(paramId); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index d54f6199c1b5..0046617052f6 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -8,7 +8,6 @@ import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.service.UserMealService; import ru.javawebinar.topjava.service.UserService; -import ru.javawebinar.topjava.util.UserMealsUtil; import javax.servlet.http.HttpServletRequest; @@ -35,13 +34,6 @@ public String userList(Model model) { return "userList"; } - @RequestMapping(value = "/meals", method = RequestMethod.GET) - public String mealList(Model model) { - model.addAttribute("mealList", - UserMealsUtil.getWithExceeded(mealService.getAll(LoggedUser.id()), LoggedUser.getCaloriesPerDay())); - return "mealList"; - } - @RequestMapping(value = "/users", method = RequestMethod.POST) public String setUser(HttpServletRequest request) { int userId = Integer.valueOf(request.getParameter("userId")); diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java deleted file mode 100644 index 1e90d2c60d11..000000000000 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; -import ru.javawebinar.topjava.LoggedUser; -import ru.javawebinar.topjava.LoggerWrapper; -import ru.javawebinar.topjava.web.user.AdminRestController; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * User: gkislin - * Date: 19.08.2014 - */ -public class UserServlet extends HttpServlet { - private static final LoggerWrapper LOG = LoggerWrapper.get(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")); - LoggedUser.setId(userId); - response.sendRedirect("meals"); - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - LOG.debug("getAll"); - request.setAttribute("userList", adminController.getAll()); - request.getRequestDispatcher("/userList.jsp").forward(request, response); -// response.sendRedirect("userList.jsp"); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractUserMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractUserMealController.java new file mode 100644 index 000000000000..ea6bd2a836a0 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractUserMealController.java @@ -0,0 +1,66 @@ +package ru.javawebinar.topjava.web.meal; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.service.UserMealService; +import ru.javawebinar.topjava.to.UserMealWithExceed; +import ru.javawebinar.topjava.util.UserMealsUtil; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +/** + * GKislin + * 06.03.2015. + */ +@Controller +public class AbstractUserMealController { + protected static final LoggerWrapper LOG = LoggerWrapper.get(AbstractUserMealController.class); + + @Autowired + protected UserMealService service; + + public UserMeal get(int id) { + int userId = LoggedUser.id(); + LOG.info("get meal {} for User {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = LoggedUser.id(); + LOG.info("delete meal {} for User {}", id, userId); + service.delete(id, userId); + } + + public List getAll() { + int userId = LoggedUser.id(); + LOG.info("getAll for User {}", userId); + return UserMealsUtil.getWithExceeded(service.getAll(userId), LoggedUser.getCaloriesPerDay()); + } + + public void update(UserMeal meal, int id) { + meal.setId(id); + int userId = LoggedUser.id(); + LOG.info("update {} for User {}", meal, userId); + service.update(meal, userId); + } + + public UserMeal create(UserMeal meal) { + meal.setId(null); + int userId = LoggedUser.id(); + LOG.info("create {} for User {}", meal, userId); + return service.save(meal, userId); + } + + public List getBetween(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) { + int userId = LoggedUser.id(); + LOG.info("getBetween dates {} - {} for time {} - {} for User {}", startDate, endDate, startTime, endTime, userId); + return UserMealsUtil.getFilteredWithExceeded( + service.getBetweenDates(startDate, endDate, userId), startTime, endTime, LoggedUser.getCaloriesPerDay() + ); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java new file mode 100644 index 000000000000..912b191e773e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java @@ -0,0 +1,84 @@ +package ru.javawebinar.topjava.web.meal; + +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.model.UserMeal; +import ru.javawebinar.topjava.util.TimeUtil; + +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Objects; + +/** + * GKislin + * 06.03.2015. + */ +@Controller +@RequestMapping(value = "/meals") +public class JspUserMealController extends AbstractUserMealController { + + @RequestMapping(method = RequestMethod.GET) + public String mealList(Model model) { + model.addAttribute("mealList", super.getAll()); + return "mealList"; + } + + @RequestMapping(value = "/delete", method = RequestMethod.GET) + public String delete(HttpServletRequest request) { + super.delete(getId(request)); + return "redirect:meals"; + } + + @RequestMapping(value = "/update", method = RequestMethod.GET) + public String editForUpdate(HttpServletRequest request, Model model) { + model.addAttribute("meal", super.get(getId(request))); + return "mealEdit"; + } + + @RequestMapping(value = "/create", method = RequestMethod.GET) + public String editForCreate(Model model) { + model.addAttribute("meal", new UserMeal(LocalDateTime.now(), "", 1000)); + return "mealEdit"; + } + + @RequestMapping(method = RequestMethod.POST) + public String updateOrCreate(HttpServletRequest request) { + String id = request.getParameter("id"); + UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id), + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); + + if (userMeal.isNew()) { + super.create(userMeal); + } else { + super.update(userMeal, userMeal.getId()); + } + return "redirect:meals"; + } + + @RequestMapping(value = "/filter", method = RequestMethod.POST) + public String getBetween(HttpServletRequest request, Model model) { + LocalDate startDate = TimeUtil.parseLocalDate(resetParam("startDate", request), TimeUtil.MIN_DATE); + LocalDate endDate = TimeUtil.parseLocalDate(resetParam("endDate", request), TimeUtil.MAX_DATE); + LocalTime startTime = TimeUtil.parseLocalTime(resetParam("startTime", request), LocalTime.MIN); + LocalTime endTime = TimeUtil.parseLocalTime(resetParam("endTime", request), LocalTime.MAX); + model.addAttribute("mealList", super.getBetween(startDate, startTime, endDate, endTime)); + return "mealList"; + } + + private String resetParam(String param, HttpServletRequest request) { + String value = request.getParameter(param); + request.setAttribute(param, value); + return value; + } + + 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/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index 60a843d9ecd2..0dcb0a604b9f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -1,64 +1,11 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import ru.javawebinar.topjava.LoggedUser; -import ru.javawebinar.topjava.LoggerWrapper; -import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.to.UserMealWithExceed; -import ru.javawebinar.topjava.service.UserMealService; -import ru.javawebinar.topjava.util.UserMealsUtil; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; /** * GKislin * 06.03.2015. */ @Controller -public class UserMealRestController { - private static final LoggerWrapper LOG = LoggerWrapper.get(UserMealRestController.class); - - @Autowired - private UserMealService service; - - public UserMeal get(int id) { - int userId = LoggedUser.id(); - LOG.info("get meal {} for User {}", id, userId); - return service.get(id, userId); - } - - public void delete(int id) { - int userId = LoggedUser.id(); - LOG.info("delete meal {} for User {}", id, userId); - service.delete(id, userId); - } - - public List getAll() { - int userId = LoggedUser.id(); - LOG.info("getAll for User {}", userId); - return UserMealsUtil.getWithExceeded(service.getAll(userId), LoggedUser.getCaloriesPerDay()); - } - - public void update(UserMeal meal) { - int userId = LoggedUser.id(); - LOG.info("update {} for User {}", meal, userId); - service.update(meal, userId); - } - - public UserMeal create(UserMeal meal) { - int userId = LoggedUser.id(); - LOG.info("create {} for User {}", meal, userId); - return service.save(meal, userId); - } - - public List getBetween(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) { - int userId = LoggedUser.id(); - LOG.info("getBetween dates {} - {} for time {} - {} for User {}", startDate, endDate, startTime, endTime, userId); - return UserMealsUtil.getFilteredWithExceeded( - service.getBetweenDates(startDate, endDate, userId), startTime, endTime, LoggedUser.getCaloriesPerDay() - ); - } +public class UserMealRestController extends AbstractUserMealController { } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index 377aefbcccf2..ae3b901214af 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -10,7 +10,7 @@ <%--http://stackoverflow.com/questions/10327390/how-should-i-get-root-folder-path-in-jsp-page--%>

Home

- +
From Date:
@@ -30,7 +30,7 @@
- Add Meal + Add Meal
@@ -52,8 +52,8 @@ - - + +
${meal.description} ${meal.calories}UpdateDeleteUpdateDelete
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 330fb5bba850..accc39fc3d22 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -36,26 +36,4 @@ / - - From ee46baaba218ea1a1fe621aac15846fe91bf2639 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:14:40 +0300 Subject: [PATCH 060/108] 7_04_HW6 Fix Relative URL UTF8 --- .../topjava/web/meal/JspUserMealController.java | 4 ++-- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 3 +++ src/main/webapp/WEB-INF/web.xml | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java index 912b191e773e..d9d6af7912d5 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java @@ -30,7 +30,7 @@ public String mealList(Model model) { @RequestMapping(value = "/delete", method = RequestMethod.GET) public String delete(HttpServletRequest request) { super.delete(getId(request)); - return "redirect:meals"; + return "redirect:/meals"; } @RequestMapping(value = "/update", method = RequestMethod.GET) @@ -58,7 +58,7 @@ public String updateOrCreate(HttpServletRequest request) { } else { super.update(userMeal, userMeal.getId()); } - return "redirect:meals"; + return "redirect:/meals"; } @RequestMapping(value = "/filter", method = RequestMethod.POST) diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index fc2b58f65786..90f86d23e672 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -1,9 +1,12 @@ <%@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" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <fmt:message key="app.title"/> + ${pageContext.request.requestURL} + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index accc39fc3d22..abc0d013bcfc 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -36,4 +36,21 @@ / + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + encodingFilter + /* + + From b19b652187900be7caea319b7ab14225b8f73635 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:22:35 +0300 Subject: [PATCH 061/108] 7_05_HW6 Optional Add Role --- src/main/java/ru/javawebinar/topjava/model/User.java | 2 +- src/main/resources/db/populateDB.sql | 3 ++- src/test/java/ru/javawebinar/topjava/UserTestData.java | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 39619698c5d8..4078b536147b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -22,7 +22,7 @@ @NamedQueries({ @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), - @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email"), + @NamedQuery(name = User.ALL_SORTED, query = "SELECT DISTINCT(u) FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email"), }) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Entity diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index 0fbffd0dfb58..cc816500ce36 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -11,7 +11,8 @@ VALUES ('Admin', 'admin@gmail.com', 'admin'); INSERT INTO user_roles (role, user_id) VALUES ('ROLE_USER', 100000), - ('ROLE_ADMIN', 100001); + ('ROLE_ADMIN', 100001), + ('ROLE_USER', 100001); INSERT INTO meals (date_time, description, calories, user_id) VALUES ('2015-05-30 10:00:00', 'Завтрак', 500, 100000), diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index c6855207f44e..06823a8151d0 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -20,7 +20,7 @@ public class UserTestData { 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 User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN, Role.ROLE_USER); public static final ModelMatcher MATCHER = new ModelMatcher<>(u -> ((u instanceof TestUser) ? (TestUser) u : new TestUser(u))); @@ -69,7 +69,8 @@ public boolean equals(Object o) { && Objects.equals(this.name, that.name) && Objects.equals(this.email, that.email) && Objects.equals(this.caloriesPerDay, that.caloriesPerDay) - && Objects.equals(this.enabled, that.enabled); + && Objects.equals(this.enabled, that.enabled) + && Objects.equals(this.roles, that.roles); } } } From 8ea8114c16376f7ac5595100fcb3450636c8adc1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:30:15 +0300 Subject: [PATCH 062/108] 7_06_HW6 Optional JDBC Test --- .../ru/javawebinar/topjava/model/User.java | 7 +- .../jdbc/JdbcUserRepositoryImpl.java | 74 ++++++++++++++++++- src/main/resources/spring/spring-db.xml | 9 ++- 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 4078b536147b..41450e9d1c4e 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,12 +1,11 @@ package ru.javawebinar.topjava.model; -import ru.javawebinar.topjava.util.UserMealsUtil; - 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.NotEmpty; +import ru.javawebinar.topjava.util.UserMealsUtil; import javax.persistence.*; import javax.validation.constraints.Digits; @@ -132,6 +131,10 @@ public List getMeals() { return meals; } + public void setRoles(List roles) { + this.roles = EnumSet.copyOf(roles); + } + @Override public String toString() { return "User (" + 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 5646bad071ed..4f93559318de 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -2,17 +2,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.support.DataAccessUtils; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; 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.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * User: gkislin @@ -53,7 +61,10 @@ public User save(User user) { if (user.isNew()) { Number newKey = insertUser.executeAndReturnKey(map); user.setId(newKey.intValue()); + insertRoles(user); } else { + deleteRoles(user); + insertRoles(user); namedParameterJdbcTemplate.update( "UPDATE users SET name=:name, email=:email, password=:password, " + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", map); @@ -69,16 +80,73 @@ public boolean delete(int id) { @Override public User get(int id) { List users = jdbcTemplate.query("SELECT * FROM users WHERE id=?", ROW_MAPPER, id); - return DataAccessUtils.singleResult(users); + return setRoles(DataAccessUtils.singleResult(users)); } @Override public User getByEmail(String email) { - return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); + return setRoles(jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email)); } @Override public List getAll() { - return jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + List users = jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + + class UserRole { + public UserRole(Role role, int userId) { + this.role = role; + this.userId = userId; + } + + final private Role role; + final private int userId; + + public Role getRole() { + return role; + } + + public int getUserId() { + return userId; + } + } + + Map> userRoles = jdbcTemplate.query("SELECT role, user_id FROM user_roles", + (rs, rowNum) -> new UserRole(Role.valueOf(rs.getString("role")), rs.getInt("user_id"))) + .stream().collect(Collectors.groupingBy(UserRole::getUserId)); + + users.forEach(u -> u.setRoles(userRoles.get(u.getId()).stream().map(UserRole::getRole).collect(Collectors.toList()))); + return users; + } + + private void insertRoles(User u) { + Set roles = u.getRoles(); + Iterator iterator = roles.iterator(); + + jdbcTemplate.batchUpdate("INSERT INTO user_roles (user_id, role) VALUES (?, ?)", + new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + ps.setInt(1, u.getId()); + ps.setString(2, iterator.next().name()); + } + + @Override + public int getBatchSize() { + return roles.size(); + } + }); + } + + private void deleteRoles(User u) { + jdbcTemplate.update("DELETE FROM user_roles WHERE user_id=?", u.getId()); + } + + private User setRoles(User u) { + if (u != null) { + List roles = jdbcTemplate.query("SELECT role FROM user_roles WHERE user_id=?", + (rs, rowNum) -> Role.valueOf(rs.getString("role")), u.getId()); + u.setRoles(roles); + } + return u; } } diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 97c9d7021d32..15c2bfe30b56 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -17,6 +17,8 @@ + + @@ -54,6 +56,11 @@ + + + + @@ -83,8 +90,6 @@ - - From 69f28ebae9df7dfb4d84f56c20a41ae2d63ade3f Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:36:14 +0300 Subject: [PATCH 063/108] 7_07 Generate DDL --- src/main/resources/spring/spring-db.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 15c2bfe30b56..e50398fc6bbd 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -80,6 +80,9 @@ + + + From 8979bd416d399a92ad2a175c2b405d7b393be6d5 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 15 Jan 2016 18:59:09 +0300 Subject: [PATCH 064/108] 7_08 Controller Test --- pom.xml | 16 ++++- src/main/resources/spring/spring-db.xml | 2 + .../topjava/web/AbstractControllerTest.java | 64 +++++++++++++++++++ .../topjava/web/user/UserControllerTest.java | 34 ++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java diff --git a/pom.xml b/pom.xml index efe85c07d7be..9d94cbe3f209 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 5.0.6.Final 5.1.3.Final + 1.3 2.10.1 @@ -189,14 +190,27 @@ junit ${junit.version} test + + + hamcrest-core + org.hamcrest + + + + + org.hamcrest + hamcrest-all + ${hamcrest.version} + test + + org.springframework spring-test ${spring.version} test - diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index e50398fc6bbd..855799c3310f 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -80,9 +80,11 @@ + diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java new file mode 100644 index 000000000000..ebf46a864743 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -0,0 +1,64 @@ +package ru.javawebinar.topjava.web; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; +import ru.javawebinar.topjava.service.UserService; + +import javax.annotation.PostConstruct; + +import static ru.javawebinar.topjava.Profiles.DATAJPA; +import static ru.javawebinar.topjava.Profiles.POSTGRES; + +/** + * User: gkislin + * Date: 10.08.2014 + */ +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-mvc.xml", + "classpath:spring/spring-db.xml" +}) +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@ActiveProfiles({POSTGRES, DATAJPA}) +abstract public class AbstractControllerTest { + + private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); + + static { + CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); + CHARACTER_ENCODING_FILTER.setForceEncoding(true); + } + + protected MockMvc mockMvc; + + @Autowired + protected UserService userService; + + @Autowired + private WebApplicationContext webApplicationContext; + + @PostConstruct + void postConstruct() { + mockMvc = MockMvcBuilders + .webAppContextSetup(webApplicationContext) + .addFilter(CHARACTER_ENCODING_FILTER) + .build(); + } + + @Before + public void setUp() { + userService.evictCache(); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java new file mode 100644 index 000000000000..f4fb6840231a --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java @@ -0,0 +1,34 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.Test; +import ru.javawebinar.topjava.web.AbstractControllerTest; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.UserTestData.USER; +import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; + +/** + * GKislin + * 10.04.2015. + */ +public class UserControllerTest extends AbstractControllerTest { + + @Test + public void testUserList() throws Exception { + mockMvc.perform(get("/users")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(view().name("userList")) + .andExpect(forwardedUrl("/WEB-INF/jsp/userList.jsp")) + .andExpect(model().attribute("userList", hasSize(2))) + .andExpect(model().attribute("userList", hasItem( + allOf( + hasProperty("id", is(START_SEQ)), + hasProperty("name", is(USER.getName())) + ) + ))); + } +} \ No newline at end of file From 5e44d5b72c6a61cc7c7c402f565975cace7a71d1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 16 Jan 2016 20:10:49 +0300 Subject: [PATCH 065/108] 7_09 REST Controller --- .../topjava/web/user/AdminRestController.java | 37 +++++++++++++++---- .../web/user/ProfileRestController.java | 14 +++++-- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index d6b8bb420803..58d9e779d64a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -1,38 +1,59 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.stereotype.Controller; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; +import java.net.URI; import java.util.List; /** * GKislin * 06.03.2015. */ -@Controller +@RestController +@RequestMapping(AdminRestController.REST_URL) public class AdminRestController extends AbstractUserController { + static final String REST_URL = "/rest/admin/users"; + @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List getAll() { return super.getAll(); } - public User get(int id) { + @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public User get(@PathVariable("id") int id) { return super.get(id); } - public User create(User user) { - return super.create(user); + @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createWithLocation(@RequestBody User user) { + User created = super.create(user); + URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() + .path(REST_URL + "/{id}") + .buildAndExpand(created.getId()).toUri(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setLocation(uriOfNewResource); + + return new ResponseEntity<>(created, httpHeaders, HttpStatus.CREATED); } - public void delete(int id) { + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + public void delete(@PathVariable("id") int id) { super.delete(id); } - public void update(User user, int id) { + @RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + public void update(@RequestBody User user, @PathVariable("id") int id) { super.update(user, id); } - public User getByMail(String email) { + @RequestMapping(value = "/by", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public User getByMail(@RequestParam("email") 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 index 1114e57eeb1e..c18fcb14f3ef 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -1,6 +1,10 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.stereotype.Controller; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.User; @@ -8,18 +12,22 @@ * GKislin * 06.03.2015. */ -@Controller +@RestController +@RequestMapping(ProfileRestController.REST_URL) public class ProfileRestController extends AbstractUserController { + public static final String REST_URL = "/rest/profile"; + @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public User get() { return super.get(LoggedUser.id()); } + @RequestMapping(method = RequestMethod.DELETE) public void delete() { super.delete(LoggedUser.id()); } - public void update(User user) { + public void update(@RequestBody User user) { super.update(user, LoggedUser.id()); } } \ No newline at end of file From 77142c909468977c0604813c8522f158327d0a7e Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 16 Jan 2016 20:33:12 +0300 Subject: [PATCH 066/108] 7_10 REST Test Jackson --- pom.xml | 8 +++++++ .../javawebinar/topjava/model/BaseEntity.java | 5 ++++ .../ru/javawebinar/topjava/model/User.java | 2 ++ .../web/user/AdminRestControllerTest.java | 24 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java diff --git a/pom.xml b/pom.xml index 9d94cbe3f209..2e4aa932782e 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 5.1.3.Final 1.3 + 2.6.4 2.10.1 @@ -145,6 +146,13 @@ + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-json.version} + + org.springframework.data spring-data-jpa diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index e5d8b6dd9971..ad165de8e73a 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 com.fasterxml.jackson.annotation.JsonAutoDetect; import ru.javawebinar.topjava.LoggerWrapper; import javax.persistence.*; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; + /** * User: gkislin * Date: 22.08.2014 */ @MappedSuperclass @Access(AccessType.FIELD) +@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) public class BaseEntity { protected static final LoggerWrapper LOG = LoggerWrapper.get(BaseEntity.class); diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 41450e9d1c4e..7c951c16b9f1 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 com.fasterxml.jackson.annotation.JsonIgnore; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.validator.constraints.Email; @@ -61,6 +62,7 @@ public class User extends NamedEntity { @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "user") @OrderBy("dateTime DESC") + @JsonIgnore protected List meals; public User() { diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java new file mode 100644 index 000000000000..686748b85646 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.Test; +import org.springframework.http.MediaType; +import ru.javawebinar.topjava.web.AbstractControllerTest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; + +public class AdminRestControllerTest extends AbstractControllerTest { + + public static final String REST_URL = AdminRestController.REST_URL + '/'; + + @Test + public void testGet() throws Exception { + mockMvc.perform(get(REST_URL + ADMIN_ID)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + } +} \ No newline at end of file From 8a8400c573ebb871b854a7f532d13956c4460bd9 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 18 Jan 2016 16:41:57 +0300 Subject: [PATCH 067/108] 7_11 Jackson Hibernate --- pom.xml | 5 +++ .../javawebinar/topjava/model/BaseEntity.java | 6 +-- .../ru/javawebinar/topjava/model/User.java | 3 +- .../ru/javawebinar/topjava/util/TimeUtil.java | 8 ++++ .../topjava/web/json/JacksonObjectMapper.java | 43 ++++++++++++++++++ .../web/json/JsonLocalDateTimeConverter.java | 42 ++++++++++++++++++ .../topjava/web/json/JsonUtil.java | 44 +++++++++++++++++++ src/main/resources/spring/spring-mvc.xml | 10 ++++- .../topjava/web/json/JsonUtilTest.java | 30 +++++++++++++ 9 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java diff --git a/pom.xml b/pom.xml index 2e4aa932782e..07a0cc7c2bc5 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,11 @@ jackson-databind ${jackson-json.version} + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate4 + ${jackson-json.version} + org.springframework.data diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index ad165de8e73a..c7d1bb2b0dae 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -1,20 +1,16 @@ package ru.javawebinar.topjava.model; -import com.fasterxml.jackson.annotation.JsonAutoDetect; import ru.javawebinar.topjava.LoggerWrapper; import javax.persistence.*; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; - /** * User: gkislin * Date: 22.08.2014 */ @MappedSuperclass @Access(AccessType.FIELD) -@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) +//@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) public class BaseEntity { protected static final LoggerWrapper LOG = LoggerWrapper.get(BaseEntity.class); diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 7c951c16b9f1..058d05cb4dc4 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.model; -import com.fasterxml.jackson.annotation.JsonIgnore; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.validator.constraints.Email; @@ -62,7 +61,7 @@ public class User extends NamedEntity { @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, mappedBy = "user") @OrderBy("dateTime DESC") - @JsonIgnore +// @JsonIgnore protected List meals; public User() { diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java index d8342ec95a9e..abc977567e19 100644 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java @@ -35,4 +35,12 @@ public static LocalDate parseLocalDate(String str, LocalDate def) { public static LocalTime parseLocalTime(String str, LocalTime def) { return StringUtils.isEmpty(str) ? def : LocalTime.parse(str); } + + public static LocalDateTime parseLocalDateTime(String str) { + return parseLocalDateTime(str, DATE_TME_FORMATTER); + } + + public static LocalDateTime parseLocalDateTime(String str, DateTimeFormatter formatter) { + return StringUtils.isEmpty(str) ? LocalDateTime.now() : LocalDateTime.parse(str, formatter); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java new file mode 100644 index 000000000000..f2c5abb268e2 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java @@ -0,0 +1,43 @@ +package ru.javawebinar.topjava.web.json; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module; + +import java.time.LocalDateTime; + +/** + * User: gkislin + * Date: 26.05.2014 + *

+ * Handling Hibernate lazy-loading + * + * @link https://github.com/FasterXML/jackson + * @link https://github.com/FasterXML/jackson-datatype-hibernate + * @link http://wiki.fasterxml.com/JacksonHowToCustomSerializers + */ +public class JacksonObjectMapper extends ObjectMapper { + + private static final ObjectMapper MAPPER = new JacksonObjectMapper(); + + public static ObjectMapper getMapper() { + return MAPPER; + } + + private JacksonObjectMapper() { + registerModule(new Hibernate4Module()); + + SimpleModule customModule = new SimpleModule("customModule", new Version(1, 0, 0, null)); + customModule.addSerializer(new JsonLocalDateTimeConverter.UserSettingSerializer()); + customModule.addDeserializer(LocalDateTime.class, new JsonLocalDateTimeConverter.UserSettingDeserializer()); + registerModule(customModule); + + setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); + setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + setSerializationInclusion(JsonInclude.Include.NON_NULL); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java new file mode 100644 index 000000000000..6447099392a9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava.web.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import ru.javawebinar.topjava.util.TimeUtil; + +import java.io.IOException; +import java.time.LocalDateTime; + +/** + * GKislin + * 06.01.2015. + */ +public class JsonLocalDateTimeConverter { + public static class UserSettingSerializer extends JsonSerializer { + @Override + public void serialize(LocalDateTime ldt, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeString(TimeUtil.toString(ldt)); + } + + @Override + public Class handledType() { + return LocalDateTime.class; + } + } + + public static class UserSettingDeserializer extends JsonDeserializer { + @Override + public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctx) throws IOException { + return TimeUtil.parseLocalDateTime(jp.getText()); + } + + @Override + public Class handledType() { + return LocalDateTime.class; + } + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java new file mode 100644 index 000000000000..6810666e50bf --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java @@ -0,0 +1,44 @@ +package ru.javawebinar.topjava.web.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectReader; +import ru.javawebinar.topjava.LoggerWrapper; + +import java.io.IOException; +import java.util.List; + +import static ru.javawebinar.topjava.web.json.JacksonObjectMapper.getMapper; + +/** + * User: gkislin + * Date: 30.04.2014 + */ +public class JsonUtil { + + private static final LoggerWrapper LOG = LoggerWrapper.get(JsonUtil.class); + + public static List readValues(String json, Class clazz) { + ObjectReader reader = getMapper().readerFor(clazz); + try { + return reader.readValues(json).readAll(); + } catch (IOException e) { + throw LOG.getIllegalArgumentException("Invalid read array from JSON:\n'" + json + "'", e); + } + } + + public static T readValue(String json, Class clazz) { + try { + return getMapper().readValue(json, clazz); + } catch (IOException e) { + throw LOG.getIllegalArgumentException("Invalid read from JSON:\n'" + json + "'", e); + } + } + + public static String writeValue(T obj) { + try { + return getMapper().writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e); + } + } +} diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 12ac233daa15..86a60d059a02 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -5,7 +5,15 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> - + + + + + + + + + diff --git a/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java b/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java new file mode 100644 index 000000000000..d1a7589c42ea --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java @@ -0,0 +1,30 @@ +package ru.javawebinar.topjava.web.json; + +import org.junit.Test; +import ru.javawebinar.topjava.MealTestData; +import ru.javawebinar.topjava.model.UserMeal; + +import java.util.List; + +/** + * GKislin + * 22.07.2015. + */ +public class JsonUtilTest { + + @Test + public void testReadWriteValue() throws Exception { + String json = JsonUtil.writeValue(MealTestData.ADMIN_MEAL); + System.out.println(json); + UserMeal userMeal = JsonUtil.readValue(json, UserMeal.class); + MealTestData.MATCHER.assertEquals(MealTestData.ADMIN_MEAL, userMeal); + } + + @Test + public void testReadWriteValues() throws Exception { + String json = JsonUtil.writeValue(MealTestData.USER_MEALS); + System.out.println(json); + List userMeals = JsonUtil.readValues(json, UserMeal.class); + MealTestData.MATCHER.assertCollectionEquals(MealTestData.USER_MEALS, userMeals); + } +} \ No newline at end of file From eefdbb2c38da1a88a6e5407c998ab9fa61ab958b Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 18 Jan 2016 16:43:57 +0300 Subject: [PATCH 068/108] 7_12 Jackson JSR310 --- pom.xml | 7 +++- .../topjava/web/json/JacksonObjectMapper.java | 12 ++---- .../web/json/JsonLocalDateTimeConverter.java | 42 ------------------- 3 files changed, 10 insertions(+), 51 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java diff --git a/pom.xml b/pom.xml index 07a0cc7c2bc5..303871eed983 100644 --- a/pom.xml +++ b/pom.xml @@ -157,6 +157,11 @@ jackson-datatype-hibernate4 ${jackson-json.version} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-json.version} + org.springframework.data @@ -210,7 +215,7 @@ - + org.hamcrest hamcrest-all diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java index f2c5abb268e2..3f4ec95f8176 100644 --- a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java +++ b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java @@ -3,12 +3,10 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module; - -import java.time.LocalDateTime; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; /** * User: gkislin @@ -31,10 +29,8 @@ public static ObjectMapper getMapper() { private JacksonObjectMapper() { registerModule(new Hibernate4Module()); - SimpleModule customModule = new SimpleModule("customModule", new Version(1, 0, 0, null)); - customModule.addSerializer(new JsonLocalDateTimeConverter.UserSettingSerializer()); - customModule.addDeserializer(LocalDateTime.class, new JsonLocalDateTimeConverter.UserSettingDeserializer()); - registerModule(customModule); + registerModule(new JavaTimeModule()); + configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java deleted file mode 100644 index 6447099392a9..000000000000 --- a/src/main/java/ru/javawebinar/topjava/web/json/JsonLocalDateTimeConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.javawebinar.topjava.web.json; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import ru.javawebinar.topjava.util.TimeUtil; - -import java.io.IOException; -import java.time.LocalDateTime; - -/** - * GKislin - * 06.01.2015. - */ -public class JsonLocalDateTimeConverter { - public static class UserSettingSerializer extends JsonSerializer { - @Override - public void serialize(LocalDateTime ldt, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(TimeUtil.toString(ldt)); - } - - @Override - public Class handledType() { - return LocalDateTime.class; - } - } - - public static class UserSettingDeserializer extends JsonDeserializer { - @Override - public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctx) throws IOException { - return TimeUtil.parseLocalDateTime(jp.getText()); - } - - @Override - public Class handledType() { - return LocalDateTime.class; - } - } -} From 981234f184d9ba194789d0e4337a016e15472b46 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 18 Jan 2016 16:45:29 +0300 Subject: [PATCH 069/108] 7_13 Test with matcher --- .../ru/javawebinar/topjava/MealTestData.java | 3 +- .../java/ru/javawebinar/topjava/TestUtil.java | 31 +++++++++ .../ru/javawebinar/topjava/UserTestData.java | 2 +- .../topjava/matcher/ModelMatcher.java | 54 ++++++++++++++- .../topjava/matcher/TestMatcher.java | 29 ++++++++ .../web/user/AdminRestControllerTest.java | 66 ++++++++++++++++++- .../web/user/ProfileRestControllerTest.java | 48 ++++++++++++++ 7 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/TestUtil.java create mode 100644 src/test/java/ru/javawebinar/topjava/matcher/TestMatcher.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index 173673299f57..ac870fafc373 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava; +import ru.javawebinar.topjava.TestUtil.ToStringModelMatcher; import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.UserMeal; @@ -17,7 +18,7 @@ */ public class MealTestData { - public static final ModelMatcher MATCHER = new ModelMatcher<>(UserMeal::toString); + public static final ModelMatcher MATCHER = new ToStringModelMatcher<>(UserMeal.class); public static final int MEAL1_ID = START_SEQ + 2; public static final int ADMIN_MEAL_ID = START_SEQ + 8; diff --git a/src/test/java/ru/javawebinar/topjava/TestUtil.java b/src/test/java/ru/javawebinar/topjava/TestUtil.java new file mode 100644 index 000000000000..bc9aa67c4897 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/TestUtil.java @@ -0,0 +1,31 @@ +package ru.javawebinar.topjava; + +import org.springframework.test.web.servlet.ResultActions; +import ru.javawebinar.topjava.matcher.ModelMatcher; + +import java.io.UnsupportedEncodingException; + +/** + * GKislin + * 05.01.2015. + */ +public class TestUtil { + + public static ResultActions print(ResultActions action) throws UnsupportedEncodingException { + System.out.println(getContent(action)); + return action; + } + + public static String getContent(ResultActions action) throws UnsupportedEncodingException { + return action.andReturn().getResponse().getContentAsString(); + } + + /** + * Compare entities using toString + */ + public static class ToStringModelMatcher extends ModelMatcher { + public ToStringModelMatcher(Class entityClass) { + super(Object::toString, entityClass); + } + } +} diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index 06823a8151d0..d6410ea3a13d 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -22,7 +22,7 @@ public class UserTestData { 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, Role.ROLE_USER); - public static final ModelMatcher MATCHER = new ModelMatcher<>(u -> ((u instanceof TestUser) ? (TestUser) u : new TestUser(u))); + public static final ModelMatcher MATCHER = new ModelMatcher<>(u -> ((u instanceof TestUser) ? (TestUser) u : new TestUser(u)), User.class); public static class TestUser extends User { diff --git a/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java index f2b18b2e34bd..b7078c203d2c 100644 --- a/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java +++ b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java @@ -1,11 +1,20 @@ package ru.javawebinar.topjava.matcher; import org.junit.Assert; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.ResultMatcher; +import ru.javawebinar.topjava.TestUtil; +import ru.javawebinar.topjava.web.json.JsonUtil; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; + /** * GKislin * 06.01.2015. @@ -15,9 +24,19 @@ */ public class ModelMatcher { protected Function entityConverter; + protected Class entityClass; - public ModelMatcher(Function entityConverter) { + public ModelMatcher(Function entityConverter, Class entityClass) { this.entityConverter = entityConverter; + this.entityClass = entityClass; + } + + private T fromJsonValue(String json) { + return JsonUtil.readValue(json, entityClass); + } + + private Collection fromJsonValues(String json) { + return JsonUtil.readValues(json, entityClass); } public void assertEquals(T expected, T actual) { @@ -28,7 +47,38 @@ public void assertCollectionEquals(Collection expected, Collection actual) Assert.assertEquals(map(expected, entityConverter), map(actual, entityConverter)); } - public static Collection map(Collection collection, Function converter) { + public static List map(Collection collection, Function converter) { return collection.stream().map(converter).collect(Collectors.toList()); } + + public ResultMatcher contentMatcher(T expect) { + return content().string( + new TestMatcher(expect) { + @Override + protected boolean compare(T expected, String body) { + R actualForCompare = entityConverter.apply(fromJsonValue(body)); + R expectedForCompare = entityConverter.apply(expected); + return expectedForCompare.equals(actualForCompare); + } + }); + } + + public final ResultMatcher contentListMatcher(T... expected) { + return contentListMatcher(Arrays.asList(expected)); + } + + public final ResultMatcher contentListMatcher(List expected) { + return content().string(new TestMatcher>(expected) { + @Override + protected boolean compare(List expected, String actual) { + List actualList = map(fromJsonValues(actual), entityConverter); + List expectedList = map(expected, entityConverter); + return expectedList.equals(actualList); + } + }); + } + + public T fromJsonAction(ResultActions action) throws UnsupportedEncodingException { + return fromJsonValue(TestUtil.getContent(action)); + } } diff --git a/src/test/java/ru/javawebinar/topjava/matcher/TestMatcher.java b/src/test/java/ru/javawebinar/topjava/matcher/TestMatcher.java new file mode 100644 index 000000000000..32d599020146 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/matcher/TestMatcher.java @@ -0,0 +1,29 @@ +package ru.javawebinar.topjava.matcher; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import ru.javawebinar.topjava.web.json.JsonUtil; + +/** + * GKislin + * 05.01.2015. + */ +abstract public class TestMatcher extends BaseMatcher { + protected T expected; + + public TestMatcher(T expected) { + this.expected = expected; + } + + @Override + public boolean matches(Object actual) { + return compare(expected, (String) actual); + } + + abstract protected boolean compare(T expected, String actual); + + @Override + public void describeTo(Description description) { + description.appendText(JsonUtil.writeValue(expected)); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 686748b85646..40dd88499da4 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -2,13 +2,21 @@ import org.junit.Test; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.ResultActions; +import ru.javawebinar.topjava.TestUtil; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import java.util.Arrays; +import java.util.Collections; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; +import static ru.javawebinar.topjava.UserTestData.*; public class AdminRestControllerTest extends AbstractControllerTest { @@ -19,6 +27,58 @@ public void testGet() throws Exception { mockMvc.perform(get(REST_URL + ADMIN_ID)) .andExpect(status().isOk()) .andDo(print()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER.contentMatcher(ADMIN)); + } + + @Test + public void testGetByEmail() throws Exception { + mockMvc.perform(get(REST_URL + "by?email=" + USER.getEmail())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER.contentMatcher(USER)); + } + + @Test + public void testDelete() throws Exception { + mockMvc.perform(delete(REST_URL + USER_ID).contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andExpect(status().isOk()); + MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), userService.getAll()); + } + + @Test + public void testUpdate() throws Exception { + User updated = new User(USER); + updated.setName("UpdatedName"); + updated.setRoles(Collections.singletonList(Role.ROLE_ADMIN)); + mockMvc.perform(put(REST_URL + USER_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andExpect(status().isOk()); + + MATCHER.assertEquals(updated, userService.get(USER_ID)); + } + + @Test + public void testCreate() throws Exception { + TestUser expected = new TestUser("New", "new@gmail.com", "newPass", Role.ROLE_USER, Role.ROLE_ADMIN); + ResultActions action = mockMvc.perform(post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(expected.asUser()))).andExpect(status().isCreated()); + + User returned = MATCHER.fromJsonAction(action); + expected.setId(returned.getId()); + + MATCHER.assertEquals(expected, returned); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, expected, USER), userService.getAll()); + } + + @Test + public void testGetAll() throws Exception { + TestUtil.print(mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER.contentListMatcher(ADMIN, USER))); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java new file mode 100644 index 000000000000..1754e5c1bdd2 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -0,0 +1,48 @@ +package ru.javawebinar.topjava.web.user; + +import org.junit.Test; +import org.springframework.http.MediaType; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.TestUtil; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; + +import java.util.Collections; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.UserTestData.*; +import static ru.javawebinar.topjava.web.user.ProfileRestController.REST_URL; + +public class ProfileRestControllerTest extends AbstractControllerTest { + + @Test + public void testGet() throws Exception { + TestUtil.print(mockMvc.perform(get(REST_URL)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER.contentMatcher(USER))); + } + + @Test + public void testDelete() throws Exception { + mockMvc.perform(delete(REST_URL).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), userService.getAll()); + } + + @Test + public void testUpdate() throws Exception { + User updated = new User(LoggedUser.id(), "newName", "newEmail", "newPassword", Role.ROLE_USER); + mockMvc.perform(put(REST_URL).contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andDo(print()) + .andExpect(status().isOk()); + + MATCHER.assertEquals(updated, new User(userService.getByEmail("newEmail"))); + } +} \ No newline at end of file From a6dd20bd05a083d1f8ffed76fe112ec1f2b09d3a Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 18 Jan 2016 16:58:02 +0300 Subject: [PATCH 070/108] 7_14 SOAP UI UTF8 Converter --- config/Topjava-soapui-project.xml | 17 +++++++++++++++++ .../topjava/web/user/ProfileRestController.java | 5 +++++ src/main/resources/spring/spring-mvc.xml | 8 ++++++++ .../topjava/web/AdminInMemoryTest.java | 4 +++- 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 config/Topjava-soapui-project.xml diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml new file mode 100644 index 000000000000..44ad8436a5cc --- /dev/null +++ b/config/Topjava-soapui-project.xml @@ -0,0 +1,17 @@ + +http://localhost:8080text/html;charset=utf-8500 401htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/usersuser@yandex.rupasswordNo AuthorizationBasicNo Authorizationapplication/jsontext/html;charset=utf-8500htmlapplication/json201user:Response<xml-fragment/>http://localhost:8080{ + "name": "New2", + "email": "new2@yandex.ru", + "password": "passwordNew", + "roles": ["ROLE_USER"] +}http://localhost/topjava/rest/admin/usersNo AuthorizationBasicNo Authorizationtext/html;charset=utf-8500htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/users/100000No AuthorizationBasicNo Authorizationtext/html;charset=utf-8405 500htmlapplication/json200data200data200data<xml-fragment/>http://localhost:8080{ + "name": "UserUpdated", + "email": "user@yandex.ru", + "password": "passwordNew", + "roles": ["ROLE_USER"] +}http://localhost/topjava/rest/admin/users/100000BasicBasicGlobal HTTP Settingstext/html;charset=utf-8500htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/profileBasicBasicGlobal HTTP Settingsapplication/jsontext/html;charset=utf-8500 405htmlapplication/json201user:Response200data200data200data200data200data<xml-fragment/>http://localhost:8080{ + "name": "New777", + "email": "new777@yandex.ru", + "password": "passwordNew", + "roles": ["ROLE_USER"] +}http://localhost/topjava/rest/profileNo AuthorizationBasicNo Authorization200datatext/html;charset=utf-8500htmlapplication/json200data<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/profileNo AuthorizationBasicNo Authorization \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index c18fcb14f3ef..7b8e07b8d64a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -30,4 +30,9 @@ public void delete() { public void update(@RequestBody User user) { super.update(user, LoggedUser.id()); } + + @RequestMapping(value = "/text", method = RequestMethod.GET) + public String testUTF() { + return "Русский текст"; + } } \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 86a60d059a02..a770bdfe84dd 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -12,6 +12,14 @@ + + + + text/plain;charset=UTF-8 + text/html;charset=UTF-8 + + + diff --git a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java index 24b3e6f47457..666cfcd1eea2 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AdminInMemoryTest.java @@ -37,7 +37,9 @@ public void setUp() throws Exception { @AfterClass public static void afterClass() { - appCtx.close(); +// May cause during JUnit "Cache is not alive (STATUS_SHUTDOWN)" as JUnit share Spring context for speed +// http://stackoverflow.com/questions/16281802/ehcache-shutdown-causing-an-exception-while-running-test-suite +// appCtx.close(); } @Test From f79504182b4ae1273b96154b4286dc936501fe14 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 22 Jan 2016 21:14:42 +0300 Subject: [PATCH 071/108] 8_01 HW07 JDBC Transaction --- config/Topjava-soapui-project.xml | 6 +++--- .../topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java | 5 +++++ .../topjava/repository/jdbc/JdbcUserRepositoryImpl.java | 4 ++++ src/test/resources/logback-test.xml | 5 +++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml index 44ad8436a5cc..e65047557167 100644 --- a/config/Topjava-soapui-project.xml +++ b/config/Topjava-soapui-project.xml @@ -1,15 +1,15 @@ -http://localhost:8080text/html;charset=utf-8500 401htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/usersuser@yandex.rupasswordNo AuthorizationBasicNo Authorizationapplication/jsontext/html;charset=utf-8500htmlapplication/json201user:Response<xml-fragment/>http://localhost:8080{ +http://localhost:8080text/html;charset=utf-8500 401htmlapplication/json200ns:Responseapplication/json;charset=UTF-8200Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/usersuser@yandex.rupasswordNo AuthorizationBasicNo Authorizationapplication/jsontext/html;charset=utf-8500htmlapplication/json201user:Responseapplication/json;charset=UTF-8201user:Response<xml-fragment/>http://localhost:8080{ "name": "New2", "email": "new2@yandex.ru", "password": "passwordNew", "roles": ["ROLE_USER"] -}http://localhost/topjava/rest/admin/usersNo AuthorizationBasicNo Authorizationtext/html;charset=utf-8500htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/users/100000No AuthorizationBasicNo Authorizationtext/html;charset=utf-8405 500htmlapplication/json200data200data200data<xml-fragment/>http://localhost:8080{ +}http://localhost/topjava/rest/admin/usersNo AuthorizationBasicNo Authorizationtext/html;charset=utf-8500htmlapplication/json200ns:Responseapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/admin/users/100000No AuthorizationBasicNo Authorizationtext/html;charset=utf-8405 500htmlapplication/json200data200data200data<xml-fragment/>http://localhost:8080{ "name": "UserUpdated", "email": "user@yandex.ru", "password": "passwordNew", "roles": ["ROLE_USER"] -}http://localhost/topjava/rest/admin/users/100000BasicBasicGlobal HTTP Settingstext/html;charset=utf-8500htmlapplication/json200ns:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/profileBasicBasicGlobal HTTP Settingsapplication/jsontext/html;charset=utf-8500 405htmlapplication/json201user:Response200data200data200data200data200data<xml-fragment/>http://localhost:8080{ +}http://localhost/topjava/rest/admin/users/100000BasicBasicGlobal HTTP Settingstext/html;charset=utf-8500htmlapplication/json200ns:Responseapplication/json;charset=UTF-8200prof:Response<xml-fragment/>http://localhost:8080http://localhost/topjava/rest/profileBasicBasicGlobal HTTP Settingsapplication/jsontext/html;charset=utf-8500 405htmlapplication/json201user:Response200data200data200data200data200data<xml-fragment/>http://localhost:8080{ "name": "New777", "email": "new777@yandex.ru", "password": "passwordNew", diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java index 3442b8151a96..31e802a837a3 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserMealRepositoryImpl.java @@ -8,6 +8,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.repository.UserMealRepository; @@ -22,6 +23,7 @@ */ @Repository +@Transactional(readOnly = true) public class JdbcUserMealRepositoryImpl implements UserMealRepository { private static final RowMapper ROW_MAPPER = @@ -45,6 +47,7 @@ public JdbcUserMealRepositoryImpl(DataSource dataSource) { } @Override + @Transactional public UserMeal save(UserMeal userMeal, int userId) { MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", userMeal.getId()) @@ -67,6 +70,7 @@ public UserMeal save(UserMeal userMeal, int userId) { } @Override + @Transactional public boolean delete(int id, int userId) { return jdbcTemplate.update("DELETE FROM meals WHERE id=? AND user_id=?", id, userId) != 0; } @@ -77,6 +81,7 @@ public UserMeal get(int id, int userId) { "SELECT * FROM meals WHERE id = ? AND user_id = ?", ROW_MAPPER, id, userId); return DataAccessUtils.singleResult(userMeals); } + public List getAll(int userId) { return jdbcTemplate.query( "SELECT * FROM meals WHERE user_id=? ORDER BY date_time DESC", ROW_MAPPER, userId); 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 4f93559318de..e566c833f167 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -9,6 +9,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; @@ -28,6 +29,7 @@ */ @Repository +@Transactional(readOnly = true) public class JdbcUserRepositoryImpl implements UserRepository { private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); @@ -48,6 +50,7 @@ public JdbcUserRepositoryImpl(DataSource dataSource) { } @Override + @Transactional public User save(User user) { MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", user.getId()) @@ -73,6 +76,7 @@ public User save(User user) { } @Override + @Transactional public boolean delete(int id) { return jdbcTemplate.update("DELETE FROM users WHERE id=?", id) != 0; } diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index ffe00fed77c7..5d60d1adbf43 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -12,10 +12,11 @@ - - + + + From ce12c294a35172007297c06c9394931714de1861 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 22 Jan 2016 21:22:11 +0300 Subject: [PATCH 072/108] 8_02 HW07 Controller Test --- src/main/resources/logback.xml | 3 +- .../topjava/web/ResourceControllerTest.java | 24 +++++++++++++ .../web/meal/UserMealControllerTest.java | 34 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java create mode 100644 src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 519084a09ab2..578254e0ffef 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -24,7 +24,8 @@ - + + diff --git a/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java new file mode 100644 index 000000000000..3d2585a50973 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.web; + +import org.junit.Test; +import org.springframework.http.MediaType; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * GKislin + * 10.04.2015. + */ +public class ResourceControllerTest extends AbstractControllerTest { + + @Test + public void testResources() throws Exception { + mockMvc.perform(get("/resources/css/style.css")) + .andDo(print()) + .andExpect(content().contentType(MediaType.valueOf("text/css"))) + .andExpect(status().isOk()); + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java new file mode 100644 index 000000000000..3c0e72aabe55 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java @@ -0,0 +1,34 @@ +package ru.javawebinar.topjava.web.meal; + +import org.junit.Test; +import ru.javawebinar.topjava.web.AbstractControllerTest; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.MealTestData.MEAL1; +import static ru.javawebinar.topjava.MealTestData.MEAL1_ID; + +/** + * GKislin + * 10.04.2015. + */ +public class UserMealControllerTest extends AbstractControllerTest { + + @Test + public void testMealList() throws Exception { + mockMvc.perform(get("/meals")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(view().name("mealList")) + .andExpect(forwardedUrl("/WEB-INF/jsp/mealList.jsp")) + .andExpect(model().attribute("mealList", hasSize(6))) + .andExpect(model().attribute("mealList", hasItem( + allOf( + hasProperty("id", is(MEAL1_ID)), + hasProperty("description", is(MEAL1.getDescription())) + ) + ))); + } +} \ No newline at end of file From 37a3b610f86e9cd8477b76576b3215460563bb9f Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 22 Jan 2016 21:24:16 +0300 Subject: [PATCH 073/108] 8_03 HW07 REST Controller --- .../topjava/to/UserMealWithExceed.java | 12 +-- .../web/meal/UserMealRestController.java | 65 ++++++++++++-- .../ru/javawebinar/topjava/MealTestData.java | 2 + .../web/meal/UserMealRestControllerTest.java | 90 +++++++++++++++++++ 4 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java diff --git a/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java index 94aa3fc4b679..de4a87407ce6 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserMealWithExceed.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava.to; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.time.LocalDateTime; /** @@ -17,11 +19,11 @@ public class UserMealWithExceed { protected final boolean exceed; - public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { - this(null, dateTime, description, calories, exceed); - } - - public UserMealWithExceed(Integer id, LocalDateTime dateTime, String description, int calories, boolean exceed) { + public UserMealWithExceed(@JsonProperty("id") Integer id, + @JsonProperty("dateTime") LocalDateTime dateTime, + @JsonProperty("description") String description, + @JsonProperty("calories") int calories, + @JsonProperty("exceed") boolean exceed) { this.id = id; this.dateTime = dateTime; this.description = description; diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index 0dcb0a604b9f..66ed92fa58e3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -1,11 +1,62 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.stereotype.Controller; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.to.UserMealWithExceed; -/** - * GKislin - * 06.03.2015. - */ -@Controller +import java.net.URI; +import java.time.LocalDateTime; +import java.util.List; + +@RestController +@RequestMapping(value = UserMealRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class UserMealRestController extends AbstractUserMealController { -} \ No newline at end of file + public static final String REST_URL = "/rest/profile/meals"; + + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + public UserMeal get(@PathVariable("id") int id) { + return super.get(id); + } + + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + public void delete(@PathVariable("id") int id) { + super.delete(id); + } + + @RequestMapping(method = RequestMethod.GET) + public List getAll() { + return super.getAll(); + } + + @RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + public void update(@RequestBody UserMeal meal, @PathVariable("id") int id) { + super.update(meal, id); + } + + @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createWithLocation(@RequestBody UserMeal meal) { + UserMeal created = super.create(meal); + + URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() + .path(REST_URL + "/{id}") + .buildAndExpand(created.getId()).toUri(); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setLocation(uriOfNewResource); + + return new ResponseEntity<>(created, httpHeaders, HttpStatus.CREATED); + } + + @RequestMapping(value = "/between", method = RequestMethod.GET) + public List getBetween( + @RequestParam(value = "startDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDateTime, + @RequestParam(value = "endDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDateTime) { + return super.getBetween(startDateTime.toLocalDate(), startDateTime.toLocalTime(), endDateTime.toLocalDate(), endDateTime.toLocalTime()); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index ac870fafc373..c38a13b59999 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -3,6 +3,7 @@ import ru.javawebinar.topjava.TestUtil.ToStringModelMatcher; import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.to.UserMealWithExceed; import java.time.LocalDateTime; import java.time.Month; @@ -19,6 +20,7 @@ public class MealTestData { public static final ModelMatcher MATCHER = new ToStringModelMatcher<>(UserMeal.class); + public static final ModelMatcher MATCHER_WITH_EXCEED = new ToStringModelMatcher<>(UserMealWithExceed.class); public static final int MEAL1_ID = START_SEQ + 2; public static final int ADMIN_MEAL_ID = START_SEQ + 8; diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java new file mode 100644 index 000000000000..8333b46e9697 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java @@ -0,0 +1,90 @@ +package ru.javawebinar.topjava.web.meal; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.ResultActions; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.service.UserMealService; +import ru.javawebinar.topjava.util.UserMealsUtil; +import ru.javawebinar.topjava.web.AbstractControllerTest; +import ru.javawebinar.topjava.web.json.JsonUtil; + +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.MealTestData.*; +import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; + +public class UserMealRestControllerTest extends AbstractControllerTest { + + public static final String REST_URL = UserMealRestController.REST_URL + '/'; + + @Autowired + private UserMealService service; + + @Test + public void testGet() throws Exception { + mockMvc.perform(get(REST_URL + MEAL1_ID)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER.contentMatcher(MEAL1)); + } + + @Test + public void testDelete() throws Exception { + mockMvc.perform(delete(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + MATCHER.assertCollectionEquals(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2), service.getAll(START_SEQ)); + } + + @Test + public void testUpdate() throws Exception { + UserMeal updated = getUpdated(); + + mockMvc.perform(put(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(updated))) + .andExpect(status().isOk()); + + assertEquals(updated, service.get(MEAL1_ID, START_SEQ)); + } + + @Test + public void testCreate() throws Exception { + UserMeal created = getCreated(); + ResultActions action = mockMvc.perform(post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(JsonUtil.writeValue(created))); + + UserMeal returned = MATCHER.fromJsonAction(action); + created.setId(returned.getId()); + + MATCHER.assertEquals(created, returned); + MATCHER.assertCollectionEquals(Arrays.asList(created, MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), service.getAll(START_SEQ)); + } + + @Test + public void testGetAll() throws Exception { + mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(MATCHER_WITH_EXCEED.contentListMatcher(UserMealsUtil.getWithExceeded(USER_MEALS, LoggedUser.getCaloriesPerDay()))); + } + + @Test + public void testGetBetween() throws Exception { + mockMvc.perform(get(REST_URL + "between?startDateTime=2015-05-30T07:00&endDateTime=2015-05-31T11:00:00")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( + UserMealsUtil.createWithExceed(MEAL4, true), + UserMealsUtil.createWithExceed(MEAL1, false))); + } +} \ No newline at end of file From ca74343480d7b4ca2e04e9debcc075ca478e97ad Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 22 Jan 2016 21:34:18 +0300 Subject: [PATCH 074/108] 8_04 HW07 Formatters --- .../util/converter/LocalDateFormatter.java | 26 +++++++++++++++++++ .../util/converter/LocalTimeFormatter.java | 26 +++++++++++++++++++ .../web/meal/UserMealRestController.java | 12 +++++++++ src/main/resources/spring/spring-mvc.xml | 11 +++++++- .../web/meal/UserMealRestControllerTest.java | 19 ++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/converter/LocalDateFormatter.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/converter/LocalTimeFormatter.java diff --git a/src/main/java/ru/javawebinar/topjava/util/converter/LocalDateFormatter.java b/src/main/java/ru/javawebinar/topjava/util/converter/LocalDateFormatter.java new file mode 100644 index 000000000000..193b32de9406 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/converter/LocalDateFormatter.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava.util.converter; + +import org.springframework.format.Formatter; +import ru.javawebinar.topjava.util.TimeUtil; + +import java.text.ParseException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * GKislin + * 15.04.2015. + */ +public class LocalDateFormatter implements Formatter { + + @Override + public LocalDate parse(String text, Locale locale) throws ParseException { + return TimeUtil.parseLocalDate(text, null); + } + + @Override + public String print(LocalDate lt, Locale locale) { + return lt.format(DateTimeFormatter.ISO_LOCAL_DATE); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/converter/LocalTimeFormatter.java b/src/main/java/ru/javawebinar/topjava/util/converter/LocalTimeFormatter.java new file mode 100644 index 000000000000..1a29f9c5accd --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/converter/LocalTimeFormatter.java @@ -0,0 +1,26 @@ +package ru.javawebinar.topjava.util.converter; + +import org.springframework.format.Formatter; +import ru.javawebinar.topjava.util.TimeUtil; + +import java.text.ParseException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * GKislin + * 15.04.2015. + */ +public class LocalTimeFormatter implements Formatter { + + @Override + public LocalTime parse(String text, Locale locale) throws ParseException { + return TimeUtil.parseLocalTime(text, null); + } + + @Override + public String print(LocalTime lt, Locale locale) { + return lt.format(DateTimeFormatter.ISO_LOCAL_TIME); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index 66ed92fa58e3..bedb2675b002 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -9,9 +9,12 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; +import ru.javawebinar.topjava.util.TimeUtil; import java.net.URI; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.List; @RestController @@ -59,4 +62,13 @@ public List getBetween( @RequestParam(value = "endDateTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDateTime) { return super.getBetween(startDateTime.toLocalDate(), startDateTime.toLocalTime(), endDateTime.toLocalDate(), endDateTime.toLocalTime()); } + + @RequestMapping(value = "/filter", method = RequestMethod.GET) + public List getBetween( + @RequestParam(value = "startDate", required = false) LocalDate startDate, @RequestParam(value = "startTime", required = false) LocalTime startTime, + @RequestParam(value = "endDate", required = false) LocalDate endDate, @RequestParam(value = "endTime", required = false) LocalTime endTime) { + return super.getBetween( + startDate != null ? startDate : TimeUtil.MIN_DATE, startTime != null ? startTime : LocalTime.MIN, + endDate != null ? endDate : TimeUtil.MAX_DATE, endTime != null ? endTime : LocalTime.MAX); + } } diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index a770bdfe84dd..dc25177311f6 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -7,7 +7,7 @@ - + @@ -23,6 +23,15 @@ + + + + + + + + + diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java index 8333b46e9697..124a7f147b83 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java @@ -87,4 +87,23 @@ public void testGetBetween() throws Exception { UserMealsUtil.createWithExceed(MEAL4, true), UserMealsUtil.createWithExceed(MEAL1, false))); } + + @Test + public void testFilter() throws Exception { + mockMvc.perform(get(REST_URL + "filter?startDate=2015-05-30&startTime=07:00&endDate=2015-05-31&endTime=11:00")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( + UserMealsUtil.createWithExceed(MEAL4, true), + UserMealsUtil.createWithExceed(MEAL1, false))); + } + + @Test + public void testFilterAll() throws Exception { + mockMvc.perform(get(REST_URL + "filter?startDate=&endTime=")) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( + UserMealsUtil.getWithExceeded(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), LoggedUser.getCaloriesPerDay()))); + } } \ No newline at end of file From 83f1197768f55134bf11353218c00e723e624a85 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 23 Jan 2016 21:22:10 +0300 Subject: [PATCH 075/108] ProfileRestController changes --- .../ru/javawebinar/topjava/web/user/ProfileRestController.java | 1 + 1 file changed, 1 insertion(+) 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 7b8e07b8d64a..dc546f7ef5b1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -27,6 +27,7 @@ public void delete() { super.delete(LoggedUser.id()); } + @RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public void update(@RequestBody User user) { super.update(user, LoggedUser.id()); } From 2a556880a86212cd3a5957170e239f386e6928b6 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 25 Jan 2016 10:27:33 +0300 Subject: [PATCH 076/108] 8_06 WebJars --- pom.xml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pom.xml b/pom.xml index 303871eed983..0f94f1473a99 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,13 @@ 1.3 2.6.4 2.10.1 + + + 3.3.5 + 2.1.4 + 2.3.4 + 2.2.4 + 1.10.9 @@ -146,6 +153,33 @@ + + + org.webjars + bootstrap + ${webjars-bootstrap.version} + + + org.webjars + jquery + ${webjars-jquery.version} + + + org.webjars + datetimepicker + ${webjars-datetimepicker.version} + + + org.webjars + noty + ${webjars-noty.version} + + + org.webjars + datatables + ${webjars-datatables.version} + + com.fasterxml.jackson.core From 28b12dcf9e77b0a0eb7667fcae34a9f82ef70543 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 25 Jan 2016 11:06:47 +0300 Subject: [PATCH 077/108] 8_07 Bootstrap --- config/messages/app.properties | 2 + config/messages/app_ru.properties | 4 +- src/main/resources/spring/spring-mvc.xml | 3 + .../WEB-INF/jsp/fragments/bodyHeader.jsp | 16 ++++- .../webapp/WEB-INF/jsp/fragments/footer.jsp | 8 ++- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 3 + src/main/webapp/WEB-INF/jsp/index.jsp | 30 +++++---- src/main/webapp/WEB-INF/jsp/userList.jsp | 65 ++++++++++++------- 8 files changed, 89 insertions(+), 42 deletions(-) diff --git a/config/messages/app.properties b/config/messages/app.properties index 8db5a6b72d32..17de531cd703 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -4,6 +4,8 @@ app.login=Login as users.title=User list users.edit=Edit user +users.add=Add user + meals.title=Meals list meals.edit=Edit meal common.select=Выбрать \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 71fc8a65f02c..0bbaf3f9f875 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -4,6 +4,8 @@ app.login=Зайти как users.title=Список пользователей users.edit=Радактировать пользователя +users.add=Добавить пользователя + meals.title=Список еды meals.edit=Редактировать еду -common.select=Select \ No newline at end of file +common.select=Выбрать diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index dc25177311f6..d090e078c7ec 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -37,6 +37,9 @@ + + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 833a84d0b2a4..1f88a5f99d00 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,4 +1,18 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> -

+ diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index 997a1677296c..171837386e40 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp @@ -1,4 +1,8 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> -
-
+ + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 90f86d23e672..522d793362fb 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -8,5 +8,8 @@ <fmt:message key="app.title"/> ${pageContext.request.requestURL} + + + diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp index 0208cf97703b..b5cb5636d33c 100644 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ b/src/main/webapp/WEB-INF/jsp/index.jsp @@ -6,19 +6,23 @@ -
-
- : - - -
    -
  • -
  • -
-
+
+
+

+ +

+ : + + +
    +
  • +
  • +
+
+
diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index e3b6ed640305..7870f02ad5b2 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -7,31 +7,46 @@ -
-

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

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameEmailRolesActiveRegistered
${user.email}${user.roles} + checked/> + EditDelete
+
+
+
+
From 2aebda8e29f7ee1604791d5c939454379f5a7932 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 25 Jan 2016 15:35:33 +0300 Subject: [PATCH 078/108] 8_08 Ajax Datatables --- .../topjava/web/user/AdminAjaxController.java | 40 ++++++ src/main/webapp/WEB-INF/jsp/userList.jsp | 116 +++++++++++++++++- .../webapp/resources/js/datatablesUtil.js | 50 ++++++++ 3 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java create mode 100644 src/main/webapp/resources/js/datatablesUtil.js diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java new file mode 100644 index 000000000000..46eb93df8971 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java @@ -0,0 +1,40 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +/** + * User: grigory.kislin + */ +@RestController +@RequestMapping("/ajax/admin/users") +public class AdminAjaxController extends AbstractUserController { + + @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public List getAll() { + return super.getAll(); + } + + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + public void delete(@PathVariable("id") int id) { + super.delete(id); + } + + @RequestMapping(method = RequestMethod.POST) + public void createOrUpdate(@RequestParam("id") int id, + @RequestParam("name") String name, + @RequestParam("email") String email, + @RequestParam("password") String password) { + + User user = new User(id, name, email, password, Role.ROLE_USER); + if (id == 0) { + super.create(user); + } else { + super.update(user, id); + } + } +} diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 7870f02ad5b2..93e28d118f3f 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -4,6 +4,9 @@ + + + @@ -13,9 +16,9 @@

- + - +
@@ -35,11 +38,11 @@ - - + +
Name${user.roles} checked/> + checked id="${user.id}"/> EditDeleteEditDelete
@@ -48,5 +51,106 @@
+ + - + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js new file mode 100644 index 000000000000..ee9b5bc7ff9f --- /dev/null +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -0,0 +1,50 @@ +function makeEditable() { + + $('#add').click(function () { + $('#id').val(0); + $('#editRow').modal(); + }); + + $('.delete').click(function () { + deleteRow($(this).attr("id")); + }); + + $('#detailsForm').submit(function () { + save(); + return false; + }); +} + +function deleteRow(id) { + $.ajax({ + url: ajaxUrl + id, + type: 'DELETE', + success: function () { + updateTable(); + } + }); +} + +function updateTable() { + $.get(ajaxUrl, function (data) { + oTable_datatable.fnClearTable(); + $.each(data, function (key, item) { + oTable_datatable.fnAddData(item); + }); + oTable_datatable.fnDraw(); + }); +} + +function save() { + var form = $('#detailsForm'); + debugger; + $.ajax({ + type: "POST", + url: ajaxUrl, + data: form.serialize(), + success: function () { + $('#editRow').modal('hide'); + updateTable(); + } + }); +} \ No newline at end of file From dbdef0a0ab75a0920016fd22a25f41aecaa90609 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 25 Jan 2016 18:02:00 +0300 Subject: [PATCH 079/108] 8_09 Notification --- .../webapp/resources/js/datatablesUtil.js | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index ee9b5bc7ff9f..85e4e2aea454 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -13,6 +13,10 @@ function makeEditable() { save(); return false; }); + + $(document).ajaxError(function (event, jqXHR, options, jsExc) { + failNoty(event, jqXHR, options, jsExc); + }); } function deleteRow(id) { @@ -21,6 +25,7 @@ function deleteRow(id) { type: 'DELETE', success: function () { updateTable(); + successNoty('Deleted'); } }); } @@ -45,6 +50,35 @@ function save() { success: function () { $('#editRow').modal('hide'); updateTable(); + successNoty('Saved'); } }); -} \ No newline at end of file +} + +var failedNote; + +function closeNoty() { + if (failedNote) { + failedNote.close(); + failedNote = undefined; + } +} + +function successNoty(text) { + closeNoty(); + noty({ + text: text, + type: 'success', + layout: 'bottomRight', + timeout: true + }); +} + +function failNoty(event, jqXHR, options, jsExc) { + closeNoty(); + failedNote = noty({ + text: 'Failed: ' + jqXHR.statusText + "
", + type: 'error', + layout: 'bottomRight' + }); +} From 9e4820f27348862de46fb7ae90d01ffb1ede5a96 Mon Sep 17 00:00:00 2001 From: m1nd Date: Tue, 26 Jan 2016 09:48:23 +0300 Subject: [PATCH 080/108] 8_10 Add Security --- pom.xml | 13 +++++++++ src/main/resources/logback.xml | 1 + src/main/resources/spring/spring-app.xml | 1 + src/main/resources/spring/spring-security.xml | 27 +++++++++++++++++++ src/main/webapp/WEB-INF/web.xml | 9 +++++++ 5 files changed, 51 insertions(+) create mode 100644 src/main/resources/spring/spring-security.xml diff --git a/pom.xml b/pom.xml index 0f94f1473a99..63215e68c7fd 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ 2.1.4 2.3.4 2.2.4 + 4.0.3.RELEASE 1.10.9 @@ -153,6 +154,18 @@
+ + + org.springframework.security + spring-security-web + ${spring-security.version} + + + org.springframework.security + spring-security-config + ${spring-security.version} + + org.webjars diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 578254e0ffef..f5dc788ce948 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -26,6 +26,7 @@ + diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 687065f24bc9..acca9cdbbd30 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -12,6 +12,7 @@
--> + diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml new file mode 100644 index 000000000000..feb591e856b6 --- /dev/null +++ b/src/main/resources/spring/spring-security.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index abc0d013bcfc..61d21dedd284 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -36,6 +36,15 @@ / + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + encodingFilter org.springframework.web.filter.CharacterEncodingFilter From c8cc43640bc232b8a8aa36f73b924113f8f9ee91 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 30 Jan 2016 18:40:29 +0300 Subject: [PATCH 081/108] 9_01 HW8 --- config/messages/app.properties | 4 +- config/messages/app_ru.properties | 4 +- .../topjava/web/RootController.java | 7 + .../web/meal/JspUserMealController.java | 84 ------- .../web/meal/UserMealAjaxController.java | 54 ++++ src/main/webapp/WEB-INF/jsp/mealEdit.jsp | 33 --- src/main/webapp/WEB-INF/jsp/mealList.jsp | 238 ++++++++++++++---- src/main/webapp/WEB-INF/jsp/userList.jsp | 11 +- src/main/webapp/resources/css/style.css | 25 +- .../webapp/resources/js/datatablesUtil.js | 12 +- 10 files changed, 267 insertions(+), 205 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/JspUserMealController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java delete mode 100644 src/main/webapp/WEB-INF/jsp/mealEdit.jsp diff --git a/config/messages/app.properties b/config/messages/app.properties index 17de531cd703..c141beb0e890 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -8,4 +8,6 @@ users.add=Add user meals.title=Meals list meals.edit=Edit meal -common.select=Выбрать \ No newline at end of file +meals.add=Add meal + +common.select=Select \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 0bbaf3f9f875..966a612bf5f5 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -3,9 +3,11 @@ app.footer=Приложение по вебинару getAll() { + return super.getAll(); + } + + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + public void delete(@PathVariable("id") int id) { + super.delete(id); + } + + @RequestMapping(method = RequestMethod.POST) + public void updateOrCreate(@RequestParam("id") int id, + @RequestParam("datetime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime, + @RequestParam("description") String description, + @RequestParam("calories") int calories) { + UserMeal meal = new UserMeal(id, dateTime, description, calories); + if (id == 0) { + super.create(meal); + } else { + super.update(meal, id); + } + } + + @RequestMapping(value = "/filter", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + public List getBetween( + @RequestParam(value = "startDate", required = false) LocalDate startDate, @RequestParam(value = "startTime", required = false) LocalTime startTime, + @RequestParam(value = "endDate", required = false) LocalDate endDate, @RequestParam(value = "endTime", required = false) LocalTime endTime) { + return super.getBetween( + startDate != null ? startDate : TimeUtil.MIN_DATE, startTime != null ? startTime : LocalTime.MIN, + endDate != null ? endDate : TimeUtil.MAX_DATE, endTime != null ? endTime : LocalTime.MAX); + } +} \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/mealEdit.jsp b/src/main/webapp/WEB-INF/jsp/mealEdit.jsp deleted file mode 100644 index 3df0c26c6dc9..000000000000 --- a/src/main/webapp/WEB-INF/jsp/mealEdit.jsp +++ /dev/null @@ -1,33 +0,0 @@ -<%@ 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" %> - - - - -
-

Home

-

-
- -
- -
-
DateTime:
-
-
-
-
Description:
-
-
-
-
Calories:
-
-
- - -
-
- - - diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index ae3b901214af..4c05214b2633 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -4,60 +4,192 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + -
- <%--http://stackoverflow.com/questions/10327390/how-should-i-get-root-folder-path-in-jsp-page--%> -

Home

-

-
-
-
From Date:
-
-
-
-
To Date:
-
-
-
-
From Time:
-
-
-
-
To Time:
-
-
- -
-
- Add Meal -
- - - - - - - - - - - - - - - - - - - - -
DateDescriptionCalories
- <%----%> - <%----%> - <%=TimeUtil.toString(meal.getDateTime())%> - ${meal.description}${meal.calories}UpdateDelete
-
+ +
+
+
+

+ +
+ +
+
+ + +
+ +
+ + + +
+ +
+
+
+ + +
+ +
+ + + +
+ +
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
DateDescriptionCalories
+ <%----%> + <%----%> + <%=TimeUtil.toString(meal.getDateTime())%> + ${meal.description}${meal.calories}EditDelete
+
+
+
+
+ + - + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 93e28d118f3f..fd6c79ea44da 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -110,7 +110,12 @@ var oTable_datatable; var oTable_datatable_params; - // $(document).ready(function () { + function updateTable() { + $.get(ajaxUrl, function (data) { + updateTableByData(data); + }); + } + $(function () { oTable_datatable = $('#datatable'); oTable_datatable_params = { @@ -133,11 +138,11 @@ "mData": "registered" }, { - "sDefaultContent": "", + "sDefaultContent": "Edit", "bSortable": false }, { - "sDefaultContent": "", + "sDefaultContent": "Delete", "bSortable": false } ], diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css index d4807fb38efb..2c6f9fc9969d 100644 --- a/src/main/webapp/resources/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -1,20 +1,3 @@ -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; } @@ -23,10 +6,6 @@ dd { color: red; } -header, footer { - background: none repeat scroll 0 0 #A6C9E2; - color: #2E6E9E; - font-size: 20px; - padding: 5px 20px; - margin: 6px 0; +.footer { + padding-bottom: 15px; } diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index 85e4e2aea454..9981256c4f41 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -30,14 +30,12 @@ function deleteRow(id) { }); } -function updateTable() { - $.get(ajaxUrl, function (data) { - oTable_datatable.fnClearTable(); - $.each(data, function (key, item) { - oTable_datatable.fnAddData(item); - }); - oTable_datatable.fnDraw(); +function updateTableByData(data) { + oTable_datatable.fnClearTable(); + $.each(data, function (key, item) { + oTable_datatable.fnAddData(item); }); + oTable_datatable.fnDraw(); } function save() { From 6f83cdce6ae806c24adf07ccd8e7eb6d737715b0 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 30 Jan 2016 18:52:49 +0300 Subject: [PATCH 082/108] 9_02 HW8 Enable/Disable --- .../javawebinar/topjava/service/UserService.java | 2 ++ .../topjava/service/UserServiceImpl.java | 10 ++++++++++ .../topjava/web/user/AbstractUserController.java | 5 +++++ .../topjava/web/user/AdminAjaxController.java | 5 +++++ src/main/webapp/WEB-INF/jsp/mealList.jsp | 4 ++++ src/main/webapp/WEB-INF/jsp/userList.jsp | 11 ++++++++++- src/main/webapp/resources/js/datatablesUtil.js | 14 ++++++++++++++ 7 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index aa06ef3607f6..b0d9458c5716 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -26,5 +26,7 @@ public interface UserService { void evictCache(); + void enable(int id, boolean enable); + 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 2e090a8ed524..e13d600546f7 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.util.exception.ExceptionUtil; @@ -57,6 +58,15 @@ public void update(User user) { public void evictCache() { } + @CacheEvict(value = "users", allEntries = true) + @Override + @Transactional + public void enable(int id, boolean enabled) { + User user = get(id); + user.setEnabled(enabled); + repository.save(user); + } + @Override public User getWithMeals(int id) { return repository.getWithMeals(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 98e0401f4523..0ffef1d017d8 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -47,4 +47,9 @@ public User getByMail(String email) { LOG.info("getByEmail " + email); return service.getByEmail(email); } + + public void enable(int id, boolean enabled) { + LOG.info((enabled ? "enable " : "disable ") + id); + service.enable(id, enabled); + } } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java index 46eb93df8971..a8f0941cbfd4 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java @@ -37,4 +37,9 @@ public void createOrUpdate(@RequestParam("id") int id, super.update(user, id); } } + + @RequestMapping(value = "/{id}", method = RequestMethod.POST) + public void enabled(@PathVariable("id") int id, @RequestParam("enabled") boolean enabled) { + super.enable(id, enabled); + } } diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index 4c05214b2633..be1a407769ea 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -190,6 +190,10 @@ return false; }); makeEditable(); + init(); }); + + function init() { + } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index fd6c79ea44da..32f09d94fe86 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -38,7 +38,7 @@ ${user.roles} checked id="${user.id}"/> + checked id="${user.id}" onclick="enable($(this))"/> Edit @@ -156,6 +156,15 @@ oTable_datatable.dataTable(oTable_datatable_params); makeEditable(); + init(); }); + + function init(){ + $(':checkbox').each(function () { + if (!$(this).is(":checked")) { + $(this).closest('tr').css("text-decoration", "line-through"); + } + }); + } \ No newline at end of file diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index 9981256c4f41..65fee174a849 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -30,12 +30,26 @@ function deleteRow(id) { }); } +function enable(chkbox) { + var enabled = chkbox.is(":checked"); + chkbox.closest('tr').css("text-decoration", enabled ? "none" : "line-through"); + $.ajax({ + url: ajaxUrl + chkbox.attr('id'), + type: 'POST', + data: 'enabled=' + enabled, + success: function () { + successNoty(enabled ? 'Enabled' : 'Disabled'); + } + }); +} + function updateTableByData(data) { oTable_datatable.fnClearTable(); $.each(data, function (key, item) { oTable_datatable.fnAddData(item); }); oTable_datatable.fnDraw(); + init(); } function save() { From 5e146b5f4490a6f2d69e87f8b71443441cbb3921 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 30 Jan 2016 18:56:36 +0300 Subject: [PATCH 083/108] 9_03 HW8 New API rowId --- src/main/webapp/WEB-INF/jsp/mealList.jsp | 16 ++++++---------- src/main/webapp/WEB-INF/jsp/userList.jsp | 18 +++++++----------- src/main/webapp/resources/js/datatablesUtil.js | 14 +++++++------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index be1a407769ea..e26fbf9e2473 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -63,7 +63,7 @@ - + <%----%> <%----%> @@ -71,8 +71,8 @@ ${meal.description} ${meal.calories} - Edit - Delete + Edit + Delete @@ -136,9 +136,7 @@ - + \ No newline at end of file diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index 03a12de37476..81736716ebd2 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -7,14 +7,6 @@ function makeEditable() { $('#editRow').modal(); }); - $('.edit').click(function () { - updateRow($(this).closest('tr').attr("id")); - }); - - $('.delete').click(function () { - deleteRow($(this).closest('tr').attr("id")); - }); - form.submit(function () { save(); return false; @@ -45,12 +37,11 @@ function deleteRow(id) { }); } -function enable(chkbox) { +function enable(chkbox, id) { var enabled = chkbox.is(":checked"); - var row = chkbox.closest('tr'); - row.css("text-decoration", enabled ? "none" : "line-through"); + chkbox.closest('tr').css("text-decoration", enabled ? "none" : "line-through"); $.ajax({ - url: ajaxUrl + row.attr('id'), + url: ajaxUrl + id, type: 'POST', data: 'enabled=' + enabled, success: function () { @@ -60,12 +51,7 @@ function enable(chkbox) { } function updateTableByData(data) { - datatableApi.clear(); - $.each(data, function (key, item) { - datatableApi.row.add(item); - }); - datatableApi.draw(); - init(); + datatableApi.clear().rows.add(data).draw(); } function save() { @@ -108,3 +94,17 @@ function failNoty(event, jqXHR, options, jsExc) { layout: 'bottomRight' }); } + +function renderEditBtn(data, type, row) { + if (type == 'display') { + return 'Edit'; + } + return data; +} + +function renderDeleteBtn(data, type, row) { + if (type == 'display') { + return 'Delete'; + } + return data; +} diff --git a/src/main/webapp/resources/js/userDatatables.js b/src/main/webapp/resources/js/userDatatables.js new file mode 100644 index 000000000000..e8573e010ab5 --- /dev/null +++ b/src/main/webapp/resources/js/userDatatables.js @@ -0,0 +1,75 @@ +var ajaxUrl = 'ajax/admin/users/'; +var datatableApi; + +function updateTable() { + $.get(ajaxUrl, function (data) { + updateTableByData(data); + }); +} + +$(function () { + datatableApi = $('#datatable').DataTable({ + "sAjaxSource": ajaxUrl, + "sAjaxDataProp": "", + "bPaginate": false, + "bInfo": false, + "aoColumns": [ + { + "mData": "name" + }, + { + "mData": "email", + "mRender": function (data, type, row) { + if (type == 'display') { + return '' + data + ''; + } + return data; + } + }, + { + "mData": "roles" + }, + { + "mData": "enabled", + "mRender": function (data, type, row) { + if (type == 'display') { + return ''; + } + return data; + } + }, + { + "mData": "registered", + "mRender": function (date, type, row) { + if (type == 'display') { + var dateObject = new Date(date); + return '' + dateObject.toISOString().substring(0, 10) + ''; + } + return date; + } + }, + { + "bSortable": false, + "sDefaultContent": "", + "mRender": renderEditBtn + }, + { + "bSortable": false, + "sDefaultContent": "", + "mRender": renderDeleteBtn + } + ], + "aaSorting": [ + [ + 0, + "asc" + ] + ], + "createdRow": function (row, data, dataIndex) { + if (!data.enabled) { + $(row).css("text-decoration", "line-through"); + } + }, + "initComplete": makeEditable + }); +}); \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java similarity index 55% rename from src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java rename to src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java index f4fb6840231a..9570c93a6a74 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/UserControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java @@ -3,18 +3,15 @@ import org.junit.Test; import ru.javawebinar.topjava.web.AbstractControllerTest; -import static org.hamcrest.Matchers.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static ru.javawebinar.topjava.UserTestData.USER; -import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; /** * GKislin * 10.04.2015. */ -public class UserControllerTest extends AbstractControllerTest { +public class RootControllerTest extends AbstractControllerTest { @Test public void testUserList() throws Exception { @@ -22,13 +19,13 @@ public void testUserList() throws Exception { .andDo(print()) .andExpect(status().isOk()) .andExpect(view().name("userList")) - .andExpect(forwardedUrl("/WEB-INF/jsp/userList.jsp")) - .andExpect(model().attribute("userList", hasSize(2))) - .andExpect(model().attribute("userList", hasItem( - allOf( - hasProperty("id", is(START_SEQ)), - hasProperty("name", is(USER.getName())) - ) - ))); + .andExpect(forwardedUrl("/WEB-INF/jsp/userList.jsp")); +// .andExpect(model().attribute("userList", hasSize(2))) +// .andExpect(model().attribute("userList", hasItem( +// allOf( +// hasProperty("id", is(START_SEQ)), +// hasProperty("name", is(USER.getName())) +// ) +// ))); } } \ No newline at end of file From 2cdf9a6250aae489886524929cde4bb92daeab4f Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 1 Feb 2016 19:05:32 +0300 Subject: [PATCH 088/108] 9_08 Min form login --- src/main/resources/spring/spring-security.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index feb591e856b6..9df60adc1288 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -16,6 +16,14 @@ + + + + + + + + From 905b79c7d5deb935da4d1c31eafbbb3371929ba6 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 1 Feb 2016 19:16:37 +0300 Subject: [PATCH 089/108] 9_09 Jsp form login --- config/messages/app.properties | 3 +- config/messages/app_ru.properties | 3 +- .../topjava/web/RootController.java | 20 ++--- src/main/resources/spring/spring-mvc.xml | 3 + src/main/resources/spring/spring-security.xml | 7 +- .../WEB-INF/jsp/fragments/bodyHeader.jsp | 2 +- src/main/webapp/WEB-INF/jsp/index.jsp | 28 ------- src/main/webapp/WEB-INF/jsp/login.jsp | 78 +++++++++++++++++++ src/main/webapp/resources/css/style.css | 17 ++++ src/main/webapp/test.html | 17 ++++ 10 files changed, 136 insertions(+), 42 deletions(-) delete mode 100644 src/main/webapp/WEB-INF/jsp/index.jsp create mode 100644 src/main/webapp/WEB-INF/jsp/login.jsp create mode 100644 src/main/webapp/test.html diff --git a/config/messages/app.properties b/config/messages/app.properties index c141beb0e890..59d8a5bd861b 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -1,6 +1,7 @@ app.title=Calories management app.footer=Project Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery application -app.login=Login as +app.login=Login +app.logout=Logout users.title=User list users.edit=Edit user diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 966a612bf5f5..4be50246aa2c 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -1,6 +1,7 @@ app.title=Подсчет калорий app.footer=Приложение по вебинару Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery -app.login=Зайти как +app.login=Войти +app.logout=Выйти users.title=Список пользователей users.edit=Редактировать пользователя diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 37b4bdc9eda6..92f1dd9bc133 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -3,15 +3,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.service.UserMealService; -import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.util.UserMealsUtil; -import javax.servlet.http.HttpServletRequest; - /** * User: gkislin * Date: 22.08.2014 @@ -24,7 +23,7 @@ public class RootController { @RequestMapping(value = "/", method = RequestMethod.GET) public String root() { - return "index"; + return "redirect:meals"; } @RequestMapping(value = "/users", method = RequestMethod.GET) @@ -38,10 +37,13 @@ public String mealList(Model model) { return "mealList"; } - @RequestMapping(value = "/users", method = RequestMethod.POST) - public String setUser(HttpServletRequest request) { - int userId = Integer.valueOf(request.getParameter("userId")); - LoggedUser.setId(userId); - return "redirect:meals"; + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String login(ModelMap model, + @RequestParam(value = "error", required = false) boolean error, + @RequestParam(value = "message", required = false) String message) { + + model.put("error", error); + model.put("message", message); + return "login"; } } diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index d090e078c7ec..e05da0366f11 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -7,6 +7,9 @@ + + + diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index 9df60adc1288..7c0661b0e2c5 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -17,10 +17,13 @@ + - - + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 1f88a5f99d00..562746dc869c 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -11,7 +11,7 @@ diff --git a/src/main/webapp/WEB-INF/jsp/index.jsp b/src/main/webapp/WEB-INF/jsp/index.jsp deleted file mode 100644 index b5cb5636d33c..000000000000 --- a/src/main/webapp/WEB-INF/jsp/index.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%@ 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" %> - - - - - -
-
-

- -

- : - - -
    -
  • -
  • -
-
-
- - - diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp new file mode 100644 index 000000000000..287d89627dae --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/login.jsp @@ -0,0 +1,78 @@ +<%@ 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" %> + + + + + + +
+
+ +
+ ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message} +
+
+ +
+ +
+
+

+ +

User login: user@yandex.ru / password

+ +

Admin login: admin@gmail.com / admin

+ +

Стек технологий: Spring Security, + Spring MVC, + Spring Data JPA, + Spring Security + Test, + Hibernate ORM, + Hibernate Validator, + SLF4J, + Json Jackson, + JSP, + JSTL, + Apache Tomcat, + WebJars, + DataTables plugin, + Ehcache, + PostgreSQL, + JUnit, + Hamcrest, + jQuery, + jQuery notification, + Bootstrap.

+
+
+
+
+    Java Enterprise проект с регистрацией/авторизацией и интерфейсом на основе ролей (USER, ADMIN). + Администратор может создавать/редактировать/удалять/пользователей, а пользователь - управлять своим + профилем и данными (день, еда, калории) через UI (по AJAX) и по REST интерфейсу с базовой авторизацией. + Возможна фильтрация данных по датам и времени, при этом цвет записи таблицы еды зависит от того, превышает ли сумма + калорий за день норму (редактируемый параметр в профиле пользователя). + Весь REST интерфейс покрывается JUnit тестами, используя Spring MVC Test и Spring Security Test. +
+
+ + + \ No newline at end of file diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css index 2c6f9fc9969d..7aa0d26a0a7a 100644 --- a/src/main/webapp/resources/css/style.css +++ b/src/main/webapp/resources/css/style.css @@ -9,3 +9,20 @@ .footer { padding-bottom: 15px; } + +.error, .message { + padding: 10px; + margin-bottom: 20px; + border-radius: 4px; + font-size: 16px; +} +.error { + color: #a94442; + background-color: #f2dede; + border: 1px solid #ebccd1; +} +.message { + color: #2f9635; + background-color: #c6fbc2; + border: 1px solid #9feba6; +} \ No newline at end of file diff --git a/src/main/webapp/test.html b/src/main/webapp/test.html new file mode 100644 index 000000000000..60423f8ed4a1 --- /dev/null +++ b/src/main/webapp/test.html @@ -0,0 +1,17 @@ + + + + + +
+
+
+
User list:
+
users
+
Meal list:
+
meals
+
+
+
+ + \ No newline at end of file From 57a43430233c5534915681ab0fdddc692723e22a Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 1 Feb 2016 19:34:00 +0300 Subject: [PATCH 090/108] 9_10 Auth via user service --- .../ru/javawebinar/topjava/LoggedUser.java | 46 ++++++++++++++++--- .../ru/javawebinar/topjava/model/Role.java | 11 ++++- .../topjava/service/UserServiceImpl.java | 16 ++++++- .../ru/javawebinar/topjava/to/UserTo.java | 18 +++++++- .../ru/javawebinar/topjava/util/UserUtil.java | 4 ++ src/main/resources/spring/spring-security.xml | 18 ++++++-- 6 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java index 5e0da30b9318..2c83b14500a3 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggedUser.java +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -1,24 +1,56 @@ package ru.javawebinar.topjava; -import ru.javawebinar.topjava.model.BaseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; +import static java.util.Objects.requireNonNull; + /** * GKislin * 06.03.2015. + *

+ * Mock implementation */ -public class LoggedUser { - public static int id = BaseEntity.START_SEQ; +public class LoggedUser extends org.springframework.security.core.userdetails.User { + private final UserTo userTo; + + public LoggedUser(User user) { + super(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, user.getRoles()); + this.userTo = UserUtil.asTo(user); + } + + public static LoggedUser safeGet() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null) { + return null; + } + Object user = auth.getPrincipal(); + return (user instanceof LoggedUser) ? (LoggedUser) user : null; + } + + public static LoggedUser get() { + LoggedUser user = safeGet(); + requireNonNull(user, "No authorized user found"); + return user; + } + + public UserTo getUserTo() { + return userTo; + } public static int id() { - return id; + return get().userTo.getId(); } - public static void setId(int id) { - LoggedUser.id = id; + @Override + public String toString() { + return userTo.toString(); } public static int getCaloriesPerDay() { - return UserUtil.DEFAULT_CALORIES_PER_DAY; + return get().userTo.getCaloriesPerDay(); } } diff --git a/src/main/java/ru/javawebinar/topjava/model/Role.java b/src/main/java/ru/javawebinar/topjava/model/Role.java index f0de2b4c4a81..9c5210417f0b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Role.java +++ b/src/main/java/ru/javawebinar/topjava/model/Role.java @@ -1,10 +1,17 @@ package ru.javawebinar.topjava.model; +import org.springframework.security.core.GrantedAuthority; + /** * User: gkislin * Date: 22.08.2014 */ -public enum Role { +public enum Role implements GrantedAuthority { ROLE_USER, - ROLE_ADMIN + ROLE_ADMIN; + + @Override + public String getAuthority() { + return name(); + } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index c4534e715c9a..614c06abce92 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -3,8 +3,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.to.UserTo; @@ -20,8 +23,8 @@ * GKislin * 06.03.2015. */ -@Service -public class UserServiceImpl implements UserService { +@Service("userService") +public class UserServiceImpl implements UserService, UserDetailsService { @Autowired private UserRepository repository; @@ -83,6 +86,15 @@ public void enable(int id, boolean enabled) { repository.save(user); } + @Override + public LoggedUser loadUserByUsername(String email) throws UsernameNotFoundException { + User u = repository.getByEmail(email); + if (u == null) { + throw new UsernameNotFoundException("User " + email + " is not found"); + } + return new LoggedUser(u); + } + @Override public User getWithMeals(int id) { return repository.getWithMeals(id); diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index 066b71286a22..f1e4182af93d 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -2,10 +2,14 @@ import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; +import org.hibernate.validator.constraints.Range; +import ru.javawebinar.topjava.util.UserUtil; +import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import java.io.Serializable; -public class UserTo { +public class UserTo implements Serializable { protected int id; @NotEmpty @@ -18,14 +22,19 @@ public class UserTo { @Size(min = 5, max = 64, message = " must between 5 and 64 characters") protected String password; + @Range(min = 500, max = 5000) + @NotNull(message = " must not be empty") + protected Integer caloriesPerDay = UserUtil.DEFAULT_CALORIES_PER_DAY; + public UserTo() { } - public UserTo(int id, String name, String email, String password) { + public UserTo(int id, String name, String email, String password, int caloriesPerDay) { this.id = id; this.name = name; this.email = email; this.password = password; + this.caloriesPerDay = caloriesPerDay; } public void setId(int id) { @@ -64,12 +73,17 @@ public String getEmail() { return email; } + public int getCaloriesPerDay() { + return caloriesPerDay; + } + @Override public String toString() { return "UserTo{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + + ", caloriesPerDay='" + caloriesPerDay + '\'' + '}'; } } diff --git a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java index fc4298698cb1..723f1645c97b 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -15,6 +15,10 @@ public static User createFromTo(UserTo newUser) { return new User(null, newUser.getName(), newUser.getEmail().toLowerCase(), newUser.getPassword(), Role.ROLE_USER); } + public static UserTo asTo(User user) { + return new UserTo(user.getId(), user.getName(), user.getEmail(), user.getPassword(), user.getCaloriesPerDay()); + } + public static User updateFromTo(User user, UserTo userTo) { user.setName(userTo.getName()); user.setEmail(userTo.getEmail().toLowerCase()); diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index 7c0661b0e2c5..d5a58b7255c5 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -28,11 +28,19 @@ - - - - - + + + \ No newline at end of file From cc94badb116e8e4a3129eae1c3d0f79b4e417a0c Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 1 Feb 2016 21:27:22 +0300 Subject: [PATCH 091/108] 9_11 Spring Security test --- pom.xml | 5 ++++ .../java/ru/javawebinar/topjava/TestUtil.java | 7 ++++++ .../topjava/web/AbstractControllerTest.java | 2 ++ .../web/user/AdminRestControllerTest.java | 23 +++++++++++++++---- .../web/user/ProfileRestControllerTest.java | 16 +++++++++---- .../topjava/web/user/RootControllerTest.java | 5 ++-- 6 files changed, 46 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 63215e68c7fd..29c6b0202a46 100644 --- a/pom.xml +++ b/pom.xml @@ -276,6 +276,11 @@ ${spring.version} test + + org.springframework.security + spring-security-test + ${spring-security.version} + diff --git a/src/test/java/ru/javawebinar/topjava/TestUtil.java b/src/test/java/ru/javawebinar/topjava/TestUtil.java index bc9aa67c4897..4bcbc18caca8 100644 --- a/src/test/java/ru/javawebinar/topjava/TestUtil.java +++ b/src/test/java/ru/javawebinar/topjava/TestUtil.java @@ -1,7 +1,10 @@ package ru.javawebinar.topjava; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import ru.javawebinar.topjava.matcher.ModelMatcher; +import ru.javawebinar.topjava.model.User; import java.io.UnsupportedEncodingException; @@ -20,6 +23,10 @@ public static String getContent(ResultActions action) throws UnsupportedEncoding return action.andReturn().getResponse().getContentAsString(); } + public static RequestPostProcessor userHttpBasic(User user) { + return SecurityMockMvcRequestPostProcessors.httpBasic(user.getEmail(), user.getPassword()); + } + /** * Compare entities using toString */ diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java index ebf46a864743..0ceb320c4aa3 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -16,6 +16,7 @@ import javax.annotation.PostConstruct; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static ru.javawebinar.topjava.Profiles.DATAJPA; import static ru.javawebinar.topjava.Profiles.POSTGRES; @@ -54,6 +55,7 @@ void postConstruct() { mockMvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) .addFilter(CHARACTER_ENCODING_FILTER) + .apply(springSecurity()) .build(); } diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 40dd88499da4..148ebf44566e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -16,6 +16,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static ru.javawebinar.topjava.TestUtil.userHttpBasic; import static ru.javawebinar.topjava.UserTestData.*; public class AdminRestControllerTest extends AbstractControllerTest { @@ -24,7 +25,8 @@ public class AdminRestControllerTest extends AbstractControllerTest { @Test public void testGet() throws Exception { - mockMvc.perform(get(REST_URL + ADMIN_ID)) + mockMvc.perform(get(REST_URL + ADMIN_ID) + .with(userHttpBasic(ADMIN))) .andExpect(status().isOk()) .andDo(print()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -33,27 +35,36 @@ public void testGet() throws Exception { @Test public void testGetByEmail() throws Exception { - mockMvc.perform(get(REST_URL + "by?email=" + USER.getEmail())) + mockMvc.perform(get(REST_URL + "by?email=" + ADMIN.getEmail()) + .with(TestUtil.userHttpBasic(ADMIN))) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(MATCHER.contentMatcher(USER)); + .andExpect(MATCHER.contentMatcher(ADMIN)); } @Test public void testDelete() throws Exception { - mockMvc.perform(delete(REST_URL + USER_ID).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(delete(REST_URL + USER_ID).contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(ADMIN))) .andDo(print()) .andExpect(status().isOk()); MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), userService.getAll()); } @Test + public void testGetUnauth() throws Exception { + mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(USER))) + .andExpect(status().isForbidden()); + } + public void testUpdate() throws Exception { User updated = new User(USER); updated.setName("UpdatedName"); updated.setRoles(Collections.singletonList(Role.ROLE_ADMIN)); mockMvc.perform(put(REST_URL + USER_ID) .contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(ADMIN)) .content(JsonUtil.writeValue(updated))) .andExpect(status().isOk()); @@ -65,6 +76,7 @@ public void testCreate() throws Exception { TestUser expected = new TestUser("New", "new@gmail.com", "newPass", Role.ROLE_USER, Role.ROLE_ADMIN); ResultActions action = mockMvc.perform(post(REST_URL) .contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(ADMIN)) .content(JsonUtil.writeValue(expected.asUser()))).andExpect(status().isCreated()); User returned = MATCHER.fromJsonAction(action); @@ -76,7 +88,8 @@ public void testCreate() throws Exception { @Test public void testGetAll() throws Exception { - TestUtil.print(mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON)) + TestUtil.print(mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(ADMIN))) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(MATCHER.contentListMatcher(ADMIN, USER))); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 1754e5c1bdd2..3a3d6f42c6b1 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -2,7 +2,6 @@ import org.junit.Test; import org.springframework.http.MediaType; -import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.TestUtil; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; @@ -22,23 +21,32 @@ public class ProfileRestControllerTest extends AbstractControllerTest { @Test public void testGet() throws Exception { - TestUtil.print(mockMvc.perform(get(REST_URL)) + TestUtil.print(mockMvc.perform(get(REST_URL) + .with(TestUtil.userHttpBasic(USER))) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(MATCHER.contentMatcher(USER))); } + @Test + public void testGetUnauth() throws Exception { + mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()); + } + @Test public void testDelete() throws Exception { - mockMvc.perform(delete(REST_URL).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(delete(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(USER))) .andExpect(status().isOk()); MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), userService.getAll()); } @Test public void testUpdate() throws Exception { - User updated = new User(LoggedUser.id(), "newName", "newEmail", "newPassword", Role.ROLE_USER); + User updated = new User(USER_ID, "newName", "newEmail", "newPassword", Role.ROLE_USER); mockMvc.perform(put(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(TestUtil.userHttpBasic(USER)) .content(JsonUtil.writeValue(updated))) .andDo(print()) .andExpect(status().isOk()); diff --git a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java index 9570c93a6a74..c226125d66e7 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java @@ -17,9 +17,8 @@ public class RootControllerTest extends AbstractControllerTest { public void testUserList() throws Exception { mockMvc.perform(get("/users")) .andDo(print()) - .andExpect(status().isOk()) - .andExpect(view().name("userList")) - .andExpect(forwardedUrl("/WEB-INF/jsp/userList.jsp")); + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login")); // .andExpect(model().attribute("userList", hasSize(2))) // .andExpect(model().attribute("userList", hasItem( // allOf( From 5950c0e169b2c1979b770cb53e48817ad67da09b Mon Sep 17 00:00:00 2001 From: m1nd Date: Wed, 10 Feb 2016 20:51:05 +0300 Subject: [PATCH 092/108] 10_00 Update jQuery Noty --- pom.xml | 4 ++-- src/main/webapp/WEB-INF/jsp/mealList.jsp | 2 +- src/main/webapp/WEB-INF/jsp/userList.jsp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 29c6b0202a46..fcdb5596e89c 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 3.3.5 2.1.4 2.3.4 - 2.2.4 + 2.3.7 4.0.3.RELEASE 1.10.9 @@ -183,7 +183,7 @@ ${webjars-datetimepicker.version} - org.webjars + org.webjars.bower noty ${webjars-noty.version} diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index e26fbf9e2473..d25ab4e5cb27 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -132,7 +132,7 @@ - + - + \ No newline at end of file From 6993af57ba5f43512f2ee3d574c9178860eb811c Mon Sep 17 00:00:00 2001 From: m1nd Date: Wed, 10 Feb 2016 22:02:47 +0300 Subject: [PATCH 093/108] 10_01 HW9 AJAX --- .../javawebinar/topjava/model/UserMeal.java | 15 ++-- .../topjava/web/RootController.java | 11 +-- .../web/meal/UserMealAjaxController.java | 27 ++++--- src/main/webapp/WEB-INF/jsp/mealList.jsp | 80 +------------------ .../webapp/resources/js/mealDatatables.js | 69 ++++++++++++++++ .../webapp/resources/js/userDatatables.js | 40 +++++----- 6 files changed, 118 insertions(+), 124 deletions(-) create mode 100644 src/main/webapp/resources/js/mealDatatables.js diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java index e1423822dbd3..6bb5472a97f1 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -2,6 +2,7 @@ import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.Range; +import org.springframework.format.annotation.DateTimeFormat; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -32,6 +33,7 @@ public class UserMeal extends BaseEntity { @Column(name = "date_time", nullable = false) @NotNull + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) protected LocalDateTime dateTime; @Column(name = "description", nullable = false) @@ -40,7 +42,8 @@ public class UserMeal extends BaseEntity { @Column(name = "calories", nullable = false) @Range(min = 10, max = 5000) - protected int calories; + @NotNull + protected Integer calories; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) @@ -60,10 +63,6 @@ public UserMeal(Integer id, LocalDateTime dateTime, String description, int calo this.calories = calories; } - public void setId(int id) { - this.id = id; - } - public LocalDateTime getDateTime() { return dateTime; } @@ -76,10 +75,6 @@ public int getCalories() { return calories; } - public Integer getId() { - return id; - } - public void setDateTime(LocalDateTime dateTime) { this.dateTime = dateTime; } @@ -88,7 +83,7 @@ public void setDescription(String description) { this.description = description; } - public void setCalories(int calories) { + public void setCalories(Integer calories) { this.calories = calories; } diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 92f1dd9bc133..bfa036577e1c 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -1,15 +1,10 @@ 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.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import ru.javawebinar.topjava.LoggedUser; -import ru.javawebinar.topjava.service.UserMealService; -import ru.javawebinar.topjava.util.UserMealsUtil; /** * User: gkislin @@ -18,9 +13,6 @@ @Controller public class RootController { - @Autowired - private UserMealService mealService; - @RequestMapping(value = "/", method = RequestMethod.GET) public String root() { return "redirect:meals"; @@ -32,8 +24,7 @@ public String userList() { } @RequestMapping(value = "/meals",method = RequestMethod.GET) - public String mealList(Model model) { - model.addAttribute("mealList", UserMealsUtil.getWithExceeded(mealService.getAll(LoggedUser.id()), LoggedUser.getCaloriesPerDay())); + public String mealList() { return "mealList"; } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java index b39b9cc583f3..cef70f8256a3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java @@ -1,14 +1,16 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; +import javax.validation.Valid; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; @@ -25,22 +27,29 @@ public List getAll() { return super.getAll(); } + @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public UserMeal get(@PathVariable("id") int id) { + return super.get(id); + } + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public void delete(@PathVariable("id") int id) { super.delete(id); } @RequestMapping(method = RequestMethod.POST) - public void updateOrCreate(@RequestParam("id") int id, - @RequestParam("datetime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime, - @RequestParam("description") String description, - @RequestParam("calories") int calories) { - UserMeal meal = new UserMeal(id, dateTime, description, calories); - if (id == 0) { + public ResponseEntity updateOrCreate(@Valid UserMeal meal, BindingResult result) { + if (result.hasErrors()) { + StringBuilder sb = new StringBuilder(); + result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); + return new ResponseEntity<>(sb.toString(), HttpStatus.UNPROCESSABLE_ENTITY); + } + if (meal.getId() == 0) { super.create(meal); } else { - super.update(meal, id); + super.update(meal, meal.getId()); } + return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/filter", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index d25ab4e5cb27..c1170599980b 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -1,4 +1,3 @@ -<%@ page import="ru.javawebinar.topjava.util.TimeUtil" %> <%@ 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" %> @@ -61,20 +60,6 @@ - - - - - <%----%> - <%----%> - <%=TimeUtil.toString(meal.getDateTime())%> - - ${meal.description} - ${meal.calories} - Edit - Delete - - @@ -94,11 +79,11 @@

- +
- +
@@ -134,62 +119,5 @@ - + \ No newline at end of file diff --git a/src/main/webapp/resources/js/mealDatatables.js b/src/main/webapp/resources/js/mealDatatables.js new file mode 100644 index 000000000000..df34b98cfcec --- /dev/null +++ b/src/main/webapp/resources/js/mealDatatables.js @@ -0,0 +1,69 @@ +var ajaxUrl = 'ajax/profile/meals/'; +var datatableApi; + +function updateTable() { + $.ajax({ + type: "POST", + url: ajaxUrl + 'filter', + data: $('#filter').serialize(), + success: function (data) { + updateTableByData(data); + } + }); + return false; +} + +$(function () { + datatableApi = $('#datatable').DataTable({ + "ajax": { + "url": ajaxUrl, + "dataSrc": "" + }, + "paging": false, + "info": true, + "columns": [ + { + "data": "dateTime", + "render": function (date, type, row) { + if (type == 'display') { + return date.replace('T', ' '); + } + return date; + } + }, + { + "data": "description" + }, + { + "data": "calories" + }, + { + "defaultContent": "", + "orderable": false, + "render": renderEditBtn + }, + { + "defaultContent": "", + "orderable": false, + "render": renderDeleteBtn + + } + ], + "order": [ + [ + 0, + "desc" + ] + ], + "createdRow": function (row, data, dataIndex) { + $(row).addClass(data.exceed ? 'exceeded' : 'normal'); + }, + "initComplete": function () { + $('#filter').submit(function () { + updateTable(); + return false; + }); + makeEditable(); + } + }); +}); diff --git a/src/main/webapp/resources/js/userDatatables.js b/src/main/webapp/resources/js/userDatatables.js index e8573e010ab5..a3cdc5533627 100644 --- a/src/main/webapp/resources/js/userDatatables.js +++ b/src/main/webapp/resources/js/userDatatables.js @@ -9,17 +9,19 @@ function updateTable() { $(function () { datatableApi = $('#datatable').DataTable({ - "sAjaxSource": ajaxUrl, - "sAjaxDataProp": "", - "bPaginate": false, - "bInfo": false, - "aoColumns": [ + "ajax": { + "url": ajaxUrl, + "dataSrc": "" + }, + "paging": false, + "info": true, + "columns": [ { - "mData": "name" + "data": "name" }, { - "mData": "email", - "mRender": function (data, type, row) { + "data": "email", + "render": function (data, type, row) { if (type == 'display') { return '' + data + ''; } @@ -27,11 +29,11 @@ $(function () { } }, { - "mData": "roles" + "data": "roles" }, { - "mData": "enabled", - "mRender": function (data, type, row) { + "data": "enabled", + "render": function (data, type, row) { if (type == 'display') { return ''; } @@ -39,8 +41,8 @@ $(function () { } }, { - "mData": "registered", - "mRender": function (date, type, row) { + "data": "registered", + "render": function (date, type, row) { if (type == 'display') { var dateObject = new Date(date); return '' + dateObject.toISOString().substring(0, 10) + ''; @@ -49,17 +51,17 @@ $(function () { } }, { - "bSortable": false, + "orderable": false, "sDefaultContent": "", - "mRender": renderEditBtn + "render": renderEditBtn }, { - "bSortable": false, - "sDefaultContent": "", - "mRender": renderDeleteBtn + "orderable": false, + "defaultContent": "", + "render": renderDeleteBtn } ], - "aaSorting": [ + "order": [ [ 0, "asc" From 13118d899d9f79a0282d05d48a340f24e251aaf6 Mon Sep 17 00:00:00 2001 From: m1nd Date: Wed, 10 Feb 2016 22:06:28 +0300 Subject: [PATCH 094/108] 10_02 HW9 Test --- pom.xml | 9 +++- .../web/meal/UserMealControllerTest.java | 34 --------------- .../web/meal/UserMealRestControllerTest.java | 42 +++++++++++++------ .../topjava/web/user/RootControllerTest.java | 8 ++++ 4 files changed, 45 insertions(+), 48 deletions(-) delete mode 100644 src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java diff --git a/pom.xml b/pom.xml index fcdb5596e89c..fd2089f71abe 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,14 @@ - + + org.apache.maven.plugins + maven-surefire-plugin + 2.17 + + -Dfile.encoding=UTF-8 + + diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java deleted file mode 100644 index 3c0e72aabe55..000000000000 --- a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealControllerTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package ru.javawebinar.topjava.web.meal; - -import org.junit.Test; -import ru.javawebinar.topjava.web.AbstractControllerTest; - -import static org.hamcrest.Matchers.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static ru.javawebinar.topjava.MealTestData.MEAL1; -import static ru.javawebinar.topjava.MealTestData.MEAL1_ID; - -/** - * GKislin - * 10.04.2015. - */ -public class UserMealControllerTest extends AbstractControllerTest { - - @Test - public void testMealList() throws Exception { - mockMvc.perform(get("/meals")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(view().name("mealList")) - .andExpect(forwardedUrl("/WEB-INF/jsp/mealList.jsp")) - .andExpect(model().attribute("mealList", hasSize(6))) - .andExpect(model().attribute("mealList", hasItem( - allOf( - hasProperty("id", is(MEAL1_ID)), - hasProperty("description", is(MEAL1.getDescription())) - ) - ))); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java index 124a7f147b83..b3b7596bb92f 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java @@ -4,7 +4,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; -import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.service.UserMealService; import ru.javawebinar.topjava.util.UserMealsUtil; @@ -19,6 +18,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static ru.javawebinar.topjava.MealTestData.*; +import static ru.javawebinar.topjava.MealTestData.MATCHER; +import static ru.javawebinar.topjava.TestUtil.userHttpBasic; +import static ru.javawebinar.topjava.UserTestData.*; import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; public class UserMealRestControllerTest extends AbstractControllerTest { @@ -30,16 +32,24 @@ public class UserMealRestControllerTest extends AbstractControllerTest { @Test public void testGet() throws Exception { - mockMvc.perform(get(REST_URL + MEAL1_ID)) + mockMvc.perform(get(REST_URL + ADMIN_MEAL_ID) + .with(userHttpBasic(ADMIN))) .andExpect(status().isOk()) .andDo(print()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(MATCHER.contentMatcher(MEAL1)); + .andExpect(MATCHER.contentMatcher(ADMIN_MEAL)); + } + + @Test + public void testGetUnauth() throws Exception { + mockMvc.perform(get(REST_URL + MEAL1_ID)) + .andExpect(status().isUnauthorized()); } @Test public void testDelete() throws Exception { - mockMvc.perform(delete(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(delete(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(USER))) .andExpect(status().isOk()); MATCHER.assertCollectionEquals(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2), service.getAll(START_SEQ)); } @@ -49,7 +59,8 @@ public void testUpdate() throws Exception { UserMeal updated = getUpdated(); mockMvc.perform(put(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(updated))) + .content(JsonUtil.writeValue(updated)) + .with(userHttpBasic(USER))) .andExpect(status().isOk()); assertEquals(updated, service.get(MEAL1_ID, START_SEQ)); @@ -60,27 +71,30 @@ public void testCreate() throws Exception { UserMeal created = getCreated(); ResultActions action = mockMvc.perform(post(REST_URL) .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(created))); + .content(JsonUtil.writeValue(created)) + .with(userHttpBasic(ADMIN))); UserMeal returned = MATCHER.fromJsonAction(action); created.setId(returned.getId()); MATCHER.assertEquals(created, returned); - MATCHER.assertCollectionEquals(Arrays.asList(created, MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), service.getAll(START_SEQ)); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN_MEAL2, created, ADMIN_MEAL), service.getAll(ADMIN_ID)); } @Test public void testGetAll() throws Exception { - mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(USER))) .andExpect(status().isOk()) .andDo(print()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(MATCHER_WITH_EXCEED.contentListMatcher(UserMealsUtil.getWithExceeded(USER_MEALS, LoggedUser.getCaloriesPerDay()))); + .andExpect(MATCHER_WITH_EXCEED.contentListMatcher(UserMealsUtil.getWithExceeded(USER_MEALS, USER.getCaloriesPerDay()))); } @Test public void testGetBetween() throws Exception { - mockMvc.perform(get(REST_URL + "between?startDateTime=2015-05-30T07:00&endDateTime=2015-05-31T11:00:00")) + mockMvc.perform(get(REST_URL + "between?startDateTime=2015-05-30T07:00&endDateTime=2015-05-31T11:00:00") + .with(userHttpBasic(USER))) .andExpect(status().isOk()) .andDo(print()) .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( @@ -90,7 +104,8 @@ public void testGetBetween() throws Exception { @Test public void testFilter() throws Exception { - mockMvc.perform(get(REST_URL + "filter?startDate=2015-05-30&startTime=07:00&endDate=2015-05-31&endTime=11:00")) + mockMvc.perform(get(REST_URL + "filter?startDate=2015-05-30&startTime=07:00&endDate=2015-05-31&endTime=11:00") + .with(userHttpBasic(USER))) .andExpect(status().isOk()) .andDo(print()) .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( @@ -100,10 +115,11 @@ public void testFilter() throws Exception { @Test public void testFilterAll() throws Exception { - mockMvc.perform(get(REST_URL + "filter?startDate=&endTime=")) + mockMvc.perform(get(REST_URL + "filter?startDate=&endTime=") + .with(userHttpBasic(USER))) .andExpect(status().isOk()) .andDo(print()) .andExpect(MATCHER_WITH_EXCEED.contentListMatcher( - UserMealsUtil.getWithExceeded(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), LoggedUser.getCaloriesPerDay()))); + UserMealsUtil.getWithExceeded(Arrays.asList(MEAL6, MEAL5, MEAL4, MEAL3, MEAL2, MEAL1), USER.getCaloriesPerDay()))); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java index c226125d66e7..91e768ca3449 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java @@ -27,4 +27,12 @@ public void testUserList() throws Exception { // ) // ))); } + + @Test + public void testMealList() throws Exception { + mockMvc.perform(get("/meals")) + .andDo(print()) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("http://localhost/login")); + } } \ No newline at end of file From be2b964e081717441d9157b5959f3f4ef9e76a3c Mon Sep 17 00:00:00 2001 From: m1nd Date: Wed, 10 Feb 2016 22:09:00 +0300 Subject: [PATCH 095/108] 10_03 HW9 dateTimePicker --- src/main/webapp/WEB-INF/jsp/mealList.jsp | 11 +++--- .../webapp/resources/js/mealDatatables.js | 37 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index c1170599980b..036049caaf84 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -21,26 +21,26 @@
- +
- +
- +
- +
@@ -82,8 +82,7 @@
- +
diff --git a/src/main/webapp/resources/js/mealDatatables.js b/src/main/webapp/resources/js/mealDatatables.js index df34b98cfcec..7fd26342e0e2 100644 --- a/src/main/webapp/resources/js/mealDatatables.js +++ b/src/main/webapp/resources/js/mealDatatables.js @@ -63,6 +63,43 @@ $(function () { updateTable(); return false; }); + + var startDate = $('#startDate'); + var endDate = $('#endDate'); + startDate.datetimepicker({ + timepicker: false, + format: 'Y-m-d', + lang: 'ru', + formatDate: 'Y-m-d', + onShow: function (ct) { + this.setOptions({ + maxDate: endDate.val() ? endDate.val() : false + }) + } + }); + endDate.datetimepicker({ + timepicker: false, + format: 'Y-m-d', + lang: 'ru', + formatDate: 'Y-m-d', + onShow: function (ct) { + this.setOptions({ + minDate: startDate.val() ? startDate.val() : false + }) + } + }); + + $('.time-picker').datetimepicker({ + datepicker: false, + format: 'H:i', + lang: 'ru' + }); + + $('#dateTime').datetimepicker({ + format: 'Y-m-d\\TH:i', + lang: 'ru' + }); + makeEditable(); } }); From 09cfc32f97d8e3c5f4cc4c0b62bcd4e8d274d493 Mon Sep 17 00:00:00 2001 From: m1nd Date: Thu, 11 Feb 2016 15:27:48 +0300 Subject: [PATCH 096/108] 10_04 Secure tag annotation --- pom.xml | 5 +++++ .../javawebinar/topjava/web/RootController.java | 3 +++ src/main/resources/spring/spring-mvc.xml | 6 ++++-- .../webapp/WEB-INF/jsp/fragments/bodyHeader.jsp | 16 ++++++++++------ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index fd2089f71abe..4dd00e8283e6 100644 --- a/pom.xml +++ b/pom.xml @@ -248,6 +248,11 @@ hibernate-ehcache ${hibernate.version} + + org.springframework.security + spring-security-taglibs + ${spring-security.version} + diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index bfa036577e1c..73d67cdf1e77 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,6 +19,8 @@ public String root() { return "redirect:meals"; } + // @Secured("ROLE_ADMIN") + @PreAuthorize("hasRole('ROLE_ADMIN')") @RequestMapping(value = "/users", method = RequestMethod.GET) public String userList() { return "userList"; diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index e05da0366f11..f56a81fe8ec1 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -1,15 +1,17 @@ + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 562746dc869c..d4d9304dbafb 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,18 +1,22 @@ <%@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" %> +<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +
\ No newline at end of file From 6fa3e8614b64954cf9e098dac4805f56f42dae4a Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 08:04:47 +0300 Subject: [PATCH 097/108] 10_05 Interceptor --- .../web/interceptor/ModelInterceptor.java | 24 +++++++++++++++++++ src/main/resources/spring/spring-mvc.xml | 4 ++++ .../WEB-INF/jsp/fragments/bodyHeader.jsp | 3 ++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java diff --git a/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java b/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java new file mode 100644 index 000000000000..84f99195eae2 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/interceptor/ModelInterceptor.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.web.interceptor; + +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import ru.javawebinar.topjava.LoggedUser; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * This interceptor adds the user to the model of every requests managed + */ +public class ModelInterceptor extends HandlerInterceptorAdapter { + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + if (modelAndView != null && !modelAndView.isEmpty() && modelAndView.getModelMap().get("user") == null) { + LoggedUser loggedUser = LoggedUser.safeGet(); + if (loggedUser != null) { + modelAndView.getModelMap().addAttribute("userTo", loggedUser.getUserTo()); + } + } + } +} diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index f56a81fe8ec1..0db53900a95c 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -12,6 +12,10 @@ + + + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index d4d9304dbafb..3a0f7cd45211 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,6 +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" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> @@ -14,6 +14,7 @@ + ${userTo.name} profile Logout From 3000f6f8a6b4d9864d13bcd672d5c55cf80cb621 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 08:09:14 +0300 Subject: [PATCH 098/108] 10_06 Profile JSPTag --- .../ru/javawebinar/topjava/LoggedUser.java | 6 ++- .../topjava/service/UserServiceImpl.java | 5 +-- .../ru/javawebinar/topjava/to/UserTo.java | 6 ++- .../ru/javawebinar/topjava/util/UserUtil.java | 1 + .../topjava/web/RootController.java | 27 +++++++++++- .../web/user/ProfileRestController.java | 6 ++- src/main/webapp/WEB-INF/jsp/profile.jsp | 41 +++++++++++++++++++ src/main/webapp/WEB-INF/tags/inputField.tag | 23 +++++++++++ .../web/user/ProfileRestControllerTest.java | 10 +++-- 9 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 src/main/webapp/WEB-INF/jsp/profile.jsp create mode 100644 src/main/webapp/WEB-INF/tags/inputField.tag diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java index 2c83b14500a3..39c36182d379 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggedUser.java +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -15,7 +15,7 @@ * Mock implementation */ public class LoggedUser extends org.springframework.security.core.userdetails.User { - private final UserTo userTo; + private UserTo userTo; public LoggedUser(User user) { super(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, user.getRoles()); @@ -53,4 +53,8 @@ public String toString() { public static int getCaloriesPerDay() { return get().userTo.getCaloriesPerDay(); } + + public void update(UserTo newTo) { + userTo = newTo; + } } diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index 614c06abce92..4fc2d4e1da80 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -15,9 +15,8 @@ import ru.javawebinar.topjava.util.exception.ExceptionUtil; import ru.javawebinar.topjava.util.exception.NotFoundException; -import java.util.Collection; -import java.util.Objects; import java.util.List; +import java.util.Objects; /** * GKislin @@ -88,7 +87,7 @@ public void enable(int id, boolean enabled) { @Override public LoggedUser loadUserByUsername(String email) throws UsernameNotFoundException { - User u = repository.getByEmail(email); + User u = repository.getByEmail(email.toLowerCase()); if (u == null) { throw new UsernameNotFoundException("User " + email + " is not found"); } diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index f1e4182af93d..c16c7ff853ae 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -73,10 +73,14 @@ public String getEmail() { return email; } - public int getCaloriesPerDay() { + public Integer getCaloriesPerDay() { return caloriesPerDay; } + public void setCaloriesPerDay(Integer caloriesPerDay) { + this.caloriesPerDay = caloriesPerDay; + } + @Override public String toString() { return "UserTo{" + diff --git a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java index 723f1645c97b..57f50b4e5c36 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -22,6 +22,7 @@ public static UserTo asTo(User user) { public static User updateFromTo(User user, UserTo userTo) { user.setName(userTo.getName()); user.setEmail(userTo.getEmail().toLowerCase()); + user.setCaloriesPerDay(userTo.getCaloriesPerDay()); String password = userTo.getPassword(); if (!StringUtils.isEmpty(password)) { user.setPassword(password); diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 73d67cdf1e77..252d4874f9e3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -3,16 +3,23 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.support.SessionStatus; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.web.user.AbstractUserController; + +import javax.validation.Valid; /** * User: gkislin * Date: 22.08.2014 */ @Controller -public class RootController { +public class RootController extends AbstractUserController { @RequestMapping(value = "/", method = RequestMethod.GET) public String root() { @@ -40,4 +47,22 @@ public String login(ModelMap model, model.put("message", message); return "login"; } + + @RequestMapping(value = "/profile", method = RequestMethod.GET) + public String profile() { + return "profile"; + } + + @RequestMapping(value = "/profile", method = RequestMethod.POST) + public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionStatus status) { + if (result.hasErrors()) { + return "profile"; + } else { + userTo.setId(LoggedUser.id()); + super.update(userTo); + LoggedUser.get().update(userTo); + status.setComplete(); + return "redirect:meals"; + } + } } 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 dc546f7ef5b1..babfd1f0e6d1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RestController; import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.to.UserTo; /** * GKislin @@ -28,8 +29,9 @@ public void delete() { } @RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) - public void update(@RequestBody User user) { - super.update(user, LoggedUser.id()); + public void update(@RequestBody UserTo userTo) { + userTo.setId(LoggedUser.id()); + super.update(userTo); } @RequestMapping(value = "/text", method = RequestMethod.GET) diff --git a/src/main/webapp/WEB-INF/jsp/profile.jsp b/src/main/webapp/WEB-INF/jsp/profile.jsp new file mode 100644 index 000000000000..6c2d2ef108d5 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/profile.jsp @@ -0,0 +1,41 @@ + +<%@page contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="topjava" tagdir="/WEB-INF/tags" %> + + + + + + + +
+
+
+

${userTo.name} profile

+ +
+ + + + + + + +
+
+ +
+
+
+
+
+
+
+ + + + diff --git a/src/main/webapp/WEB-INF/tags/inputField.tag b/src/main/webapp/WEB-INF/tags/inputField.tag new file mode 100644 index 000000000000..6f4f5ff5adc2 --- /dev/null +++ b/src/main/webapp/WEB-INF/tags/inputField.tag @@ -0,0 +1,23 @@ +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> + +<%@ attribute name="name" required="true" description="Name of corresponding property in bean object" %> +<%@ attribute name="label" required="true" description="Field label" %> +<%@ attribute name="inputType" required="false" description="Input type" %> + + + +
+ + +
+ + + + + +  ${status.errorMessage} +
+
+
\ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 3a3d6f42c6b1..beb826bc6383 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -3,8 +3,9 @@ import org.junit.Test; import org.springframework.http.MediaType; import ru.javawebinar.topjava.TestUtil; -import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.util.UserUtil; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; @@ -44,13 +45,14 @@ public void testDelete() throws Exception { @Test public void testUpdate() throws Exception { - User updated = new User(USER_ID, "newName", "newEmail", "newPassword", Role.ROLE_USER); + UserTo updatedTo = new UserTo(0, "newName", "newEmail", "newPassword", 1500); + mockMvc.perform(put(REST_URL).contentType(MediaType.APPLICATION_JSON) .with(TestUtil.userHttpBasic(USER)) - .content(JsonUtil.writeValue(updated))) + .content(JsonUtil.writeValue(updatedTo))) .andDo(print()) .andExpect(status().isOk()); - MATCHER.assertEquals(updated, new User(userService.getByEmail("newEmail"))); + MATCHER.assertEquals(UserUtil.updateFromTo(new User(USER), updatedTo), userService.getByEmail("newemail")); } } \ No newline at end of file From 46be73d225e947395f15fa8fdacc51ea489d91b4 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 15:29:57 +0300 Subject: [PATCH 099/108] 10_7 Registrations --- config/messages/app.properties | 1 + config/messages/app_ru.properties | 1 + .../ru/javawebinar/topjava/model/User.java | 4 ++-- .../ru/javawebinar/topjava/util/UserUtil.java | 2 +- .../topjava/web/RootController.java | 20 +++++++++++++++++++ src/main/resources/db/populateDB.sql | 8 ++++---- src/main/resources/spring/spring-security.xml | 2 ++ src/main/webapp/WEB-INF/jsp/login.jsp | 3 ++- src/main/webapp/WEB-INF/jsp/profile.jsp | 7 ++++--- .../ru/javawebinar/topjava/SpringMain.java | 2 +- .../ru/javawebinar/topjava/UserTestData.java | 4 ++-- 11 files changed, 40 insertions(+), 14 deletions(-) diff --git a/config/messages/app.properties b/config/messages/app.properties index 59d8a5bd861b..6ca82d2d1e1e 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -2,6 +2,7 @@ app.title=Calories management app.footer=Project Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery application app.login=Login app.logout=Logout +app.registered=You are registered. Please Sign in. users.title=User list users.edit=Edit user diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties index 4be50246aa2c..51b10c2e9fe8 100644 --- a/config/messages/app_ru.properties +++ b/config/messages/app_ru.properties @@ -2,6 +2,7 @@ app.title=Подсчет калорий app.footer=Приложение по вебинару Maven/ Spring/ Security/ JPA(Hibernate)/ Jackson/jQuery app.login=Войти app.logout=Выйти +app.registered=Вы загеристрированы. Введите ваш логин/пароль. users.title=Список пользователей users.edit=Редактировать пользователя diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 3c9da585fe73..deb1c33b1b68 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -71,8 +71,8 @@ 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, UserUtil.DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); + public User(Integer id, String name, String email, String password, int caloriesPerDay, Role role, Role... roles) { + this(id, name, email, password, caloriesPerDay, true, EnumSet.of(role, roles)); } public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) { diff --git a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java index 57f50b4e5c36..b4c183435b90 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -12,7 +12,7 @@ public class UserUtil { public static final int DEFAULT_CALORIES_PER_DAY = 2000; public static User createFromTo(UserTo newUser) { - return new User(null, newUser.getName(), newUser.getEmail().toLowerCase(), newUser.getPassword(), Role.ROLE_USER); + return new User(null, newUser.getName(), newUser.getEmail().toLowerCase(), newUser.getPassword(), newUser.getCaloriesPerDay(), Role.ROLE_USER); } public static UserTo asTo(User user) { diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 252d4874f9e3..5fe092c5159e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.support.SessionStatus; import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.util.UserUtil; import ru.javawebinar.topjava.web.user.AbstractUserController; import javax.validation.Valid; @@ -65,4 +66,23 @@ public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionS return "redirect:meals"; } } + + @RequestMapping(value = "/register", method = RequestMethod.GET) + public String register(ModelMap model) { + model.addAttribute("userTo", new UserTo()); + model.addAttribute("register", true); + return "profile"; + } + + @RequestMapping(value = "/register", method = RequestMethod.POST) + public String saveRegister(@Valid UserTo userTo, BindingResult result, SessionStatus status, ModelMap model) { + if (result.hasErrors()) { + model.addAttribute("register", true); + return "profile"; + } else { + super.create(UserUtil.createFromTo(userTo)); + status.setComplete(); + return "redirect:login?message=app.registered"; + } + } } diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index cc816500ce36..8c38aaf256f5 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -3,11 +3,11 @@ DELETE FROM meals; 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, calories_per_day) +VALUES ('User', 'user@yandex.ru', 'password', 2005); -INSERT INTO users (name, email, password) -VALUES ('Admin', 'admin@gmail.com', 'admin'); +INSERT INTO users (name, email, password, calories_per_day) +VALUES ('Admin', 'admin@gmail.com', 'admin', 1900); INSERT INTO user_roles (role, user_id) VALUES ('ROLE_USER', 100000), diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index d5a58b7255c5..d34aa2e7bbfd 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -18,6 +18,8 @@ + + Admin login: admin@gmail.com / admin

+

Register »

Стек технологий: Spring Security, Spring MVC, Spring Data JPA, @@ -75,4 +76,4 @@ - \ No newline at end of file + diff --git a/src/main/webapp/WEB-INF/jsp/profile.jsp b/src/main/webapp/WEB-INF/jsp/profile.jsp index 6c2d2ef108d5..e2cc0affaec4 100644 --- a/src/main/webapp/WEB-INF/jsp/profile.jsp +++ b/src/main/webapp/WEB-INF/jsp/profile.jsp @@ -14,10 +14,11 @@

-

${userTo.name} profile

+

${register ? 'Register new' : userTo.name.concat(' profile')}

- @@ -27,7 +28,7 @@
- +
diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index e4773b4b3b65..9df2488eedf2 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -27,7 +27,7 @@ public static void main(String[] args) { System.out.println(Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); - System.out.println(adminUserController.create(new User(1, "userName", "email", "password", Role.ROLE_ADMIN))); + System.out.println(adminUserController.create(new User(1, "userName", "email", "password", 2005, Role.ROLE_ADMIN))); System.out.println(); UserMealRestController mealController = appCtx.getBean(UserMealRestController.class); diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index d92998bf7764..0532ee81486d 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -19,8 +19,8 @@ public class UserTestData { 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, Role.ROLE_USER); + public static final User USER = new User(USER_ID, "User", "user@yandex.ru", "password", 2005, Role.ROLE_USER); + public static final User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", 1900, Role.ROLE_ADMIN, Role.ROLE_USER); public static final ModelMatcher MATCHER = new ModelMatcher<>(u -> ((u instanceof TestUser) ? (TestUser) u : new TestUser(u)), User.class); From 3b3f58f55947a020cc3c3e23a28c622a14ac9c88 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 19:49:37 +0300 Subject: [PATCH 100/108] 10_08 Not found 404 --- .../util/exception/NotFoundException.java | 4 ++++ .../web/meal/UserMealRestControllerTest.java | 15 +++++++++++++++ .../web/user/AdminRestControllerTest.java | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java index 7a770f0db834..1c85842a3497 100644 --- a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -1,9 +1,13 @@ package ru.javawebinar.topjava.util.exception; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + /** * User: gkislin * Date: 19.08.2014 */ +@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No data found") // 404 public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java index b3b7596bb92f..219d32df23d7 100644 --- a/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/meal/UserMealRestControllerTest.java @@ -46,6 +46,21 @@ public void testGetUnauth() throws Exception { .andExpect(status().isUnauthorized()); } + @Test + public void testGetNotFound() throws Exception { + mockMvc.perform(get(REST_URL + ADMIN_MEAL_ID) + .with(userHttpBasic(USER))) + .andExpect(status().isNotFound()); + } + + @Test + public void testDeleteNotFound() throws Exception { + mockMvc.perform(delete(REST_URL + ADMIN_MEAL_ID).contentType(MediaType.APPLICATION_JSON) + .with(userHttpBasic(USER))) + .andDo(print()) + .andExpect(status().isNotFound()); + } + @Test public void testDelete() throws Exception { mockMvc.perform(delete(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java index 148ebf44566e..c02d2327ca92 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java @@ -33,6 +33,14 @@ public void testGet() throws Exception { .andExpect(MATCHER.contentMatcher(ADMIN)); } + @Test + public void testGetNotFound() throws Exception { + mockMvc.perform(get(REST_URL + 1) + .with(TestUtil.userHttpBasic(ADMIN))) + .andExpect(status().isNotFound()) + .andDo(print()); + } + @Test public void testGetByEmail() throws Exception { mockMvc.perform(get(REST_URL + "by?email=" + ADMIN.getEmail()) @@ -51,6 +59,14 @@ public void testDelete() throws Exception { MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), userService.getAll()); } + @Test + public void testDeleteNotFound() throws Exception { + mockMvc.perform(delete(REST_URL + 1) + .with(TestUtil.userHttpBasic(ADMIN))) + .andExpect(status().isNotFound()) + .andDo(print()); + } + @Test public void testGetUnauth() throws Exception { mockMvc.perform(get(REST_URL).contentType(MediaType.APPLICATION_JSON) From ee4cf87b85efa2071e959a50c70d17a75093faf3 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 19:51:34 +0300 Subject: [PATCH 101/108] 10_09 Global Exception --- .../web/GlobalControllerExceptionHandler.java | 36 +++++++++++++++++++ .../WEB-INF/jsp/exception/exception.jsp | 24 +++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/web/GlobalControllerExceptionHandler.java create mode 100644 src/main/webapp/WEB-INF/jsp/exception/exception.jsp diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalControllerExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalControllerExceptionHandler.java new file mode 100644 index 000000000000..b633bda11fc2 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/GlobalControllerExceptionHandler.java @@ -0,0 +1,36 @@ +package ru.javawebinar.topjava.web; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; +import ru.javawebinar.topjava.LoggedUser; +import ru.javawebinar.topjava.LoggerWrapper; + +import javax.servlet.http.HttpServletRequest; + +/** + * User: gkislin + * Date: 23.09.2014 + */ +@ControllerAdvice +public class GlobalControllerExceptionHandler { + private static final LoggerWrapper LOG = LoggerWrapper.get(GlobalControllerExceptionHandler.class); + + + @ExceptionHandler(Exception.class) + @Order(Ordered.LOWEST_PRECEDENCE) + ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { + LOG.error("Exception at request " + req.getRequestURL()); + ModelAndView mav = new ModelAndView("exception/exception"); + mav.addObject("exception", e); + LoggedUser loggedUser = LoggedUser.safeGet(); + + // Interceptor is not invoked, put userTo + if (loggedUser != null) { + mav.addObject("userTo", loggedUser.getUserTo()); + } + return mav; + } +} diff --git a/src/main/webapp/WEB-INF/jsp/exception/exception.jsp b/src/main/webapp/WEB-INF/jsp/exception/exception.jsp new file mode 100644 index 000000000000..1ee7c6a7761f --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/exception/exception.jsp @@ -0,0 +1,24 @@ +<%@page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + +
+
+
+

Application error:

+

${exception.message}

+ +
+
+ + + \ No newline at end of file From 9e380131d951e94755d74f4c08b39858fb6cae25 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 20:57:56 +0300 Subject: [PATCH 102/108] 10_10 Controller based exception --- .../ru/javawebinar/topjava/LoggerWrapper.java | 6 +++ .../topjava/util/exception/ErrorInfo.java | 17 +++++++ .../topjava/web/ExceptionInfoHandler.java | 46 +++++++++++++++++++ .../web/meal/UserMealAjaxController.java | 4 +- .../web/meal/UserMealRestController.java | 3 +- .../topjava/web/user/AdminAjaxController.java | 4 +- .../topjava/web/user/AdminRestController.java | 3 +- .../web/user/ProfileRestController.java | 3 +- .../webapp/resources/js/datatablesUtil.js | 3 +- 9 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java diff --git a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java index 8ab6153791d8..54c2c23865e2 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java +++ b/src/main/java/ru/javawebinar/topjava/LoggerWrapper.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.util.exception.ErrorInfo; import ru.javawebinar.topjava.util.exception.NotFoundException; /** @@ -75,4 +76,9 @@ public NotFoundException getNotFoundException(String reason) { logger.error(reason); return new NotFoundException(reason); } + + public ErrorInfo getErrorInfo(CharSequence requestUrl, Exception e) { + logger.error("Exception at request " + requestUrl); + return new ErrorInfo(requestUrl, e); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java new file mode 100644 index 000000000000..c422c409e630 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java @@ -0,0 +1,17 @@ +package ru.javawebinar.topjava.util.exception; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class ErrorInfo { + public final String url; + public final String cause; + public final String detail; + + public ErrorInfo(CharSequence url, Throwable ex) { + this.url = url.toString(); + this.cause = ex.getClass().getSimpleName(); + this.detail = ex.getLocalizedMessage(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java new file mode 100644 index 000000000000..17720bb430e6 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -0,0 +1,46 @@ +package ru.javawebinar.topjava.web; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import ru.javawebinar.topjava.LoggerWrapper; +import ru.javawebinar.topjava.util.exception.ErrorInfo; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import javax.servlet.http.HttpServletRequest; + +/** + * User: gkislin + * Date: 23.09.2014 + */ +public interface ExceptionInfoHandler { + LoggerWrapper LOG = LoggerWrapper.get(ExceptionInfoHandler.class); + + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(NotFoundException.class) + @ResponseBody + @Order(Ordered.HIGHEST_PRECEDENCE) + default ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { + return LOG.getErrorInfo(req.getRequestURL(), e); + } + + @ResponseStatus(value = HttpStatus.CONFLICT) // 409 + @ExceptionHandler(DataIntegrityViolationException.class) + @ResponseBody + @Order(Ordered.HIGHEST_PRECEDENCE + 1) + default ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { + return LOG.getErrorInfo(req.getRequestURL(), e); + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(Exception.class) + @ResponseBody + @Order(Ordered.LOWEST_PRECEDENCE) + default ErrorInfo handleError(HttpServletRequest req, Exception e) { + return LOG.getErrorInfo(req.getRequestURL(), e); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java index cef70f8256a3..15242ee6e70f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; import javax.validation.Valid; import java.time.LocalDate; @@ -20,7 +21,7 @@ */ @RestController @RequestMapping(value = "/ajax/profile/meals") -public class UserMealAjaxController extends AbstractUserMealController { +public class UserMealAjaxController extends AbstractUserMealController implements ExceptionInfoHandler { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List getAll() { @@ -40,6 +41,7 @@ public void delete(@PathVariable("id") int id) { @RequestMapping(method = RequestMethod.POST) public ResponseEntity updateOrCreate(@Valid UserMeal meal, BindingResult result) { if (result.hasErrors()) { + // TODO change to exception handler StringBuilder sb = new StringBuilder(); result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); return new ResponseEntity<>(sb.toString(), HttpStatus.UNPROCESSABLE_ENTITY); diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index bedb2675b002..3a1ffdd2f4d2 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -10,6 +10,7 @@ import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; import java.net.URI; import java.time.LocalDate; @@ -19,7 +20,7 @@ @RestController @RequestMapping(value = UserMealRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) -public class UserMealRestController extends AbstractUserMealController { +public class UserMealRestController extends AbstractUserMealController implements ExceptionInfoHandler { public static final String REST_URL = "/rest/profile/meals"; @RequestMapping(value = "/{id}", method = RequestMethod.GET) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java index 797a26219f2f..10bd0ed125a2 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; import javax.validation.Valid; import java.util.List; @@ -17,7 +18,7 @@ */ @RestController @RequestMapping("/ajax/admin/users") -public class AdminAjaxController extends AbstractUserController { +public class AdminAjaxController extends AbstractUserController implements ExceptionInfoHandler { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List getAll() { @@ -37,6 +38,7 @@ public void delete(@PathVariable("id") int id) { @RequestMapping(method = RequestMethod.POST) public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { if (result.hasErrors()) { + // TODO change to exception handler StringBuilder sb = new StringBuilder(); result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); return new ResponseEntity<>(sb.toString(), HttpStatus.UNPROCESSABLE_ENTITY); 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 58d9e779d64a..ec92680cd054 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; import java.net.URI; import java.util.List; @@ -17,7 +18,7 @@ */ @RestController @RequestMapping(AdminRestController.REST_URL) -public class AdminRestController extends AbstractUserController { +public class AdminRestController extends AbstractUserController implements ExceptionInfoHandler { static final String REST_URL = "/rest/admin/users"; @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 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 babfd1f0e6d1..a9aff4dab065 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.web.ExceptionInfoHandler; /** * GKislin @@ -15,7 +16,7 @@ */ @RestController @RequestMapping(ProfileRestController.REST_URL) -public class ProfileRestController extends AbstractUserController { +public class ProfileRestController extends AbstractUserController implements ExceptionInfoHandler { public static final String REST_URL = "/rest/profile"; @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index 81736716ebd2..f8d79dd45ec0 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -88,8 +88,9 @@ function successNoty(text) { function failNoty(event, jqXHR, options, jsExc) { closeNoty(); + var errorInfo = $.parseJSON(jqXHR.responseText); failedNote = noty({ - text: 'Failed: ' + jqXHR.statusText + "
" + jqXHR.responseJSON, + text: 'Failed: ' + jqXHR.statusText + "
" + errorInfo.cause + "
" + errorInfo.detail, type: 'error', layout: 'bottomRight' }); From aa1f3006559d30a20304c63bba1270d051bddf52 Mon Sep 17 00:00:00 2001 From: m1nd Date: Fri, 12 Feb 2016 21:07:38 +0300 Subject: [PATCH 103/108] 10_11 Password encoding --- .../topjava/util/PasswordUtil.java | 39 +++++++++++++++++++ .../ru/javawebinar/topjava/util/UserUtil.java | 16 ++++---- .../web/user/AbstractUserController.java | 5 ++- src/main/resources/db/populateDB.sql | 5 ++- src/main/resources/spring/spring-security.xml | 4 +- .../ru/javawebinar/topjava/UserTestData.java | 17 +++++++- 6 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/PasswordUtil.java diff --git a/src/main/java/ru/javawebinar/topjava/util/PasswordUtil.java b/src/main/java/ru/javawebinar/topjava/util/PasswordUtil.java new file mode 100644 index 000000000000..07faed1e87d7 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/PasswordUtil.java @@ -0,0 +1,39 @@ +package ru.javawebinar.topjava.util; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.StringUtils; + +import java.util.regex.Pattern; + +/** + * GKislin + * 05.01.2015. + */ + +public class PasswordUtil { + private static final Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}"); + public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder(); + + public static PasswordEncoder getPasswordEncoder() { + return PASSWORD_ENCODER; + } + + public static String encode(String newPassword) { + if (StringUtils.isEmpty(newPassword)) { + return null; + } + if (isEncoded(newPassword)) { + return newPassword; + } + return PASSWORD_ENCODER.encode(newPassword); + } + + public static boolean isMatch(String rawPassword, String password) { + return PASSWORD_ENCODER.matches(rawPassword, password); + } + + public static boolean isEncoded(String newPassword) { + return BCRYPT_PATTERN.matcher(newPassword).matches(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java index b4c183435b90..da23e4557822 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UserUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.util; -import org.springframework.util.StringUtils; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; @@ -21,12 +20,15 @@ public static UserTo asTo(User user) { public static User updateFromTo(User user, UserTo userTo) { user.setName(userTo.getName()); - user.setEmail(userTo.getEmail().toLowerCase()); + user.setEmail(userTo.getEmail()); + user.setPassword(userTo.getPassword()); user.setCaloriesPerDay(userTo.getCaloriesPerDay()); - String password = userTo.getPassword(); - if (!StringUtils.isEmpty(password)) { - user.setPassword(password); - } + return prepareToSave(user); + } + + public static User prepareToSave(User user) { + user.setPassword(PasswordUtil.encode(user.getPassword())); + user.setEmail(user.getEmail().toLowerCase()); return user; } -} +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 6001b2e012b9..049a28ed93bb 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -5,6 +5,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; import ru.javawebinar.topjava.to.UserTo; +import ru.javawebinar.topjava.util.UserUtil; import java.util.List; @@ -30,7 +31,7 @@ public User get(int id) { public User create(User user) { user.setId(null); LOG.info("create " + user); - return service.save(user); + return service.save(UserUtil.prepareToSave(user)); } public void delete(int id) { @@ -41,7 +42,7 @@ public void delete(int id) { public void update(User user, int id) { user.setId(id); LOG.info("update " + user); - service.update(user); + service.update(UserUtil.prepareToSave(user)); } public void update(UserTo userTo) { diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index 8c38aaf256f5..a3362075c71e 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -4,10 +4,11 @@ DELETE FROM users; ALTER SEQUENCE global_seq RESTART WITH 100000; INSERT INTO users (name, email, password, calories_per_day) -VALUES ('User', 'user@yandex.ru', 'password', 2005); +VALUES ('User', 'user@yandex.ru', '$2a$10$Sh0ZD2NFrzRRJJEKEWn8l.92ROEuzlVyzB9SV1AM8fdluPR0aC1ni', 2005); + INSERT INTO users (name, email, password, calories_per_day) -VALUES ('Admin', 'admin@gmail.com', 'admin', 1900); +VALUES ('Admin', 'admin@gmail.com', '$2a$10$WejOLxVuXRpOgr4IlzQJ.eT4UcukNqHlAiOVZj1P/nmc8WbpMkiju', 1900); INSERT INTO user_roles (role, user_id) VALUES ('ROLE_USER', 100000), diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index d34aa2e7bbfd..0bdbfba4954e 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -29,9 +29,11 @@ + + - + + + diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml index 0bdbfba4954e..3805bb1f6864 100644 --- a/src/main/resources/spring/spring-security.xml +++ b/src/main/resources/spring/spring-security.xml @@ -26,7 +26,7 @@ authentication-failure-url="/login?error=true" login-processing-url="/spring_security_check"/> - + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 3a0f7cd45211..420ead589c8a 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -2,22 +2,22 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> \ 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 522d793362fb..47a1892d9d6d 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -5,6 +5,10 @@ + + + + <fmt:message key="app.title"/> ${pageContext.request.requestURL} diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp index 6b3d28449f31..bc604f7777b2 100644 --- a/src/main/webapp/WEB-INF/jsp/login.jsp +++ b/src/main/webapp/WEB-INF/jsp/login.jsp @@ -1,6 +1,7 @@ <%@ 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="form" uri="http://www.springframework.org/tags/form" %> @@ -9,7 +10,7 @@
- +
diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index 036049caaf84..700c37b62b94 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -1,6 +1,8 @@ <%@ 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="form" uri="http://www.springframework.org/tags/form" %> + @@ -16,7 +18,7 @@
-
+
@@ -48,7 +50,7 @@
- + @@ -75,7 +77,7 @@ - + diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 94eb67c8410f..4c6fb400ccad 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -1,6 +1,7 @@ <%@ 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="form" uri="http://www.springframework.org/tags/form" %> @@ -45,7 +46,7 @@ - + diff --git a/src/main/webapp/resources/js/datatablesUtil.js b/src/main/webapp/resources/js/datatablesUtil.js index f8d79dd45ec0..5bd7091c3779 100644 --- a/src/main/webapp/resources/js/datatablesUtil.js +++ b/src/main/webapp/resources/js/datatablesUtil.js @@ -15,6 +15,12 @@ function makeEditable() { $(document).ajaxError(function (event, jqXHR, options, jsExc) { failNoty(event, jqXHR, options, jsExc); }); + + var token = $("meta[name='_csrf']").attr("content"); + var header = $("meta[name='_csrf_header']").attr("content"); + $(document).ajaxSend(function(e, xhr, options) { + xhr.setRequestHeader(header, token); + }); } function updateRow(id) { From a7147f89d089e6d7a432c4303b2de423ad2e9cc7 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 13 Feb 2016 21:02:21 +0300 Subject: [PATCH 105/108] Final 1 Refactoring fix --- .../ru/javawebinar/topjava/LoggedUser.java | 2 ++ .../ru/javawebinar/topjava/to/UserTo.java | 2 ++ .../topjava/web/ExceptionInfoHandler.java | 15 +++++----- .../web/meal/UserMealAjaxController.java | 3 +- .../web/meal/UserMealRestController.java | 3 +- .../topjava/web/user/AdminAjaxController.java | 3 +- .../topjava/web/user/AdminRestController.java | 3 +- .../web/user/ProfileRestController.java | 3 +- src/main/webapp/WEB-INF/web.xml | 19 ++++++------ .../java/ru/javawebinar/topjava/TestUtil.java | 7 +++++ .../topjava/web/user/RootControllerTest.java | 30 ++++++++++++------- 11 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java index 39c36182d379..3dd8c3aa7384 100644 --- a/src/main/java/ru/javawebinar/topjava/LoggedUser.java +++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java @@ -15,6 +15,8 @@ * Mock implementation */ public class LoggedUser extends org.springframework.security.core.userdetails.User { + static final long serialVersionUID = 1L; + private UserTo userTo; public LoggedUser(User user) { diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index c16c7ff853ae..83323d937167 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -10,6 +10,8 @@ import java.io.Serializable; public class UserTo implements Serializable { + static final long serialVersionUID = 1L; + protected int id; @NotEmpty diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 17720bb430e6..80433f631743 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -4,9 +4,7 @@ import org.springframework.core.annotation.Order; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.*; import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.util.exception.ErrorInfo; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -17,14 +15,15 @@ * User: gkislin * Date: 23.09.2014 */ -public interface ExceptionInfoHandler { - LoggerWrapper LOG = LoggerWrapper.get(ExceptionInfoHandler.class); +@ControllerAdvice(annotations = RestController.class) +public class ExceptionInfoHandler { + private static final LoggerWrapper LOG = LoggerWrapper.get(ExceptionInfoHandler.class); @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(NotFoundException.class) @ResponseBody @Order(Ordered.HIGHEST_PRECEDENCE) - default ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { + public ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { return LOG.getErrorInfo(req.getRequestURL(), e); } @@ -32,7 +31,7 @@ default ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { @ExceptionHandler(DataIntegrityViolationException.class) @ResponseBody @Order(Ordered.HIGHEST_PRECEDENCE + 1) - default ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { + public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { return LOG.getErrorInfo(req.getRequestURL(), e); } @@ -40,7 +39,7 @@ default ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExcepti @ExceptionHandler(Exception.class) @ResponseBody @Order(Ordered.LOWEST_PRECEDENCE) - default ErrorInfo handleError(HttpServletRequest req, Exception e) { + public ErrorInfo handleError(HttpServletRequest req, Exception e) { return LOG.getErrorInfo(req.getRequestURL(), e); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java index 15242ee6e70f..5eb9c54bfffd 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java @@ -8,7 +8,6 @@ import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; import javax.validation.Valid; import java.time.LocalDate; @@ -21,7 +20,7 @@ */ @RestController @RequestMapping(value = "/ajax/profile/meals") -public class UserMealAjaxController extends AbstractUserMealController implements ExceptionInfoHandler { +public class UserMealAjaxController extends AbstractUserMealController { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List getAll() { diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java index 3a1ffdd2f4d2..bedb2675b002 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java @@ -10,7 +10,6 @@ import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; import java.net.URI; import java.time.LocalDate; @@ -20,7 +19,7 @@ @RestController @RequestMapping(value = UserMealRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) -public class UserMealRestController extends AbstractUserMealController implements ExceptionInfoHandler { +public class UserMealRestController extends AbstractUserMealController { public static final String REST_URL = "/rest/profile/meals"; @RequestMapping(value = "/{id}", method = RequestMethod.GET) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java index 10bd0ed125a2..bab70fe80c90 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java @@ -8,7 +8,6 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; import javax.validation.Valid; import java.util.List; @@ -18,7 +17,7 @@ */ @RestController @RequestMapping("/ajax/admin/users") -public class AdminAjaxController extends AbstractUserController implements ExceptionInfoHandler { +public class AdminAjaxController extends AbstractUserController { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 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 ec92680cd054..58d9e779d64a 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -7,7 +7,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; import java.net.URI; import java.util.List; @@ -18,7 +17,7 @@ */ @RestController @RequestMapping(AdminRestController.REST_URL) -public class AdminRestController extends AbstractUserController implements ExceptionInfoHandler { +public class AdminRestController extends AbstractUserController { static final String REST_URL = "/rest/admin/users"; @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 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 a9aff4dab065..babfd1f0e6d1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -8,7 +8,6 @@ import ru.javawebinar.topjava.LoggedUser; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; /** * GKislin @@ -16,7 +15,7 @@ */ @RestController @RequestMapping(ProfileRestController.REST_URL) -public class ProfileRestController extends AbstractUserController implements ExceptionInfoHandler { +public class ProfileRestController extends AbstractUserController { public static final String REST_URL = "/rest/profile"; @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 61d21dedd284..7f618158ee8d 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -36,15 +36,6 @@ / - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - encodingFilter org.springframework.web.filter.CharacterEncodingFilter @@ -62,4 +53,14 @@ /* + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + + diff --git a/src/test/java/ru/javawebinar/topjava/TestUtil.java b/src/test/java/ru/javawebinar/topjava/TestUtil.java index 4bcbc18caca8..6c39e88a84cc 100644 --- a/src/test/java/ru/javawebinar/topjava/TestUtil.java +++ b/src/test/java/ru/javawebinar/topjava/TestUtil.java @@ -1,5 +1,7 @@ package ru.javawebinar.topjava; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.RequestPostProcessor; @@ -27,6 +29,11 @@ public static RequestPostProcessor userHttpBasic(User user) { return SecurityMockMvcRequestPostProcessors.httpBasic(user.getEmail(), user.getPassword()); } + public static void authorize(User user) { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())); + } + /** * Compare entities using toString */ diff --git a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java index 91e768ca3449..a5353ccac866 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/RootControllerTest.java @@ -6,6 +6,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static ru.javawebinar.topjava.TestUtil.authorize; +import static ru.javawebinar.topjava.UserTestData.ADMIN; +import static ru.javawebinar.topjava.UserTestData.USER; /** * GKislin @@ -15,24 +18,29 @@ public class RootControllerTest extends AbstractControllerTest { @Test public void testUserList() throws Exception { + authorize(ADMIN); mockMvc.perform(get("/users")) .andDo(print()) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); -// .andExpect(model().attribute("userList", hasSize(2))) -// .andExpect(model().attribute("userList", hasItem( -// allOf( -// hasProperty("id", is(START_SEQ)), -// hasProperty("name", is(USER.getName())) -// ) -// ))); + .andExpect(status().isOk()) + .andExpect(view().name("userList")) + .andExpect(forwardedUrl("/WEB-INF/jsp/userList.jsp")); } @Test + public void testUserListUnAuth() throws Exception { + authorize(USER); + mockMvc.perform(get("/users")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(view().name("exception/exception")) + .andExpect(forwardedUrl("/WEB-INF/jsp/exception/exception.jsp")); + } + public void testMealList() throws Exception { + authorize(USER); mockMvc.perform(get("/meals")) .andDo(print()) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(view().name("mealList")) + .andExpect(forwardedUrl("/WEB-INF/jsp/mealList.jsp")); } } \ No newline at end of file From 0f17384c46b113b5a689f82f5f78bc636914a5ca Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 13 Feb 2016 21:06:52 +0300 Subject: [PATCH 106/108] Final 1 Validation Exception --- .../util/exception/ValidationException.java | 20 ++++++++++ .../topjava/web/ExceptionInfoHandler.java | 9 +++++ .../topjava/web/RootController.java | 39 +++++++++++-------- .../web/meal/UserMealAjaxController.java | 13 +++---- .../topjava/web/user/AdminAjaxController.java | 26 +++++++------ 5 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/ValidationException.java diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ValidationException.java b/src/main/java/ru/javawebinar/topjava/util/exception/ValidationException.java new file mode 100644 index 000000000000..4a912734cbf8 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/ValidationException.java @@ -0,0 +1,20 @@ +package ru.javawebinar.topjava.util.exception; + +import org.springframework.validation.BindingResult; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class ValidationException extends RuntimeException { + + public ValidationException(BindingResult result) { + super(getDescription(result)); + } + + private static String getDescription(BindingResult result) { + StringBuilder sb = new StringBuilder(); + result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); + return sb.toString(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 80433f631743..03d1f96edf84 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -8,6 +8,7 @@ import ru.javawebinar.topjava.LoggerWrapper; import ru.javawebinar.topjava.util.exception.ErrorInfo; import ru.javawebinar.topjava.util.exception.NotFoundException; +import ru.javawebinar.topjava.util.exception.ValidationException; import javax.servlet.http.HttpServletRequest; @@ -35,6 +36,14 @@ public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExceptio return LOG.getErrorInfo(req.getRequestURL(), e); } + @ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY) // 422 + @ExceptionHandler(ValidationException.class) + @ResponseBody + @Order(Ordered.HIGHEST_PRECEDENCE + 2) + ErrorInfo validationError(HttpServletRequest req, ValidationException e) { + return LOG.getErrorInfo(req.getRequestURL(), e); + } + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) @ResponseBody diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java index 5fe092c5159e..332d7d71c174 100644 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ b/src/main/java/ru/javawebinar/topjava/web/RootController.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.web; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -34,7 +35,7 @@ public String userList() { return "userList"; } - @RequestMapping(value = "/meals",method = RequestMethod.GET) + @RequestMapping(value = "/meals", method = RequestMethod.GET) public String mealList() { return "mealList"; } @@ -56,15 +57,18 @@ public String profile() { @RequestMapping(value = "/profile", method = RequestMethod.POST) public String updateProfile(@Valid UserTo userTo, BindingResult result, SessionStatus status) { - if (result.hasErrors()) { - return "profile"; - } else { - userTo.setId(LoggedUser.id()); - super.update(userTo); - LoggedUser.get().update(userTo); - status.setComplete(); - return "redirect:meals"; + if (!result.hasErrors()) { + try { + userTo.setId(LoggedUser.id()); + super.update(userTo); + LoggedUser.get().update(userTo); + status.setComplete(); + return "redirect:meals"; + } catch (DataIntegrityViolationException ex) { + result.rejectValue("email", "error.user", "user with this email already present in application"); + } } + return "profile"; } @RequestMapping(value = "/register", method = RequestMethod.GET) @@ -76,13 +80,16 @@ public String register(ModelMap model) { @RequestMapping(value = "/register", method = RequestMethod.POST) public String saveRegister(@Valid UserTo userTo, BindingResult result, SessionStatus status, ModelMap model) { - if (result.hasErrors()) { - model.addAttribute("register", true); - return "profile"; - } else { - super.create(UserUtil.createFromTo(userTo)); - status.setComplete(); - return "redirect:login?message=app.registered"; + if (!result.hasErrors()) { + try { + super.create(UserUtil.createFromTo(userTo)); + status.setComplete(); + return "redirect:login?message=app.registered"; + } catch (DataIntegrityViolationException ex) { + result.rejectValue("email", "error.user", "user with this email already present in application"); + } } + model.addAttribute("register", true); + return "profile"; } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java index 5eb9c54bfffd..88727f2b9c27 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealAjaxController.java @@ -1,13 +1,13 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.support.SessionStatus; import ru.javawebinar.topjava.model.UserMeal; import ru.javawebinar.topjava.to.UserMealWithExceed; import ru.javawebinar.topjava.util.TimeUtil; +import ru.javawebinar.topjava.util.exception.ValidationException; import javax.validation.Valid; import java.time.LocalDate; @@ -38,19 +38,16 @@ public void delete(@PathVariable("id") int id) { } @RequestMapping(method = RequestMethod.POST) - public ResponseEntity updateOrCreate(@Valid UserMeal meal, BindingResult result) { + public void updateOrCreate(@Valid UserMeal meal, BindingResult result, SessionStatus status) { if (result.hasErrors()) { - // TODO change to exception handler - StringBuilder sb = new StringBuilder(); - result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); - return new ResponseEntity<>(sb.toString(), HttpStatus.UNPROCESSABLE_ENTITY); + throw new ValidationException(result); } + status.setComplete(); if (meal.getId() == 0) { super.create(meal); } else { super.update(meal, meal.getId()); } - return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/filter", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java index bab70fe80c90..f70d8debfbdd 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminAjaxController.java @@ -1,13 +1,14 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.http.HttpStatus; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.support.SessionStatus; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; import ru.javawebinar.topjava.util.UserUtil; +import ru.javawebinar.topjava.util.exception.ValidationException; import javax.validation.Valid; import java.util.List; @@ -35,19 +36,20 @@ public void delete(@PathVariable("id") int id) { } @RequestMapping(method = RequestMethod.POST) - public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { + public void createOrUpdate(@Valid UserTo userTo, BindingResult result, SessionStatus status) { if (result.hasErrors()) { - // TODO change to exception handler - StringBuilder sb = new StringBuilder(); - result.getFieldErrors().forEach(fe -> sb.append(fe.getField()).append(" ").append(fe.getDefaultMessage()).append("
")); - return new ResponseEntity<>(sb.toString(), HttpStatus.UNPROCESSABLE_ENTITY); + throw new ValidationException(result); } - if (userTo.getId() == 0) { - super.create(UserUtil.createFromTo(userTo)); - } else { - super.update(userTo); + status.setComplete(); + try { + if (userTo.getId() == 0) { + super.create(UserUtil.createFromTo(userTo)); + } else { + super.update(userTo); + } + } catch (DataIntegrityViolationException e) { + throw new DataIntegrityViolationException("user with this email already present in application"); } - return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/{id}", method = RequestMethod.POST) From e657fe4dc7c0c7bc324bbacab23bc944808a72e1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Sat, 13 Feb 2016 21:22:51 +0300 Subject: [PATCH 107/108] Final 3 Change locale --- src/main/resources/spring/spring-mvc.xml | 33 ++++++++++++------- .../WEB-INF/jsp/exception/exception.jsp | 1 - .../WEB-INF/jsp/fragments/bodyHeader.jsp | 27 ++++++++------- .../webapp/WEB-INF/jsp/fragments/footer.jsp | 7 ++-- .../webapp/WEB-INF/jsp/fragments/headTag.jsp | 4 +-- .../webapp/WEB-INF/jsp/fragments/lang.jsp | 14 ++++++++ src/main/webapp/WEB-INF/jsp/login.jsp | 30 ++++++++++------- src/main/webapp/WEB-INF/jsp/mealList.jsp | 10 +++--- src/main/webapp/WEB-INF/jsp/userList.jsp | 10 +++--- 9 files changed, 85 insertions(+), 51 deletions(-) create mode 100644 src/main/webapp/WEB-INF/jsp/fragments/lang.jsp diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml index 0db53900a95c..2081af853a06 100644 --- a/src/main/resources/spring/spring-mvc.xml +++ b/src/main/resources/spring/spring-mvc.xml @@ -53,21 +53,32 @@ p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/> - + + + messages/app + + + + --> + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/exception/exception.jsp b/src/main/webapp/WEB-INF/jsp/exception/exception.jsp index 1ee7c6a7761f..2bedcea6a2c5 100644 --- a/src/main/webapp/WEB-INF/jsp/exception/exception.jsp +++ b/src/main/webapp/WEB-INF/jsp/exception/exception.jsp @@ -1,5 +1,4 @@ <%@page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 420ead589c8a..e4fdb3684adb 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -1,23 +1,28 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index 171837386e40..945e72a46459 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp @@ -1,8 +1,11 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> + + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp index 47a1892d9d6d..ce3928881af9 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -1,5 +1,5 @@ <%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> @@ -9,7 +9,7 @@ - <fmt:message key="app.title"/> + <spring:message code="app.title"/> ${pageContext.request.requestURL} diff --git a/src/main/webapp/WEB-INF/jsp/fragments/lang.jsp b/src/main/webapp/WEB-INF/jsp/fragments/lang.jsp new file mode 100644 index 000000000000..3583cdfa7d8c --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/fragments/lang.jsp @@ -0,0 +1,14 @@ +<%@page contentType="text/html" pageEncoding="UTF-8" %> + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp index bc604f7777b2..51a3b2f67aed 100644 --- a/src/main/webapp/WEB-INF/jsp/login.jsp +++ b/src/main/webapp/WEB-INF/jsp/login.jsp @@ -1,6 +1,6 @@ <%@ 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="spring" uri="http://www.springframework.org/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -8,17 +8,23 @@ @@ -32,7 +38,7 @@
- +

diff --git a/src/main/webapp/WEB-INF/jsp/mealList.jsp b/src/main/webapp/WEB-INF/jsp/mealList.jsp index 700c37b62b94..284364be6254 100644 --- a/src/main/webapp/WEB-INF/jsp/mealList.jsp +++ b/src/main/webapp/WEB-INF/jsp/mealList.jsp @@ -1,6 +1,6 @@ <%@ 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="spring" uri="http://www.springframework.org/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -14,7 +14,7 @@

-

+

@@ -51,7 +51,7 @@
- +
@@ -74,7 +74,7 @@ - - diff --git a/src/main/webapp/WEB-INF/jsp/userList.jsp b/src/main/webapp/WEB-INF/jsp/userList.jsp index 4c6fb400ccad..8c4c6aaa7b97 100644 --- a/src/main/webapp/WEB-INF/jsp/userList.jsp +++ b/src/main/webapp/WEB-INF/jsp/userList.jsp @@ -1,6 +1,6 @@ <%@ 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="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -14,10 +14,10 @@
-

+

- +
@@ -43,7 +43,7 @@ - - From 08a85882ffb6644ae53649d79deb40c4d37a60b1 Mon Sep 17 00:00:00 2001 From: m1nd Date: Mon, 15 Feb 2016 19:00:57 +0300 Subject: [PATCH 108/108] Final 4 Heroku --- Procfile | 1 + hr.bat | 2 + pom.xml | 30 +++++++++++ settings.xml | 11 ++++ src/main/resources/db/heroku.properties | 5 ++ src/main/resources/db/postgres.properties | 19 +++++++ src/main/resources/spring/spring-db.xml | 25 +++++++++ src/main/resources/tomcat/context.xml | 49 ++++++++++++++++++ .../webapp/resources/images/icon-meal.png | Bin 0 -> 1898 bytes system.properties | 1 + 10 files changed, 143 insertions(+) create mode 100644 Procfile create mode 100644 hr.bat create mode 100644 settings.xml create mode 100644 src/main/resources/db/heroku.properties create mode 100644 src/main/resources/db/postgres.properties create mode 100644 src/main/resources/tomcat/context.xml create mode 100644 src/main/webapp/resources/images/icon-meal.png create mode 100644 system.properties diff --git a/Procfile b/Procfile new file mode 100644 index 000000000000..4afeb7900918 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: java $JAVA_OPTS -Dspring.profiles.active="datajpa,heroku" -DTOPJAVA_ROOT="." -jar target/dependency/webapp-runner.jar --port $PORT target/*.war \ No newline at end of file diff --git a/hr.bat b/hr.bat new file mode 100644 index 000000000000..1742ed99fa81 --- /dev/null +++ b/hr.bat @@ -0,0 +1,2 @@ +call mvn -B -s settings.xml -DskipTests=true clean install +call java -Dspring.profiles.active="datajpa,heroku" -DDATABASE_URL="postgres://postgres:passwd@localhost:5432/topjava" -jar target/dependency/webapp-runner.jar target/*.war diff --git a/pom.xml b/pom.xml index 4dd00e8283e6..45ffcd2860ae 100644 --- a/pom.xml +++ b/pom.xml @@ -324,6 +324,36 @@ true + + heroku + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + package + + copy + + + + + com.github.jsimone + webapp-runner + 7.0.57.2 + webapp-runner.jar + + + + + + + + + diff --git a/settings.xml b/settings.xml new file mode 100644 index 000000000000..cb1c8aa69b1b --- /dev/null +++ b/settings.xml @@ -0,0 +1,11 @@ + + + + + + postgres + heroku + + diff --git a/src/main/resources/db/heroku.properties b/src/main/resources/db/heroku.properties new file mode 100644 index 000000000000..02667eefeade --- /dev/null +++ b/src/main/resources/db/heroku.properties @@ -0,0 +1,5 @@ +jpa.showSql=false +hibernate.format_sql=false +hibernate.use_sql_comments=false +database.init=false +jdbc.initLocation=initDB.sql diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties new file mode 100644 index 000000000000..d6d778f0b756 --- /dev/null +++ b/src/main/resources/db/postgres.properties @@ -0,0 +1,19 @@ +#Heroku Kislin +#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 + +#Heroku m1nd +database.url=jdbc:postgresql://ec2-54-217-202-110.eu-west-1.compute.amazonaws.com:5432/d1ig636c20l8rb?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory +database.username=gfqoqzmbtjknwv +database.password=OX9qCLuo6vJZWCIta0vjr0h0m3 + +database.init=true +jdbc.initLocation=initDB.sql +jpa.showSql=true +hibernate.format_sql=true +hibernate.use_sql_comments=true +database.driverClassName=org.postgresql.Driver +#database.url=jdbc:postgresql://localhost:5432/topjava +#database.username=postgres +#database.password=passwd diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 855799c3310f..802c4c515d04 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -63,6 +63,31 @@ + + + + + + + + + + + + + + + + + WEB-INF/web.xml + ${catalina.base}/conf/web.xml + + + + + + + + + diff --git a/src/main/webapp/resources/images/icon-meal.png b/src/main/webapp/resources/images/icon-meal.png new file mode 100644 index 0000000000000000000000000000000000000000..b4fc54ad01294abb103d9238c12b1826a9240dfd GIT binary patch literal 1898 zcmV-w2bK7VP)PS&+k>eS5@~O7WLfN z#z>r1)zQ7@JLlXpd}L<$FKLxT1mO7i`0((sD2l48LI|y1MB+H^bUOWhKS`1&t~@IA z1b~^D88e&NXf)c{*(uAi)iCqP{SZP3!OTRY*Xx}A^;#TU>54SHnYXW#g&y6RSh99^HTs$tZ#2`udlC*h;z;d0}zWj3TB}R zFn|flWT07)I)DiT%x1GFidI)w^E?kB5K$Y-9N@(I#>U3x=4P6v0K6d;BWwP3q;Kx$ z_a^B<71hQG`=w-hwO4#}p&VpZ25mf4sA(GS{pHJ-yWQ>y2H8Vl=B=%*jg5^g%R(>^ zpsL+&Chb!3l!F8yFZyq(Lj5wj%J{QS=1FV`-RXv|gz1JiLuH$O`+tkPjc%tTC8 zFh=;G>b&~)rPnv3Ze$)5$8pm%o12^M8_WSlqmlRCIcLNj^|#;a|88fwA6w$=xgmY{ z3@O6{xx>xRAXX4z=JADR>E-A6R2)3HpZxQ}ojuAO`G7P{_xJaw(g~evn6dlC zz2#Zsoia0v$b$zDh=_^k=;$bf;GAP-3;wnBGbR$umb>)D6^v`t0WV%eKS2{4Wl=yZli zQ3XsrIx+ew0zib-1)S^QbOHd(2!?BCD8SOl{xn)BYg1tn0Wg_N*vzV`a?TO4WBc1d zR;d)!V2&CN+Nj4+MfR07QZ`!jIp;_5>jO?C-d9fIKE* zu-I^;zq+&~{XOY%# z5#czF=Rpv~PA_$85Q)&-ghCj?bBuPEs6ZGZPzDp6%=1G+xi%NO7D!e2q_RG|zaIE4tDSi`uGlo$I_gH^9nL%#7Qm zwy>};o6WrUPPl}7=~DWQ)4QejGoqQdqOrEijv55RG{6antne1h4gzPPym{gH%CI-} zh+{V%kK;IQ>CAj(647`(-rL(-UVf&o8)A;F{=@y^)=no$GbJ3?U|1qpi10xPAv8v@ zz){Gm34shDAOyfb1Qrm7fW`-5YB|H<;o)E~NYk{<`O}$Qi%}HC?(S}yru}~3 zIcGo!W-x#-pHg5VCNi_KEDsJ2h-fevsOqEEp9DC8X(Ky0I4FujRns(`7d6kT&U;_i z^>jKd%aVwCy