From 73651e099e89f997acf8059f9f1e79ff95c43be9 Mon Sep 17 00:00:00 2001 From: Grigory Kislin Date: Tue, 21 Feb 2017 16:28:00 +0300 Subject: [PATCH 01/32] Add description --- README.md | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ ReleaseNotes.md | 82 ++++++++++++++++++++++++ cv.md | 83 +++++++++++++++++++++++++ description.md | 67 ++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 README.md create mode 100644 ReleaseNotes.md create mode 100644 cv.md create mode 100644 description.md diff --git a/README.md b/README.md new file mode 100644 index 000000000000..37f722cffed5 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +Java Enterprise Online Project +=============================== +Наиболее востребованные технологии /инструменты / фреймворки Java Enterprise: +Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + plugins. + + Когда вы слышите что-то, вы забываете это. + Когда вы видите что-то, вы запоминаете это. + Но только когда вы начинаете делать это, + вы начинаете понимать это + + Старинная китайская поговорка + +## Описание и план проекта +### Демо разрабатываемого приложения +### [Изменения проекта (Release Notes)](ReleaseNotes.md) +### Требования к участникам, Wiki +### Составление резюме, подготовка к интервью, поиск работы + +Вводное занятие +=============== +## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Осваиваем Java Enterprise. Трудоустройство. Ответы на вопросы. +- Слайды презентации +- Java Tools and Technologies Landscape for 2014 +- Java Tools and Technologies Landscape Report 2016 +- Из юниоров в разработчики: получаем первую работу + +#### Spring Pet-Clinic +- Spring PetClinic Sample Application +- Demo +- Presentation + +## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Системы управления версиями. Git. +- **Wiki по ведению проекта в Git** +- Система управления версиями. VCS/DVSC. +- Ресурсы: + - Интерактивная Git обучалка + - Еще одна интерактивная обучалка, по русски + - Книга Git + - Working with remote repositories + - Видео по обучению Git + - Git Overview + - Видеокурс по Git + +## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Работа с проектом (выполнять инструкции) +- **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 + - Выставить кодировку UTF-8 в консоли + - Поставить кодировку UTF-8 + - Поменять фонт по умолчанию (DejaVu) +- По ходу видео сделать Apply Patch... скаченного патча Prepare_ to_ HW0.patch +- Закоммитить и запушить изменения (commit + push) +- Сделать ветку домашнего задания +- Выполнить задание и залить на GitHub (commit + push) +- Переключиться в основную ветку проекта master. + +## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Домашнее задание HW0 + +- Java 8 Date and Time API +- Алгоритмы и структуры данных для начинающих: сложность алгоритмов +- Time complexity +- Временная сложность алгоритма +- Вычислительная сложность +``` +Реализовать метод UserMealsUtil.getFilteredWithExceeded: +- должны возвращаться только записи между startTime и endTime +- поле UserMealWithExceed.exceed должно показывать, + превышает ли сумма калорий за весь день параметра метода caloriesPerDay + +Т.е UserMealWithExceed - это запись одной еды, но поле exceeded будет одинаково для всех записей за этот день. + +- Проверте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды) +- Оцените Time complexity вашего алгоритма, если он O(N*N)- попробуйте сделать O(N). +``` +#### Optional (Java 8) +- Java 8: Lambda выражения +- Java 8: Потоки +- Pуководство по Java 8 Stream +- Лямбда-выражения в Java 8 +- A Guide to Java 8 +- Шпаргалка Java Stream API +- Алексея Владыкин: Элементы функционального программирования в Java +- Yakov Fain о новом в Java 8 +- stream.map vs forEach + +``` +Сделать реализация через Java 8 Stream API. Заменяйте forEach оператором stream.map(..) +``` + +## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Замечания к HW0 +- Код проекта менять можно! Одна из распространенных ошибок как в тестовых заданиях на собеседовании, так и при работе на проекте, что ничего нельзя менять. Конечно при правках в рабочем проекте обязятельно нужно проконсультироваться/проревьюится у авторов кода (находится по истории VCS) +- Наследовать `UserMealWithExceed` от `UserMeal` я не буду, т.к. это разные сущности: Transfer Object и Entity. Мы будет их проходить на 2м уроке. +- Правильная реализация должна быть простой и красивой, можно сделать 2-мя способами: через стримы и через циклы. Сложность должна быть O(N), т.е. без вложенных стримов и циклов. +- При реализации через циклы посмотрите в `Map` на методы `getOrDefault` или `merge` +- При реализации через `Stream` заменяйте `forEach` оператором `stream.map(..)` +- Объявляйте переменные непосредственно перед использованием (если возможно - сразу с инициализацией). При объявлении коллекций используйте тип переменной - интерфейс (Map, List, ..) +- Если IDEA предлагает оптимизацию (желтым подчеркивает), нарпимер заменить лямбду на метод-референс, соглашайтесь (Alt+Enter) +- Пользуйтесь форматированием кода в IDEA: `Alt+Ctrl+L` +- Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), делайте ему revert. +- `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. + +----- + +### Полезные ресурсы +> ВНИМАНИЕ: основы JavaSсript необходимы для понимания проекта, начиная с 8-го занятия! + +Все остальное - опционально. + +#### HTML, JavaScript, CSS +- HTML, CSS, JAVASCRIPT, SQL, JQUERY, BOOTSTRAP +- Введение в программирование на JavaScript +- Стандарты кодирования для HTML, CSS и JavaScript’a +- Основы работы с HTML/CSS/JavaScript +- JavaScript - Основы +- Основы JavaScript +- Bootstrap 3 - Основы +- jQuery для начинающих + +#### Java (базовые вещи) +- Интуит. Программирование на Java +- 1й урок MasterJava: Многопоточность +- Основы Java garbage collection +- Размер Java объектов +- Введение в Java Reflection API +- Структуры данных в картинках +- Обзор java.util.concurrent.* +- Синхронизация потоков +- String literal pool +- Маленькие хитрости Java +- A Guide to Java 8 + +#### Сервлеты +- Как создать Servlet? Полное руководство. +- Простое CRUD приложение на JSP и сервлетах + +#### JDBC, SQL +- Основы SQL на примере задачи +- Уроки по JDBC +- Learn SQL +- Интуит. Основы SQL +- Try SQL +- Курс "Введение в базы данных" + +#### Разное +- Эффективная работа с кодом в IntelliJ IDEA +- Quizful- тесты онлайн +- Введение в Linux + +#### Книги +- Джошуа Блох: Java. Эффективное программирование. Второе издание +- Гамма, Хелм, Джонсон: Приемы объектно-ориентированного проектирования. Паттерны проектирования +- Редмонд Э.: Семь баз данных за семь недель. Введение в современные базы данных и идеологию NoSQL +- Brian Goetz: Java Concurrency in Practice +- G.L. McDowell: Cracking the Coding Interview diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 000000000000..5af9f258a2d9 --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,82 @@ +# TopJava Release Notes + +### Topjava 9 +- добавил + - выбор профиля базы через `ActiveProfilesResolver`/`AllActiveProfileResolver` на основе драйвера базы в classpath + - видео Cascade. Auto generate DDL. + - проверку на правильность id в Ajax/Rest контроллерах (treat IDs in REST body) + - тесты на валидацию входных значений контроллеров и зависимость на имплементацию + - Bootstrap Glyphicons +- рефакторинг + - переименовал `TimeUtil` в `DateTimeUtil` + - переименовал `ExceptionUtil` в `ValidationUtil` + - заменил валидацию `@NotEmpty` на `@NotBlank` + - заменил `CascadeType.REMOVE` на `@OnDelete` + - изменил `JdbcUserRepositoryImpl.getAll()` + - обновил jQuery до 3.x, исключил из зависимостей webjars ненужные jQuery + - cделал загрузку скриптов асинхронной + - фильтр еды сделал в [Bootstrap Panels](http://getbootstrap.com/components/#panels) + - вместо `Persistable` ввел интерфейс `HasId` и наследую от него как Entity, так и TO + +### Topjava 8 +- добавил: + - [защиту от XSS (Cross-Site Scripting)](http://stackoverflow.com/a/40644276/548473) + - интеграцию с Dependency Ci и Travis Ci + - локализацию календаря + - сводку по результатам тестов + - примеры запросов curl в `config/curl.md` + - DataTables/Bootstrap 3 integration + - тесты на профиль деплоя Heroku (общее количество JUnit тестов стало 102) +- удалил зависимость `jul-to-slf4j` +- рефакторинг + - переименовал все классы `UserMeal**` в `Meal**`, JSP + - переименовал `LoggedUser` в `AuthorizedUser` + - починил работа с PK Hibernate в случае ленивой загрузки (баг HHH-3718) + - поменял в `BaseEntity` `equals/hashCode/implements Persistable` + - в `InMemoryMealRepositoryImpl` выделил метод `getAllStream` + - перенес проверки пердусловий `Assert` из `InMemory` репозиториев в сервисы + - переименовал классы _Proxy*_ на более адекватные _Crud*_ + - поменял реализацию `JpaMealRepositoryImpl.get`, добавил в JPA модель `@BatchSize` + - вместо `@RequestMapping` ввел Spring 4.3 аннотации `@Get/Post/...Mapping` + - поменял авторизацию в тестах не-REST контроллеров + - перенес вызовы `UserUtil.prepareToSave` из `AbstractUserController` в `UserServiceImpl` + - зарефакторил обработку ошибок (`ExceptionInfoHandler`) + +### Topjava 7 +- добавил: + - [JPA 2.1 EntityGraph](https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm) + - [Jackson @JsonView](https://habrahabr.ru/post/307392/) + - валидацию объектов REST + - [i18n в JavaScript](http://stackoverflow.com/a/6242840/548473) + - проверку предусловий и видео Методы улучшения качества кода + - интеграцию с проверкой кода в Codacy + - [сравнение вермени исполнения запросов при различных meals индексах](https://drive.google.com/open?id=0B9Ye2auQ_NsFX3RLcnJCWmQ2Y0U) +- tomcat7-maven-plugin плагин перключили на Tomcat 8 (cargo-maven2-plugin) +- рефакторинг + - обработка ошибок сделал с array + - матчеров тестирования (сделал автоматические обертки и сравнение на основе передаваемого компаратора) + - вынес форматирование даты в `functions.tld` + +### Topjava 3-6 +- добавил + - [выпускной проект](https://drive.google.com/open?id=0B9Ye2auQ_NsFcG83dEVDVTVMamc) + - в таблицу meals составной индекс + - константы `Profiles.ACTIVE_DB`, `Profiles.DB_IMPLEMENTATION` + - проверки и тесты на `NotFound` для `UserMealService.getWithUser` и `UserService.getWithMeals` + - в MockMvc фильтр CharacterEncodingFilter + - защиту от межсайтовой подделки запроса, видео Межсайтовая подделка запроса (CSRF) + - ограничение на диапазон дат для фильтра еды +- рефакторинг + - UserMealsUtil, ProfileRestController, компараторов в репозитоии + - `LoggedUser` отнаследовал от `org.springframework.security.core.userdetails.User` + - переименовал `DbTest` в `AbstractServiceTest` и перенес сюда `@ActiveProfiles` + - сделал выполнение скриптов в тестах через аннотацию `@Sql` + - вместо использования id и селектора сделал обработчик `onclick` + - изменил формат ввода даты в форме без 'T' +- убрал + - `LoggerWrapper` + - Dandelion обертку к datatables +- обновил + - Hibernate до 5.x и Hibernate Validator, добавились новые зависимости и `jackson-datatype-hibernate5` + - datatables API (1.10) + - Postgres драйвер. Новый драйвер поддерживает Java 8 Time API, разделил реализацию JdbcMealRepositoryImpl на Java8 (Postgresql) и Timestamp (HSQL) diff --git a/cv.md b/cv.md new file mode 100644 index 000000000000..dd5874a3ee1a --- /dev/null +++ b/cv.md @@ -0,0 +1,83 @@ +## Составление резюме, подготовка к интервью, поиск работы + +![cv](https://cloud.githubusercontent.com/assets/13649199/10877471/93ea86b8-8157-11e5-9bfa-95e3fba75c58.jpg) + +- Научиться программировать сложнее, чем кажется + +### Составление резюме: +- Выбрать шаблон для резюме +- Идеальное резюме Junior’а +- Как продать свое резюме в 2 раза дороже +- Как правильно составить резюме +- Резюме программистов. Часть 1 (плохие) +- Резюме программистов. Часть 2 (хорошие) +- Как составить резюме на английском +- ОФОРМЛЕНИЕ IT-РЕЗЮМЕ для USA + +### Наши истории (делимся опытом и успехом) + +### Тесты/задачи онлайн: +- game: test Java skills +- Codility lesson tests +- Quizful- тесты онлайн +- Multiple-Choice Questions in Java +- LeetCode Online Judge +- Sphere online judge +- Codility programmers lessons +- Hackerrank practice coding + +### Интервью: +- Михаил Портнов. Собеседование на работу: как продать себя грамотно +- Михаил Портнов. Какие вопросы мы задаем на собеседовании? +- Михаил Портнов. Собеседование на работу: жизненный путь +- Канал: Резюме, поиск работы, интервью +- Яков Файн: Как стать профессиональным Java разработчиком +- Ответы на вопросы на собеседовании Junior Java Developer +- Список вопросов с ответами для собеседования по Java +- Сборка по вопросам на интервью +- Сборка вопросов-ответов от JavaStudy +- Тест на знание SQL +- Вопросы на собеседовании Java Junior Developer +- Java вопросы с собеседований на Android +- Сборка вопросов от JavaRush +> про clone и finalize объязательно прочтите Джошуа Блох: Java. Эффективное программирование (второе издание) + +- Cracking the Coding Interview +> Особенно обратите внимание на раздел: Часть VIII. Вопросы собеседования + + +### От себя: +- email, skype - очень желательно, чтобы по ним вы были узнаваемы. Заведите рабочие, если не так. +- написать ВЕСЬ IT опыт (исключая опыт пользователя: Windows, MS Word, Photophop, Yandex disk, Google docs, ..): технологии, какие задачи решали (конкретные), какие инструменты использовали, VCS, DB, инструменты сборки, ... включая опыт в ВУЗе. +- на English иметь желательно. Если вакансия опублинована на Englsih - шлите на нем. Часто могут на нем попросить, если работодатель иностранный. +- удобно иметь резюме где то в инете (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. + - отлично будет, если вы его еще как то сами доработаете + + +Делайте упор не на обучение, а на *участие в проекте*. Выполнение домашних заданий - это полноценное участие с написанием функционала по всем этим технологиям. На собеседовании меньше смотрят на то, что вы заканчивали, больше - на ваш опыт и то что вы знаете. + +- Ссылку на организацию можно поставить: http://javaops.ru (или в linkedin: https://www.linkedin.com/company/java-online-projects) + +### В течении проекта заполните форму соискателя "Разработчик Java" +Я ее рассылаю всем знакомым HR, уже есть пинги и трудоустройство. + +Ближе к концу курса я вышлю приглашение в нашу общую группу slack: общение на все темы IT, обсуждение вступительных заданий, вакансии, цены на рынке труда, события, помощь, интересные видео по теме и пр. + +### Подпишитесь на рассылку вакансий под себя + +### Основные сайты поиска работы: +- Яндекс агрегатор +- HH +- LinkedIn +- djinni.co (более актуально для Украины) + +## Тестовое собеседование, самые спрашиваемые темы diff --git a/description.md b/description.md new file mode 100644 index 000000000000..cbb102fa72aa --- /dev/null +++ b/description.md @@ -0,0 +1,67 @@ +#### Разработка полнофункционального 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 кодированию и архитектуре приложения. +- Каждая итерация проекта в закрепляется домашним заданием по реализации схожей функциональности. Следующее занятие начинается с разбора домашних заданий. +- Большое внимание уделяется тестированию кода: в проекте 88 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 +- Кэш Hibernate + +### Разработка WEB +- Spring кэш +- Spring Web +- JSP, JSTL, i18n +- Tomcat maven plugin. JNDI +- Spring Web MVC +- Spring Internationalization +- Тестирование Spring MVC +- REST контроллеры +- Тестирование REST контроллеров. Jackson. +- jackson-datatype-hibernate. Тестирование через матчеры. +- Тестирование через SoapUi. UTF-8 +- WebJars. +- 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 0d6d69d47aa8408ce3810bd2638dd36ae2b2ac54 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Tue, 21 Feb 2017 19:45:39 +0300 Subject: [PATCH 02/32] Update description.md --- description.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/description.md b/description.md index cbb102fa72aa..895d3b155bfb 100644 --- a/description.md +++ b/description.md @@ -53,15 +53,18 @@ Hibernate, делаем конверторы для типов LocalDateTime (Ja - jackson-datatype-hibernate. Тестирование через матчеры. - Тестирование через SoapUi. UTF-8 - WebJars. -- Bootstrap. Datatables. +- Bootstrap. jQuery datatables. - AJAX. jQuery. Notifications. - Spring Security - Spring Binding/Validation -- Работа с Datatables через Ajax. +- Работа с datatables через Ajax. - Spring Security Test - Encoding password - CSRF (добавление в проект защиты от межсайтовой подделки запроса) - form-login. Spring Security Taglib - Handler interceptor - Spring Exception Handling +- Смена локали +- Фильтрация JSON через @JsonView +- Защита от XSS (Cross Site Scripting) - Деплой в Heroku From eea2ea7efd92e520327d4fdc6c9615692b34b6d6 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Fri, 24 Feb 2017 01:43:19 +0300 Subject: [PATCH 03/32] Update ReleaseNotes.md --- ReleaseNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 5af9f258a2d9..c430cbac8f69 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -17,6 +17,7 @@ - cделал загрузку скриптов асинхронной - фильтр еды сделал в [Bootstrap Panels](http://getbootstrap.com/components/#panels) - вместо `Persistable` ввел интерфейс `HasId` и наследую от него как Entity, так и TO + - сделал универсальную обработку исключений дублирования email и dateTime ### Topjava 8 - добавил: From 00ac1d73af70e8062d4c29d1dc67aa8b9b713459 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Fri, 24 Feb 2017 16:55:40 +0300 Subject: [PATCH 04/32] Update cv.md --- cv.md | 1 - 1 file changed, 1 deletion(-) diff --git a/cv.md b/cv.md index dd5874a3ee1a..7034da33b8a5 100644 --- a/cv.md +++ b/cv.md @@ -6,7 +6,6 @@ ### Составление резюме: - Выбрать шаблон для резюме -- Идеальное резюме Junior’а - Как продать свое резюме в 2 раза дороже - Как правильно составить резюме - Резюме программистов. Часть 1 (плохие) From 3dbfd5ee05b7e0f51051500c5f72e58ab37bb42b Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Fri, 24 Feb 2017 19:04:39 +0300 Subject: [PATCH 05/32] Update cv.md --- cv.md | 1 - 1 file changed, 1 deletion(-) diff --git a/cv.md b/cv.md index 7034da33b8a5..8a731b5fd8c5 100644 --- a/cv.md +++ b/cv.md @@ -19,7 +19,6 @@ - game: test Java skills - Codility lesson tests - Quizful- тесты онлайн -- Multiple-Choice Questions in Java - LeetCode Online Judge - Sphere online judge - Codility programmers lessons From 408b9c4a47e55ec764b83f2f336ecb0a9d4ae3ea Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Sat, 25 Feb 2017 15:46:24 +0300 Subject: [PATCH 06/32] Update cv.md --- cv.md | 1 + 1 file changed, 1 insertion(+) diff --git a/cv.md b/cv.md index 8a731b5fd8c5..09991593aa9c 100644 --- a/cv.md +++ b/cv.md @@ -16,6 +16,7 @@ ### Наши истории (делимся опытом и успехом) ### Тесты/задачи онлайн: +- [Java Programming Test](https://tests4geeks.com/java) - game: test Java skills - Codility lesson tests - Quizful- тесты онлайн From 9b673d501b8dbe6b1e38d586fc531ebf02654aec Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Thu, 9 Mar 2017 00:05:30 +0300 Subject: [PATCH 07/32] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 37f722cffed5..78785efd6f33 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Пользуйтесь форматированием кода в IDEA: `Alt+Ctrl+L` - Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), делайте ему revert. - `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. +- Результаты, возвращаемые `UserMealsUtil.getFilteredWithExceeded` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом. ----- From 2131e3f6048e338ad07d961999e827d9d53023dd Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Tue, 21 Mar 2017 15:38:31 +0300 Subject: [PATCH 08/32] Update README.md --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 78785efd6f33..33e18565f87b 100644 --- a/README.md +++ b/README.md @@ -62,12 +62,6 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Переключиться в основную ветку проекта master. ## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Домашнее задание HW0 - -- Java 8 Date and Time API -- Алгоритмы и структуры данных для начинающих: сложность алгоритмов -- Time complexity -- Временная сложность алгоритма -- Вычислительная сложность ``` Реализовать метод UserMealsUtil.getFilteredWithExceeded: - должны возвращаться только записи между startTime и endTime @@ -79,7 +73,16 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Проверте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды) - Оцените Time complexity вашего алгоритма, если он O(N*N)- попробуйте сделать O(N). ``` -#### Optional (Java 8) +- Java 8 Date and Time API +- Алгоритмы и структуры данных для начинающих: сложность алгоритмов +- Time complexity +- Временная сложность алгоритма +- Вычислительная сложность + +#### Optional (Java 8 Stream API) +``` +Сделать реализация через Java 8 Stream API. Заменяйте forEach оператором stream.map(..) +``` - Java 8: Lambda выражения - Java 8: Потоки - Pуководство по Java 8 Stream @@ -89,10 +92,9 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Алексея Владыкин: Элементы функционального программирования в Java - Yakov Fain о новом в Java 8 - stream.map vs forEach - -``` -Сделать реализация через Java 8 Stream API. Заменяйте forEach оператором stream.map(..) -``` +- Дополнительно + - [Сергей Куксенко — Stream API, часть 1](https://www.youtube.com/watch?v=O8oN4KSZEXE) + - [Сергей Куксенко — Stream API, часть 2](https://www.youtube.com/watch?v=i0Jr2l3jrDA) ## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Замечания к HW0 - Код проекта менять можно! Одна из распространенных ошибок как в тестовых заданиях на собеседовании, так и при работе на проекте, что ничего нельзя менять. Конечно при правках в рабочем проекте обязятельно нужно проконсультироваться/проревьюится у авторов кода (находится по истории VCS) From abd841c19f01ae84450c59566848a4ee3a1218b5 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Wed, 22 Mar 2017 16:20:42 +0300 Subject: [PATCH 09/32] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 33e18565f87b..b9393f81723b 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), делайте ему revert. - `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. - Результаты, возвращаемые `UserMealsUtil.getFilteredWithExceeded` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом. +- Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные ----- From 2877fc5ca89369457c740b63a70390222af4dc89 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Wed, 22 Mar 2017 16:21:04 +0300 Subject: [PATCH 10/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9393f81723b..b34db3ebb631 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), делайте ему revert. - `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. - Результаты, возвращаемые `UserMealsUtil.getFilteredWithExceeded` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом. -- Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные +- Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные (лучше на english) ----- From 4e70087f61e860b3017529ee49a94fc38ac773ed Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Fri, 31 Mar 2017 11:54:58 +0300 Subject: [PATCH 11/32] Update cv.md --- cv.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cv.md b/cv.md index 09991593aa9c..04ccfacba69d 100644 --- a/cv.md +++ b/cv.md @@ -66,11 +66,18 @@ - Ссылку на организацию можно поставить: http://javaops.ru (или в linkedin: https://www.linkedin.com/company/java-online-projects) -### В течении проекта заполните форму соискателя "Разработчик Java" -Я ее рассылаю всем знакомым HR, уже есть пинги и трудоустройство. +Если рассмотриваете предложения по работе, поднимите в своем профиле этот флаг и обязательно заполните ссылку на резюме: я запустил интеграцию с HR. Ближе к концу курса я вышлю приглашение в нашу общую группу slack: общение на все темы IT, обсуждение вступительных заданий, вакансии, цены на рынке труда, события, помощь, интересные видео по теме и пр. +**Из кнала _#interview_ группы выпускников Slack**: + +> Всем привет! сходил на первое собеседование на вакансию Junior Java Developer, на следующий день получил офер) Теперь дорабатываю 2 недели на старом месте и выхожу на работу. Зарплата полностью белая, 60 тыс. рублей к начислению. Если интересно, напишу подробнее, как проходило собеседование, что спрашивали и т.д. Пока даже с трудом верится что такое возможно, теперь думаю где может быть подвох)) + +> Компания работает с гос. заказчиками, в штате примерно 120 человек. У них сейчас несколько крупных проектов, на каждый отдельная команда. В какой конкретно проект я попаду, пока не известно, сказали определят ближе к дате выхода. Собеседовала меня как обычно сначала HR, задавала общие вопросы, спрашивала что за организация Java Online Projects, и официально ли я там работаю. Я честно сказал что это стажировка, и описал вкратце как у нас происходит разработка:) Спрашивала почему решил сменить сферу деятельности и т.д. Потом пришёл руководитель одного из проектов, в команду к которому я скорее всего попаду, Он спрашивал в основном в общем, на понимание предметной области: с какими технологиями я знаком, какие для чего применял и т.д. Поспрашивал немного про микросервисную архитектуру. Сказал что ему в целом всё понятно, и сейчас позовёт тимлида, чтобы проверить мои технические навыки. Пришёл тимлид, и тоже сначала поспрашивал по технологиям в целом, потом начал задавать более детальные вопросы по некоторым технологиям. + +> Из тех что запомнил: по мавену - как добавлять зависимости, для чего нужны плагины. По спрингу - как конфигурируется спринг, какие бывают бины, как сделать бин не синглетоном. Довольно много спрашивал про Hibernate - какие бывают аннотации, связи, так же дал небольшую задачу на N+1 select. Потом задал буквально пару вопросов по Java Core - какие бывают коллекции, какие бывают сортированные мапы, что такое Hash. Вот с хэшем я немного затупил, не смог ответить почему в HashMap в качестве ключа можно добавить объекты с одинаковым хэшем. Тут тимлида позвали на какое-то важное совещание, и он закончил собеседование, сказав что по результатам мне сообщат. Я был уверен что меня не возьмут из-за неправильного ответа про хэш, и не очень чётких ответов по спринг бинам. Но на следующий день HR связалась со мной и прислала офер:) + ### Подпишитесь на рассылку вакансий под себя ### Основные сайты поиска работы: From 97b2d06b01d0cd47cd84970d9e05a705393c2bcd Mon Sep 17 00:00:00 2001 From: abwr Date: Sun, 2 Apr 2017 17:29:11 +0200 Subject: [PATCH 12/32] Prepare to HW0 --- .gitignore | 2 +- .../javawebinar/topjava/model/UserMeal.java | 33 +++++++++++++++++ .../topjava/model/UserMealWithExceed.java | 24 +++++++++++++ .../ru/javawebinar/topjava/util/TimeUtil.java | 13 +++++++ .../topjava/util/UserMealsUtil.java | 35 +++++++++++++++++++ 5 files changed, 106 insertions(+), 1 deletion(-) 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/.gitignore b/.gitignore index 6ca05c4a5184..73e6d9e3dd11 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ out target *.iml log - +*.patch diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java new file mode 100644 index 000000000000..aec9beb946cf --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java @@ -0,0 +1,33 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +/** + * GKislin + * 11.01.2015. + */ +public class UserMeal { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + public UserMeal(LocalDateTime dateTime, String description, int calories) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + } + + public LocalDateTime getDateTime() { + return dateTime; + } + + public String getDescription() { + return description; + } + + public int getCalories() { + return calories; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java new file mode 100644 index 000000000000..f4f880e4e67a --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +/** + * GKislin + * 11.01.2015. + */ +public class UserMealWithExceed { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + private final boolean exceed; + + public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + this.exceed = exceed; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java new file mode 100644 index 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..8c5f1dc60142 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java @@ -0,0 +1,35 @@ +package ru.javawebinar.topjava.util; + +import ru.javawebinar.topjava.model.UserMeal; +import ru.javawebinar.topjava.model.UserMealWithExceed; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.util.Arrays; +import java.util.List; + +/** + * GKislin + * 31.05.2015. + */ +public class UserMealsUtil { + public static void main(String[] args) { + List mealList = Arrays.asList( + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,10,0), "Завтрак", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,13,0), "Обед", 1000), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,20,0), "Ужин", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,10,0), "Завтрак", 1000), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), + new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) + ); + getFilteredWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); +// .toLocalDate(); +// .toLocalTime(); + } + + public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + // TODO return filtered list with correctly exceeded field + return null; + } +} From 06e96fa866a53b555b12828aaf9f1b418cfa8524 Mon Sep 17 00:00:00 2001 From: abwr Date: Sun, 2 Apr 2017 18:28:48 +0200 Subject: [PATCH 13/32] 1 6 logging --- pom.xml | 37 ++++++++++++++++++- .../javawebinar/topjava/web/UserServlet.java | 27 ++++++++++++++ src/main/resources/logback.xml | 29 +++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/web/UserServlet.java create mode 100644 src/main/resources/logback.xml diff --git a/pom.xml b/pom.xml index c8a1c78f3b29..68c2a884063a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ ru.javawebinar topjava - jar + war 1.0-SNAPSHOT @@ -15,11 +15,15 @@ 1.8 UTF-8 UTF-8 + + + 1.2.2 + 1.7.25 topjava - install + package org.apache.maven.plugins @@ -34,6 +38,35 @@ + + + org.slf4j + slf4j-api + ${slf4j.version} + compile + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + + ch.qos.logback + logback-classic + ${logback.version} + runtime + + + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java new file mode 100644 index 000000000000..3371178a94f6 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -0,0 +1,27 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.slf4j.LoggerFactory.getLogger; + + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class UserServlet extends HttpServlet { + private static final Logger LOG = getLogger(UserServlet.class); + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + LOG.debug("redirect to users"); + +// request.getRequestDispatcher("/users.jsp").forward(request, response); + response.sendRedirect("users.jsp"); + } +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 000000000000..e9b900b26669 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + + ${TOPJAVA_ROOT}/log/topjava.log + + + UTF-8 + %date %-5level %logger{0} [%file:%line] %msg%n + + + + + + UTF-8 + %-5level %logger{0} [%file:%line] %msg%n + + + + + + + + + + From 41aba3af0653ca4ed5724b92f259a1fd10a2adb3 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 04:08:42 +0200 Subject: [PATCH 14/32] 2 2 HW1 optional --- .../topjava/repository/MealRepository.java | 19 +++++++ src/main/webapp/meal.jsp | 51 +++++++++++++++++++ src/main/webapp/meals.jsp | 51 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/MealRepository.java create mode 100644 src/main/webapp/meal.jsp create mode 100644 src/main/webapp/meals.jsp diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java new file mode 100644 index 000000000000..f80eec996562 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -0,0 +1,19 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.Meal; + +import java.util.Collection; + +/** + * GKislin + * 06.03.2015. + */ +public interface MealRepository { + Meal save(Meal Meal); + + void delete(int id); + + Meal get(int id); + + Collection getAll(); +} diff --git a/src/main/webapp/meal.jsp b/src/main/webapp/meal.jsp new file mode 100644 index 000000000000..7541b419caf7 --- /dev/null +++ b/src/main/webapp/meal.jsp @@ -0,0 +1,51 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + Meal + + + +
+

Home

+

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

+
+ +
+ +
+
DateTime:
+
+
+
+
Description:
+
+
+
+
Calories:
+
+
+ + +
+
+ + diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp new file mode 100644 index 000000000000..45b470903183 --- /dev/null +++ b/src/main/webapp/meals.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" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> + + + Meal list + + + +
+

Home

+

Meal list

+ Add Meal +
+ + + + + + + + + + + + + + + + + + + + +
DateDescriptionCalories
+ <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> + <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> + ${fn:formatDateTime(meal.dateTime)} + ${meal.description}${meal.calories}UpdateDelete
+
+ + \ No newline at end of file From 8b18b752be3bcb1c3c0b938ff4bcc657a4cfe83e Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 04:09:24 +0200 Subject: [PATCH 15/32] 2 2 HW1 optional --- pom.xml | 21 +++++ .../javawebinar/topjava/AuthorizedUser.java | 14 +++ .../ru/javawebinar/topjava/SpringMain.java | 20 ++++ .../javawebinar/topjava/model/BaseEntity.java | 24 +++++ .../topjava/model/NamedEntity.java | 27 ++++++ .../ru/javawebinar/topjava/model/Role.java | 6 ++ .../ru/javawebinar/topjava/model/User.java | 94 +++++++++++++++++++ .../topjava/repository/UserRepository.java | 20 ++++ .../mock/InMemoryMealRepositoryImpl.java | 48 ++++++++++ .../mock/MockUserRepositoryImpl.java | 45 +++++++++ .../topjava/service/MealService.java | 4 + .../topjava/service/MealServiceImpl.java | 9 ++ .../topjava/service/UserService.java | 22 +++++ .../topjava/service/UserServiceImpl.java | 49 ++++++++++ .../topjava/util/DateTimeUtil.java | 21 +++++ .../ru/javawebinar/topjava/util/TimeUtil.java | 13 --- .../topjava/util/ValidationUtil.java | 42 +++++++++ .../util/exception/NotFoundException.java | 7 ++ .../javawebinar/topjava/web/MealServlet.java | 83 ++++++++++++++++ .../javawebinar/topjava/web/UserServlet.java | 7 +- .../topjava/web/meal/MealRestController.java | 8 ++ .../web/user/AbstractUserController.java | 51 ++++++++++ .../topjava/web/user/AdminRestController.java | 40 ++++++++ .../web/user/ProfileRestController.java | 21 +++++ src/main/resources/spring/spring-app.xml | 23 +++++ src/main/webapp/WEB-INF/tld/functions.tld | 16 ++++ 26 files changed, 718 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/AuthorizedUser.java create mode 100644 src/main/java/ru/javawebinar/topjava/SpringMain.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/repository/mock/InMemoryMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/MealService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserService.java create mode 100644 src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java delete mode 100644 src/main/java/ru/javawebinar/topjava/util/TimeUtil.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/MealServlet.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java create mode 100644 src/main/resources/spring/spring-app.xml create mode 100644 src/main/webapp/WEB-INF/tld/functions.tld diff --git a/pom.xml b/pom.xml index 68c2a884063a..711746d72c41 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ UTF-8 UTF-8 + 4.3.7.RELEASE + 1.2.2 1.7.25 @@ -60,6 +62,19 @@ runtime + + + org.springframework + spring-context + ${spring.version} + + + commons-logging + commons-logging + + + + javax.servlet @@ -67,6 +82,12 @@ 3.1.0 provided + + + javax.servlet + jstl + 1.2 + diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java new file mode 100644 index 000000000000..177a05931540 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -0,0 +1,14 @@ +package ru.javawebinar.topjava; + +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; + +public class AuthorizedUser { + + public static int id() { + return 1; + } + + public static int getCaloriesPerDay() { + return DEFAULT_CALORIES_PER_DAY; + } +} \ No newline at end of file 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..9fa857a7c46a --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -0,0 +1,20 @@ +package ru.javawebinar.topjava; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.web.user.AdminRestController; + +import java.util.Arrays; + +public class SpringMain { + public static void main(String[] args) { + // java 7 Automatic resource management + try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml")) { + System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); + AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); + adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); + } + } +} 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..f15234bd6353 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -0,0 +1,24 @@ +package ru.javawebinar.topjava.model; + +public class BaseEntity { + protected Integer id; + + public BaseEntity() { + } + + protected BaseEntity(Integer id) { + this.id = id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public boolean isNew() { + return (this.id == null); + } +} \ No newline at end of file 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..820df2de5088 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java @@ -0,0 +1,27 @@ +package ru.javawebinar.topjava.model; + +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; + } +} \ No newline at end of file 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..84d62071ad9c --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/Role.java @@ -0,0 +1,6 @@ +package ru.javawebinar.topjava.model; + +public enum Role { + ROLE_USER, + ROLE_ADMIN +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java new file mode 100644 index 000000000000..f0971e1ea1ed --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -0,0 +1,94 @@ +package ru.javawebinar.topjava.model; + +import java.util.Date; +import java.util.EnumSet; +import java.util.Set; + +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; + +public class User extends NamedEntity { + + private String email; + + private String password; + + private boolean enabled = true; + + private Date registered = new Date(); + + private Set roles; + + private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; + + public User() { + } + + public User(Integer id, String name, String email, String password, Role role, Role... roles) { + this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); + } + + public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) { + super(id, name); + this.email = email; + this.password = password; + this.caloriesPerDay = caloriesPerDay; + this.enabled = enabled; + this.roles = roles; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setPassword(String password) { + this.password = password; + } + + public Date getRegistered() { + return registered; + } + + public void setRegistered(Date registered) { + this.registered = registered; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getCaloriesPerDay() { + return caloriesPerDay; + } + + public void setCaloriesPerDay(int caloriesPerDay) { + this.caloriesPerDay = caloriesPerDay; + } + + public boolean isEnabled() { + return enabled; + } + + public Set getRoles() { + return roles; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + return "User (" + + "id=" + id + + ", email=" + email + + ", name=" + name + + ", enabled=" + enabled + + ", roles=" + roles + + ", caloriesPerDay=" + caloriesPerDay + + ')'; + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java new file mode 100644 index 000000000000..c37b84d5fd77 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -0,0 +1,20 @@ +package ru.javawebinar.topjava.repository; + +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +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(); +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java new file mode 100644 index 000000000000..f6ead5176644 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -0,0 +1,48 @@ +package ru.javawebinar.topjava.repository.mock; + +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.MealsUtil; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * GKislin + * 15.09.2015. + */ +public class InMemoryMealRepositoryImpl implements MealRepository { + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + { + MealsUtil.MEALS.forEach(this::save); + } + + @Override + public Meal save(Meal meal) { + if (meal.isNew()) { + meal.setId(counter.incrementAndGet()); + } + repository.put(meal.getId(), meal); + return meal; + } + + @Override + public void delete(int id) { + repository.remove(id); + } + + @Override + public Meal get(int id) { + return repository.get(id); + } + + @Override + public Collection getAll() { + return repository.values(); + } +} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java new file mode 100644 index 000000000000..f3704e62c2fc --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java @@ -0,0 +1,45 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; + +import java.util.Collections; +import java.util.List; + +@Repository +public class MockUserRepositoryImpl implements UserRepository { + private static final Logger LOG = LoggerFactory.getLogger(MockUserRepositoryImpl.class); + + @Override + public boolean delete(int id) { + LOG.info("delete " + id); + return true; + } + + @Override + public User save(User user) { + LOG.info("save " + user); + return user; + } + + @Override + public User get(int id) { + LOG.info("get " + id); + return null; + } + + @Override + public List getAll() { + LOG.info("getAll"); + return Collections.emptyList(); + } + + @Override + public User getByEmail(String email) { + LOG.info("getByEmail " + email); + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java new file mode 100644 index 000000000000..b63fc9b1df00 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -0,0 +1,4 @@ +package ru.javawebinar.topjava.service; + +public interface MealService { +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java new file mode 100644 index 000000000000..9017380f392b --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -0,0 +1,9 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.repository.MealRepository; + +public class MealServiceImpl implements MealService { + + private MealRepository repository; + +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java new file mode 100644 index 000000000000..b48ae667efab --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -0,0 +1,22 @@ +package ru.javawebinar.topjava.service; + + +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +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); +} \ No newline at end of file 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..e4dd5ce6b5af --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -0,0 +1,49 @@ +package ru.javawebinar.topjava.service; + +import ru.javawebinar.topjava.model.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserRepository repository; + + @Override + public User save(User user) { + return repository.save(user); + } + + @Override + public void delete(int id) { + checkNotFoundWithId(repository.delete(id), id); + } + + @Override + public User get(int id) throws NotFoundException { + return checkNotFoundWithId(repository.get(id), id); + } + + @Override + public User getByEmail(String email) throws NotFoundException { + return checkNotFound(repository.getByEmail(email), "email=" + email); + } + + @Override + public List getAll() { + return repository.getAll(); + } + + @Override + public void update(User user) { + repository.save(user); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java new file mode 100644 index 000000000000..1a0629a94589 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -0,0 +1,21 @@ +package ru.javawebinar.topjava.util; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * GKislin + * 07.01.2015. + */ +public class DateTimeUtil { + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + + public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { + return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; + } + + public static String toString(LocalDateTime ldt) { + return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java deleted file mode 100644 index 02399b7288cf..000000000000 --- a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.javawebinar.topjava.util; - -import java.time.LocalTime; - -/** - * GKislin - * 07.01.2015. - */ -public class TimeUtil { - public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java new file mode 100644 index 000000000000..cd702f58b852 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -0,0 +1,42 @@ +package ru.javawebinar.topjava.util; + + +import ru.javawebinar.topjava.model.BaseEntity; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +public class ValidationUtil { + + public static void checkNotFoundWithId(boolean found, int id) { + checkNotFound(found, "id=" + id); + } + + public static T checkNotFoundWithId(T object, int id) { + return checkNotFound(object, "id=" + id); + } + + public static T checkNotFound(T object, String msg) { + checkNotFound(object != null, msg); + return object; + } + + public static void checkNotFound(boolean found, String msg) { + if (!found) { + throw new NotFoundException("Not found entity with " + msg); + } + } + + public static void checkNew(BaseEntity entity) { + if (!entity.isNew()) { + throw new IllegalArgumentException(entity + " must be new (id=null)"); + } + } + + public static void checkIdConsistent(BaseEntity entity, int id) { +// http://stackoverflow.com/a/32728226/548473 + if (entity.isNew()) { + entity.setId(id); + } else if (entity.getId() != id) { + throw new IllegalArgumentException(entity + " must be with id=" + id); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java new file mode 100644 index 000000000000..f1e9b0e46376 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.javawebinar.topjava.util.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java new file mode 100644 index 000000000000..d3c0b7cb7345 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -0,0 +1,83 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; +import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.MealsUtil; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; + +/** + * User: gkislin + * Date: 19.08.2014 + */ +public class MealServlet extends HttpServlet { + private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); + + private MealRepository repository; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + repository = new InMemoryMealRepositoryImpl(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.setCharacterEncoding("UTF-8"); + String id = request.getParameter("id"); + + Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); + + LOG.info(meal.isNew() ? "Create {}" : "Update {}", meal); + repository.save(meal); + response.sendRedirect("meals"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String action = request.getParameter("action"); + + switch (action == null ? "all" : action) { + case "delete": + int id = getId(request); + LOG.info("Delete {}", id); + repository.delete(id); + response.sendRedirect("meals"); + break; + case "create": + case "update": + final Meal meal = action.equals("create") ? + new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : + repository.get(getId(request)); + request.setAttribute("meal", meal); + request.getRequestDispatcher("/meal.jsp").forward(request, response); + break; + case "all": + default: + LOG.info("getAll"); + request.setAttribute("meals", + MealsUtil.getWithExceeded(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + break; + } + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.valueOf(paramId); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 3371178a94f6..07ede01d611e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -18,10 +18,9 @@ public class UserServlet extends HttpServlet { private static final Logger LOG = getLogger(UserServlet.class); + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - LOG.debug("redirect to users"); - -// request.getRequestDispatcher("/users.jsp").forward(request, response); - response.sendRedirect("users.jsp"); + LOG.debug("forward to users"); + request.getRequestDispatcher("/users.jsp").forward(request, response); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java new file mode 100644 index 000000000000..ab4e8ea8bb8e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -0,0 +1,8 @@ +package ru.javawebinar.topjava.web.meal; + +import ru.javawebinar.topjava.service.MealService; + +public class MealRestController { + private MealService service; + +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java new file mode 100644 index 000000000000..b1cf8b23e56e --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -0,0 +1,51 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; + +import java.util.List; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; + +public abstract class AbstractUserController { + protected final Logger LOG = LoggerFactory.getLogger(getClass()); + + @Autowired + private UserService service; + + public List getAll() { + LOG.info("getAll"); + return service.getAll(); + } + + public User get(int id) { + LOG.info("get " + id); + return service.get(id); + } + + public User create(User user) { + LOG.info("create " + user); + checkNew(user); + return service.save(user); + } + + public void delete(int id) { + LOG.info("delete " + id); + service.delete(id); + } + + public void update(User user, int id) { + LOG.info("update " + user); + checkIdConsistent(user, id); + service.update(user); + } + + public User getByMail(String email) { + LOG.info("getByEmail " + email); + return service.getByEmail(email); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java new file mode 100644 index 000000000000..b37a8ed6c8a5 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -0,0 +1,40 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.model.User; + +import java.util.List; + +@Controller +public class AdminRestController extends AbstractUserController { + + @Override + public List getAll() { + return super.getAll(); + } + + @Override + public User get(int id) { + return super.get(id); + } + + @Override + public User create(User user) { + return super.create(user); + } + + @Override + public void delete(int id) { + super.delete(id); + } + + @Override + public void update(User user, int id) { + super.update(user, id); + } + + @Override + public User getByMail(String email) { + return super.getByMail(email); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java new file mode 100644 index 000000000000..eb4c0f3f3ada --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -0,0 +1,21 @@ +package ru.javawebinar.topjava.web.user; + +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.model.User; + +@Controller +public class ProfileRestController extends AbstractUserController { + + public User get() { + return super.get(AuthorizedUser.id()); + } + + public void delete() { + super.delete(AuthorizedUser.id()); + } + + public void update(User user) { + super.update(user, AuthorizedUser.id()); + } +} \ No newline at end of file diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml new file mode 100644 index 000000000000..96e40e902b66 --- /dev/null +++ b/src/main/resources/spring/spring-app.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tld/functions.tld b/src/main/webapp/WEB-INF/tld/functions.tld new file mode 100644 index 000000000000..d138fecdbfb5 --- /dev/null +++ b/src/main/webapp/WEB-INF/tld/functions.tld @@ -0,0 +1,16 @@ + + + + 1.0 + functions + http://topjava.javawebinar.ru/functions + + + formatDateTime + ru.javawebinar.topjava.util.DateTimeUtil + java.lang.String toString(java.time.LocalDateTime) + + From c5d7703b8e17df94b67ec8d473c87097237f3c51 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:00:32 +0200 Subject: [PATCH 16/32] HW2 --- .../ru/javawebinar/topjava/SpringMain.java | 14 +++ .../ru/javawebinar/topjava/model/Meal.java | 65 ++++++++++++++ .../{UserMeal.java => MealWithExceed.java} | 19 +++-- .../topjava/model/UserMealWithExceed.java | 24 ------ .../topjava/repository/MealRepository.java | 12 +-- .../mock/InMemoryMealRepositoryImpl.java | 76 ++++++++++++++--- .../mock/InMemoryUserRepositoryImpl.java | 85 +++++++++++++++++++ .../mock/MockUserRepositoryImpl.java | 45 ---------- .../topjava/service/MealService.java | 12 +++ .../topjava/service/MealServiceImpl.java | 27 ++++++ .../javawebinar/topjava/util/MealsUtil.java | 67 +++++++++++++++ .../topjava/util/UserMealsUtil.java | 35 -------- .../javawebinar/topjava/web/MealServlet.java | 15 ++-- .../javawebinar/topjava/web/UserServlet.java | 2 +- .../topjava/web/meal/MealRestController.java | 31 +++++++ src/main/webapp/WEB-INF/web.xml | 28 ++++++ src/main/webapp/index.html | 15 ++++ src/main/webapp/meal.jsp | 2 +- src/main/webapp/users.jsp | 10 +++ 19 files changed, 443 insertions(+), 141 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/model/Meal.java rename src/main/java/ru/javawebinar/topjava/model/{UserMeal.java => MealWithExceed.java} (66%) delete mode 100644 src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java 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 create mode 100644 src/main/java/ru/javawebinar/topjava/util/MealsUtil.java delete mode 100644 src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.html create mode 100644 src/main/webapp/users.jsp diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 9fa857a7c46a..00d39dfdab33 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -4,8 +4,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; +import java.time.LocalDateTime; +import java.time.Month; import java.util.Arrays; public class SpringMain { @@ -15,6 +19,16 @@ public static void main(String[] args) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); + + adminUserController.getAll(); + MealRestController mealRestController = appCtx.getBean(MealRestController.class); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 8, 0), "Breakfast", 50)); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 12, 0), "Lunch", 100)); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 16, 0), "Dinner", 150)); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 8, 0), "Breakfast", 500)); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 12, 0), "Lunch", 1000)); + mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 16, 0), "Dinner", 1500)); + mealRestController.getAll(); } } } diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java new file mode 100644 index 000000000000..1eff6bebe330 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -0,0 +1,65 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +public class Meal extends BaseEntity { + private Integer userId; + + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + public Meal(LocalDateTime dateTime, String description, int calories) { + this(null, dateTime, description, calories); + } + + public Meal(Integer id, LocalDateTime dateTime, String description, int calories) { + this.id = id; + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + } + + public Integer getUserId() { + return userId; + } + + public void setUserId(Integer userId) { + this.userId = userId; + } + + public LocalDate getDate() { + return dateTime.toLocalDate(); + } + + public LocalTime getTime() { + return dateTime.toLocalTime(); + } + + public LocalDateTime getDateTime() { + return dateTime; + } + + public String getDescription() { + return description; + } + + public int getCalories() { + return calories; + } + + @Override + public String toString() { + return "Meal (" + + "id=" + id + + ", userId=" + userId + + ", dateTime=" + dateTime + + ", description='" + description + '\'' + + ", calories=" + calories + + ')'; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java similarity index 66% rename from src/main/java/ru/javawebinar/topjava/model/UserMeal.java rename to src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java index aec9beb946cf..ae28a3f65874 100644 --- a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java +++ b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java @@ -6,18 +6,14 @@ * GKislin * 11.01.2015. */ -public class UserMeal { +public class MealWithExceed extends BaseEntity { private final LocalDateTime dateTime; private final String description; private final int calories; - public UserMeal(LocalDateTime dateTime, String description, int calories) { - this.dateTime = dateTime; - this.description = description; - this.calories = calories; - } + private final boolean exceed; public LocalDateTime getDateTime() { return dateTime; @@ -30,4 +26,15 @@ public String getDescription() { public int getCalories() { return calories; } + + public boolean isExceed() { + return exceed; + } + + public MealWithExceed(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/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java deleted file mode 100644 index f4f880e4e67a..000000000000 --- a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.javawebinar.topjava.model; - -import java.time.LocalDateTime; - -/** - * GKislin - * 11.01.2015. - */ -public class UserMealWithExceed { - private final LocalDateTime dateTime; - - private final String description; - - private final int calories; - - private final boolean exceed; - - public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { - this.dateTime = dateTime; - this.description = description; - this.calories = calories; - this.exceed = exceed; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index f80eec996562..7fdf3dfd741e 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -4,16 +4,12 @@ import java.util.Collection; -/** - * GKislin - * 06.03.2015. - */ public interface MealRepository { - Meal save(Meal Meal); + Meal save(Meal Meal, int userId); - void delete(int id); + boolean delete(int id, int userId); - Meal get(int id); + Meal get(int id, int userId); - Collection getAll(); + Collection getAll(int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index f6ead5176644..ddd670f78bd5 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -1,48 +1,96 @@ package ru.javawebinar.topjava.repository.mock; +import org.slf4j.Logger; +import org.springframework.stereotype.Repository; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; -import java.util.Collection; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; -/** - * GKislin - * 15.09.2015. - */ +import static org.slf4j.LoggerFactory.getLogger; + +@Repository public class InMemoryMealRepositoryImpl implements MealRepository { + private static final Logger LOG = getLogger(InMemoryMealRepositoryImpl.class); + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); { - MealsUtil.MEALS.forEach(this::save); + MealsUtil.MEALS.forEach(m->this.save(m, 2)); } @Override - public Meal save(Meal meal) { + public Meal save(Meal meal, int userId) { if (meal.isNew()) { meal.setId(counter.incrementAndGet()); + meal.setUserId(userId); } + + LOG.info("Save meal " + meal); repository.put(meal.getId(), meal); return meal; } @Override - public void delete(int id) { - repository.remove(id); + + public boolean delete(int id, int userId) { + LOG.info("Delete meal " + id); + Meal meal = repository.remove(id); + + if (meal == null) { + LOG.info("No meal with id " + id); + return false; + } + + if (!meal.getUserId().equals(userId)) { + LOG.info("Wrong user with id " + id); + return false; + } + + return true; } @Override - public Meal get(int id) { - return repository.get(id); + public Meal get(int id, int userId) { + LOG.info("Get meal " + id); + Meal meal = repository.get(id); + + if (meal == null) { + LOG.info("No meal with id " + id); + return null; + } + + if (!meal.getUserId().equals(userId)) { + LOG.info("Wrong user with id " + id); + return null; + } + + return meal; } @Override - public Collection getAll() { - return repository.values(); + public Collection getAll(int userId) { + LOG.info("Get all meals"); + + List result = new ArrayList<>( + repository + .values() + .stream() + .filter(meal -> meal.getUserId().equals(userId)) + .collect(Collectors.toList()) + ); + + result.sort((o1, o2) -> o2.getDateTime().compareTo(o1.getDateTime())); + + result.forEach(m -> LOG.info(m.toString())); + + return Optional.of(result).orElseGet(Collections::emptyList); } } 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..871e1f445cd4 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -0,0 +1,85 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +@Repository +public class InMemoryUserRepositoryImpl implements UserRepository { + private static final Logger LOG = LoggerFactory.getLogger(InMemoryUserRepositoryImpl.class); + + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + { + save(new User(null, "user", "user@domain.com", "pwd", Role.ROLE_USER)); + save(new User(null, "John", "john@domain.com", "pwd", Role.ROLE_USER)); + save(new User(null, "Chris", "chris@domain.com", "pwd", Role.ROLE_USER)); + save(new User(null, "admin", "admin@domain.com", "pwd", Role.ROLE_ADMIN, Role.ROLE_USER)); + } + + @Override + public boolean delete(int id) { + LOG.info("Delete user with id " + id); + + if (!repository.containsKey(id)) + return false; + + repository.remove(id); + + return true; + } + + @Override + public User save(User user) { + if (user.isNew()) + user.setId(counter.incrementAndGet()); + + repository.put(user.getId(), user); + + LOG.info("Save user " + user); + + return user; + } + + @Override + public User get(int id) { + LOG.info("Get user with id " + id); + + if (!repository.containsKey(id)) + return null; + + return repository.get(id); + } + + @Override + public List getAll() { + LOG.info("Get all users"); + + List result = new ArrayList<>(repository.values()); + result.sort((o1, o2) -> o1.getEmail().compareTo(o2.getEmail())); + result.forEach(u -> LOG.info(u.toString())); + + return Optional.of(result).orElseGet(Collections::emptyList); + } + + @Override + public User getByEmail(String email) { + User user = repository.values().stream() + .filter(x -> email.equals(x.getEmail())) + .findAny() + .orElse(null); + + LOG.info("Get user by e-mail " + email); + LOG.info(user.toString()); + + return user; + } +} 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 f3704e62c2fc..000000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/MockUserRepositoryImpl.java +++ /dev/null @@ -1,45 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.UserRepository; - -import java.util.Collections; -import java.util.List; - -@Repository -public class MockUserRepositoryImpl implements UserRepository { - private static final Logger LOG = LoggerFactory.getLogger(MockUserRepositoryImpl.class); - - @Override - public boolean delete(int id) { - LOG.info("delete " + id); - return true; - } - - @Override - public User save(User user) { - LOG.info("save " + user); - return user; - } - - @Override - public User get(int id) { - LOG.info("get " + id); - return null; - } - - @Override - public List getAll() { - LOG.info("getAll"); - return Collections.emptyList(); - } - - @Override - public User getByEmail(String email) { - LOG.info("getByEmail " + email); - return null; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index b63fc9b1df00..beb06dd4cd1e 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -1,4 +1,16 @@ package ru.javawebinar.topjava.service; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.Collection; + public interface MealService { + Meal save(Meal meal, int userId); + + void delete(int id, int userId) throws NotFoundException; + + Meal get(int id, int userId) throws NotFoundException; + + Collection getAll(int userId); } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java index 9017380f392b..8c268213734e 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -1,9 +1,36 @@ package ru.javawebinar.topjava.service; +import org.springframework.beans.factory.annotation.Autowired; +import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; +import java.util.Collection; + +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; + public class MealServiceImpl implements MealService { + @Autowired private MealRepository repository; + @Override + public Meal save(Meal meal, int userId) { + return repository.save(meal, userId); + } + + @Override + public void delete(int id, int userId) { + checkNotFoundWithId(repository.delete(id, userId), id); + } + + @Override + public Meal get(int id, int userId) { + return checkNotFoundWithId(repository.get(id, userId), id); + } + + @Override + public Collection getAll(int userId) { + return repository.getAll(userId); + } + } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java new file mode 100644 index 000000000000..38413608c518 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -0,0 +1,67 @@ +package ru.javawebinar.topjava.util; + +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.model.MealWithExceed; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.util.*; +import java.util.stream.Collectors; + + +public class MealsUtil { + public static final List MEALS = Arrays.asList( + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) + ); + + public static final int DEFAULT_CALORIES_PER_DAY = 2000; + + public static void main(String[] args) { + List mealsWithExceeded = getFilteredWithExceeded(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + mealsWithExceeded.forEach(System.out::println); + + System.out.println(getFilteredWithExceededByCycle(MEALS, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY)); + } + + public static List getWithExceeded(Collection meals, int caloriesPerDay) { + return getFilteredWithExceeded(meals, LocalTime.MIN, LocalTime.MAX, caloriesPerDay); + } + + public static List getFilteredWithExceeded(Collection meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + Map caloriesSumByDate = meals.stream() + .collect( + Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) +// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum) + ); + + return meals.stream() + .filter(meal -> DateTimeUtil.isBetween(meal.getTime(), startTime, endTime)) + .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .collect(Collectors.toList()); + } + + public static List getFilteredWithExceededByCycle(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + + final Map caloriesSumByDate = new HashMap<>(); + meals.forEach(meal -> caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum)); + + final List mealsWithExceeded = new ArrayList<>(); + meals.forEach(meal -> { + if (DateTimeUtil.isBetween(meal.getTime(), startTime, endTime)) { + mealsWithExceeded.add(createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)); + } + }); + return mealsWithExceeded; + } + + public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { + return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java deleted file mode 100644 index 8c5f1dc60142..000000000000 --- a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.javawebinar.topjava.util; - -import ru.javawebinar.topjava.model.UserMeal; -import ru.javawebinar.topjava.model.UserMealWithExceed; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.util.Arrays; -import java.util.List; - -/** - * GKislin - * 31.05.2015. - */ -public class UserMealsUtil { - public static void main(String[] args) { - List mealList = Arrays.asList( - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,10,0), "Завтрак", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,13,0), "Обед", 1000), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 30,20,0), "Ужин", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,10,0), "Завтрак", 1000), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), - new UserMeal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) - ); - getFilteredWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); -// .toLocalDate(); -// .toLocalTime(); - } - - public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { - // TODO return filtered list with correctly exceeded field - return null; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index d3c0b7cb7345..8b6152ec3862 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; import ru.javawebinar.topjava.repository.MealRepository; @@ -37,13 +38,13 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.setCharacterEncoding("UTF-8"); String id = request.getParameter("id"); - Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), + Meal meal = new Meal( LocalDateTime.parse(request.getParameter("dateTime")), request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); LOG.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal); + repository.save(meal, AuthorizedUser.id()); response.sendRedirect("meals"); } @@ -55,23 +56,23 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "delete": int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id); + repository.delete(id, AuthorizedUser.id()); response.sendRedirect("meals"); break; case "create": case "update": final Meal meal = action.equals("create") ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request)); + repository.get(getId(request), AuthorizedUser.id()); request.setAttribute("meal", meal); - request.getRequestDispatcher("/meal.jsp").forward(request, response); + request.getRequestDispatcher("meal.jsp").forward(request, response); break; case "all": default: LOG.info("getAll"); request.setAttribute("meals", - MealsUtil.getWithExceeded(repository.getAll(), MealsUtil.DEFAULT_CALORIES_PER_DAY)); - request.getRequestDispatcher("/meals.jsp").forward(request, response); + MealsUtil.getWithExceeded(repository.getAll(AuthorizedUser.id()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.getRequestDispatcher("meals.jsp").forward(request, response); break; } } diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index 07ede01d611e..f4e4bea07511 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -21,6 +21,6 @@ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LOG.debug("forward to users"); - request.getRequestDispatcher("/users.jsp").forward(request, response); + request.getRequestDispatcher("users.jsp").forward(request, response); } } diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index ab4e8ea8bb8e..ee6ede4a57dd 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,8 +1,39 @@ package ru.javawebinar.topjava.web.meal; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.model.MealWithExceed; import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.util.MealsUtil; +import java.util.Collection; + +import static org.slf4j.LoggerFactory.getLogger; + +@Controller public class MealRestController { + private static final Logger LOG = getLogger(MealRestController.class); + + @Autowired private MealService service; + public Meal save(Meal meal) { return service.save(meal, AuthorizedUser.id()); } + + public void delete(int id) { + service.delete(id, AuthorizedUser.id()); + } + + public Meal get(int id) { + return service.get(id, AuthorizedUser.id()); + } + + public Collection getAll() { + Collection result = MealsUtil.getWithExceeded(service.getAll(AuthorizedUser.id()), AuthorizedUser.getCaloriesPerDay()); + result.forEach(u->LOG.info(u.toString())); + return result; + } + } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..b3ad141ec858 --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + Topjava + + + userServlet + ru.javawebinar.topjava.web.UserServlet + 0 + + + userServlet + /users + + + + mealServlet + ru.javawebinar.topjava.web.MealServlet + 0 + + + mealServlet + /meals + + + \ No newline at end of file diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 000000000000..589a1e5548be --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,15 @@ + + + + + Java Enterprise (Topjava) + + +

Проект Java Enterprise (Topjava)

+
+ + + \ No newline at end of file diff --git a/src/main/webapp/meal.jsp b/src/main/webapp/meal.jsp index 7541b419caf7..f23bfc9fcf73 100644 --- a/src/main/webapp/meal.jsp +++ b/src/main/webapp/meal.jsp @@ -32,7 +32,7 @@
-
DateTime:
+
Date & Time:
diff --git a/src/main/webapp/users.jsp b/src/main/webapp/users.jsp new file mode 100644 index 000000000000..8272834ce0db --- /dev/null +++ b/src/main/webapp/users.jsp @@ -0,0 +1,10 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + User list + + +

Home

+

User list

+ + \ No newline at end of file From f5de65d5ca39041c32302fd028bdb5fcad4cb23c Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:15:32 +0200 Subject: [PATCH 17/32] 3 0 1 correction --- .../javawebinar/topjava/service/UserServiceImpl.java | 2 +- .../ru/javawebinar/topjava/util/DateTimeUtil.java | 6 +----- .../java/ru/javawebinar/topjava/web/MealServlet.java | 2 +- .../topjava/web/user/AbstractUserController.java | 12 ++++++------ 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index e4dd5ce6b5af..dd121d8ec1b0 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -23,7 +23,7 @@ public User save(User user) { } @Override - public void delete(int id) { + public void delete(int id) throws NotFoundException { checkNotFoundWithId(repository.delete(id), id); } diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 1a0629a94589..5de28849657a 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -4,12 +4,8 @@ import java.time.LocalTime; import java.time.format.DateTimeFormatter; -/** - * GKislin - * 07.01.2015. - */ public class DateTimeUtil { - public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 8b6152ec3862..a2b6294262c0 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -61,7 +61,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t break; case "create": case "update": - final Meal meal = action.equals("create") ? + final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : repository.get(getId(request), AuthorizedUser.id()); request.setAttribute("meal", meal); diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index b1cf8b23e56e..c978d15fe6e1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -1,8 +1,8 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.beans.factory.annotation.Autowired; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; @@ -23,29 +23,29 @@ public List getAll() { } public User get(int id) { - LOG.info("get " + id); + LOG.info("get {}", id); return service.get(id); } public User create(User user) { - LOG.info("create " + user); + LOG.info("create {}", user); checkNew(user); return service.save(user); } public void delete(int id) { - LOG.info("delete " + id); + LOG.info("delete {}", id); service.delete(id); } public void update(User user, int id) { - LOG.info("update " + user); + LOG.info("update {}", user); checkIdConsistent(user, id); service.update(user); } public User getByMail(String email) { - LOG.info("getByEmail " + email); + LOG.info("getByEmail {}", email); return service.getByEmail(email); } } \ No newline at end of file From 6bc4563b6877542e4b0ec1f2b49831c555e2e0eb Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:16:40 +0200 Subject: [PATCH 18/32] 3 0 2 constructor di --- .../javawebinar/topjava/service/UserServiceImpl.java | 10 ++++++---- .../topjava/web/user/AbstractUserController.java | 8 +++++--- .../topjava/web/user/AdminRestController.java | 7 +++++++ .../topjava/web/user/ProfileRestController.java | 7 +++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java index dd121d8ec1b0..6b540886e866 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java @@ -1,8 +1,7 @@ package ru.javawebinar.topjava.service; -import ru.javawebinar.topjava.model.User; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; import ru.javawebinar.topjava.util.exception.NotFoundException; @@ -14,8 +13,11 @@ @Service public class UserServiceImpl implements UserService { - @Autowired - private UserRepository repository; + private final UserRepository repository; + + public UserServiceImpl(UserRepository repository) { + this.repository = repository; + } @Override public User save(User 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 c978d15fe6e1..b97de4a998e3 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -2,7 +2,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; @@ -14,8 +13,11 @@ public abstract class AbstractUserController { protected final Logger LOG = LoggerFactory.getLogger(getClass()); - @Autowired - private UserService service; + private final UserService service; + + public AbstractUserController(UserService service) { + this.service = service; + } public List getAll() { LOG.info("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 b37a8ed6c8a5..aba649e40542 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -1,13 +1,20 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; import java.util.List; @Controller public class AdminRestController extends AbstractUserController { + @Autowired + public AdminRestController(UserService service) { + super(service); + } + @Override public List getAll() { return super.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 eb4c0f3f3ada..528327938972 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -1,12 +1,19 @@ package ru.javawebinar.topjava.web.user; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.service.UserService; @Controller public class ProfileRestController extends AbstractUserController { + @Autowired + public ProfileRestController(UserService service) { + super(service); + } + public User get() { return super.get(AuthorizedUser.id()); } From 01b290f93fc272f4508e9323495b000934ec9f77 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:42:36 +0200 Subject: [PATCH 19/32] 3 01 HW2 repository --- .../java/ru/javawebinar/topjava/Main.java | 7 -- .../ru/javawebinar/topjava/SpringMain.java | 22 ++--- .../ru/javawebinar/topjava/model/Meal.java | 2 +- .../topjava/repository/MealRepository.java | 17 +++- .../mock/InMemoryMealRepositoryImpl.java | 96 ------------------- .../mock/InMemoryUserRepositoryImpl.java | 85 ---------------- .../topjava/service/MealService.java | 21 +++- .../topjava/service/MealServiceImpl.java | 33 +++++-- .../topjava/{model => to}/MealWithExceed.java | 8 +- .../topjava/util/DateTimeUtil.java | 9 +- .../javawebinar/topjava/util/MealsUtil.java | 2 +- .../javawebinar/topjava/web/MealServlet.java | 7 +- .../javawebinar/topjava/web/UserServlet.java | 5 - .../topjava/web/meal/MealRestController.java | 67 ++++++++++--- src/main/webapp/meals.jsp | 2 +- 15 files changed, 134 insertions(+), 249 deletions(-) delete mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java delete mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java rename src/main/java/ru/javawebinar/topjava/{model => to}/MealWithExceed.java (89%) diff --git a/src/main/java/ru/javawebinar/topjava/Main.java b/src/main/java/ru/javawebinar/topjava/Main.java index b23a2f0961fc..e7d68b071a57 100644 --- a/src/main/java/ru/javawebinar/topjava/Main.java +++ b/src/main/java/ru/javawebinar/topjava/Main.java @@ -1,12 +1,5 @@ package ru.javawebinar.topjava; -/** - * User: gkislin - * Date: 05.08.2015 - * - * @link http://caloriesmng.herokuapp.com/ - * @link https://github.com/JavaOPs/topjava - */ public class Main { public static void main(String[] args) { System.out.format("Hello Topjava Enterprise!"); diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/main/java/ru/javawebinar/topjava/SpringMain.java index 00d39dfdab33..4aed4a14bf12 100644 --- a/src/main/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/main/java/ru/javawebinar/topjava/SpringMain.java @@ -4,13 +4,15 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.to.MealWithExceed; import ru.javawebinar.topjava.web.meal.MealRestController; import ru.javawebinar.topjava.web.user.AdminRestController; -import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; import java.time.Month; import java.util.Arrays; +import java.util.List; public class SpringMain { public static void main(String[] args) { @@ -19,16 +21,14 @@ public static void main(String[] args) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email", "password", Role.ROLE_ADMIN)); + System.out.println(); - adminUserController.getAll(); - MealRestController mealRestController = appCtx.getBean(MealRestController.class); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 8, 0), "Breakfast", 50)); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 12, 0), "Lunch", 100)); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 29, 16, 0), "Dinner", 150)); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 8, 0), "Breakfast", 500)); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 12, 0), "Lunch", 1000)); - mealRestController.save(new Meal(LocalDateTime.of(2017, Month.APRIL, 30, 16, 0), "Dinner", 1500)); - mealRestController.getAll(); + MealRestController mealController = appCtx.getBean(MealRestController.class); + List filteredMealsWithExceeded = + mealController.getBetween( + LocalDate.of(2015, Month.MAY, 30), LocalTime.of(7, 0), + LocalDate.of(2015, Month.MAY, 31), LocalTime.of(11, 0)); + filteredMealsWithExceeded.forEach(System.out::println); } } } diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 1eff6bebe330..46d41d81b475 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -18,7 +18,7 @@ public Meal(LocalDateTime dateTime, String description, int calories) { } public Meal(Integer id, LocalDateTime dateTime, String description, int calories) { - this.id = id; + super(id); this.dateTime = dateTime; this.description = description; this.calories = calories; diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index 7fdf3dfd741e..c33b74a9b3ff 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -3,13 +3,26 @@ import ru.javawebinar.topjava.model.Meal; import java.util.Collection; +import java.time.LocalDateTime; +import java.util.List; public interface MealRepository { - Meal save(Meal Meal, int userId); + Meal save(Meal Meal); + // null if updated meal do not belong to userId + Meal save(Meal meal, int userId); + void delete(int id); + // false if meal do not belong to userId boolean delete(int id, int userId); + Meal get(int id); + // null if meal do not belong to userId Meal get(int id, int userId); - Collection getAll(int userId); + Collection getAll(); + // ORDERED dateTime + List getAll(int userId); + + // ORDERED dateTime + List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java deleted file mode 100644 index ddd670f78bd5..000000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -import org.slf4j.Logger; -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.util.MealsUtil; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static org.slf4j.LoggerFactory.getLogger; - -@Repository -public class InMemoryMealRepositoryImpl implements MealRepository { - private static final Logger LOG = getLogger(InMemoryMealRepositoryImpl.class); - - private Map repository = new ConcurrentHashMap<>(); - - private AtomicInteger counter = new AtomicInteger(0); - - { - MealsUtil.MEALS.forEach(m->this.save(m, 2)); - } - - @Override - public Meal save(Meal meal, int userId) { - if (meal.isNew()) { - meal.setId(counter.incrementAndGet()); - meal.setUserId(userId); - } - - LOG.info("Save meal " + meal); - repository.put(meal.getId(), meal); - return meal; - } - - @Override - - public boolean delete(int id, int userId) { - LOG.info("Delete meal " + id); - Meal meal = repository.remove(id); - - if (meal == null) { - LOG.info("No meal with id " + id); - return false; - } - - if (!meal.getUserId().equals(userId)) { - LOG.info("Wrong user with id " + id); - return false; - } - - return true; - } - - @Override - public Meal get(int id, int userId) { - LOG.info("Get meal " + id); - Meal meal = repository.get(id); - - if (meal == null) { - LOG.info("No meal with id " + id); - return null; - } - - if (!meal.getUserId().equals(userId)) { - LOG.info("Wrong user with id " + id); - return null; - } - - return meal; - } - - @Override - public Collection getAll(int userId) { - LOG.info("Get all meals"); - - List result = new ArrayList<>( - repository - .values() - .stream() - .filter(meal -> meal.getUserId().equals(userId)) - .collect(Collectors.toList()) - ); - - result.sort((o1, o2) -> o2.getDateTime().compareTo(o1.getDateTime())); - - result.forEach(m -> LOG.info(m.toString())); - - return Optional.of(result).orElseGet(Collections::emptyList); - } -} - diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java deleted file mode 100644 index 871e1f445cd4..000000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.javawebinar.topjava.repository.mock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.model.Role; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.UserRepository; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -@Repository -public class InMemoryUserRepositoryImpl implements UserRepository { - private static final Logger LOG = LoggerFactory.getLogger(InMemoryUserRepositoryImpl.class); - - private Map repository = new ConcurrentHashMap<>(); - private AtomicInteger counter = new AtomicInteger(0); - - { - save(new User(null, "user", "user@domain.com", "pwd", Role.ROLE_USER)); - save(new User(null, "John", "john@domain.com", "pwd", Role.ROLE_USER)); - save(new User(null, "Chris", "chris@domain.com", "pwd", Role.ROLE_USER)); - save(new User(null, "admin", "admin@domain.com", "pwd", Role.ROLE_ADMIN, Role.ROLE_USER)); - } - - @Override - public boolean delete(int id) { - LOG.info("Delete user with id " + id); - - if (!repository.containsKey(id)) - return false; - - repository.remove(id); - - return true; - } - - @Override - public User save(User user) { - if (user.isNew()) - user.setId(counter.incrementAndGet()); - - repository.put(user.getId(), user); - - LOG.info("Save user " + user); - - return user; - } - - @Override - public User get(int id) { - LOG.info("Get user with id " + id); - - if (!repository.containsKey(id)) - return null; - - return repository.get(id); - } - - @Override - public List getAll() { - LOG.info("Get all users"); - - List result = new ArrayList<>(repository.values()); - result.sort((o1, o2) -> o1.getEmail().compareTo(o2.getEmail())); - result.forEach(u -> LOG.info(u.toString())); - - return Optional.of(result).orElseGet(Collections::emptyList); - } - - @Override - public User getByEmail(String email) { - User user = repository.values().stream() - .filter(x -> email.equals(x.getEmail())) - .findAny() - .orElse(null); - - LOG.info("Get user by e-mail " + email); - LOG.info(user.toString()); - - return user; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index beb06dd4cd1e..1ef6db7e9a92 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -3,14 +3,25 @@ import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; -import java.util.Collection; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; public interface MealService { - Meal save(Meal meal, int userId); + Meal get(int id, int userId) throws NotFoundException; void delete(int id, int userId) throws NotFoundException; - Meal get(int id, int userId) throws NotFoundException; + default List getBetweenDates(LocalDate startDate, LocalDate endDate, int userId) { + return getBetweenDateTimes(LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX), userId); + } + + List getBetweenDateTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId); - Collection getAll(int userId); -} \ No newline at end of file + List getAll(int userId); + + Meal update(Meal meal, int userId) throws NotFoundException; + + Meal save(Meal meal, int userId); +} diff --git a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java index 8c268213734e..6fe93c648cab 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealServiceImpl.java @@ -1,21 +1,29 @@ package ru.javawebinar.topjava.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; -import java.util.Collection; - import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; +import java.time.LocalDateTime; +import java.util.List; + +@Service public class MealServiceImpl implements MealService { @Autowired - private MealRepository repository; + private final MealRepository repository; + + @Autowired + public MealServiceImpl(MealRepository repository) { + this.repository = repository; + } @Override - public Meal save(Meal meal, int userId) { - return repository.save(meal, userId); + public Meal get(int id, int userId) { + return checkNotFoundWithId(repository.get(id, userId), id); } @Override @@ -24,13 +32,22 @@ public void delete(int id, int userId) { } @Override - public Meal get(int id, int userId) { - return checkNotFoundWithId(repository.get(id, userId), id); + public List getBetweenDateTimes(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { + return repository.getBetween(startDateTime, endDateTime, userId); } @Override - public Collection getAll(int userId) { + public List getAll(int userId) { return repository.getAll(userId); } + @Override + public Meal update(Meal meal, int userId) { + return checkNotFoundWithId(repository.save(meal, userId), meal.getId()); + } + + @Override + public Meal save(Meal meal, int userId) { + return repository.save(meal, userId); + } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/to/MealWithExceed.java similarity index 89% rename from src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java rename to src/main/java/ru/javawebinar/topjava/to/MealWithExceed.java index ae28a3f65874..87faf661b710 100644 --- a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealWithExceed.java @@ -1,11 +1,9 @@ -package ru.javawebinar.topjava.model; +package ru.javawebinar.topjava.to; + +import ru.javawebinar.topjava.model.BaseEntity; import java.time.LocalDateTime; -/** - * GKislin - * 11.01.2015. - */ public class MealWithExceed extends BaseEntity { private final LocalDateTime dateTime; diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 5de28849657a..8f57ba58cad3 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,14 +1,17 @@ package ru.javawebinar.topjava.util; +import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.format.DateTimeFormatter; public class DateTimeUtil { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); - public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) { - return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0; + public static final LocalDate MIN_DATE = LocalDate.of(1, 1, 1); + public static final LocalDate MAX_DATE = LocalDate.of(3000, 1, 1); + + public static > boolean isBetween(T value, T start, T end) { + return value.compareTo(start) >= 0 && value.compareTo(end) <= 0; } public static String toString(LocalDateTime ldt) { diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index 38413608c518..64fdf66d7d60 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.util; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.model.MealWithExceed; +import ru.javawebinar.topjava.to.MealWithExceed; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index a2b6294262c0..6d75b5062aff 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.mock.InMemoryMealRepositoryImpl; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; @@ -18,10 +17,6 @@ import java.time.temporal.ChronoUnit; import java.util.Objects; -/** - * User: gkislin - * Date: 19.08.2014 - */ public class MealServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); @@ -30,7 +25,7 @@ public class MealServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); - repository = new InMemoryMealRepositoryImpl(); + //repository = new InMemoryMealRepositoryImpl(); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index f4e4bea07511..a0313d1f3582 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -10,11 +10,6 @@ import static org.slf4j.LoggerFactory.getLogger; - -/** - * User: gkislin - * Date: 19.08.2014 - */ public class UserServlet extends HttpServlet { private static final Logger LOG = getLogger(UserServlet.class); diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index ee6ede4a57dd..175ad6a24b06 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,39 +1,80 @@ package ru.javawebinar.topjava.web.meal; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.AuthorizedUser; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.model.MealWithExceed; +import ru.javawebinar.topjava.to.MealWithExceed; import ru.javawebinar.topjava.service.MealService; +import ru.javawebinar.topjava.util.DateTimeUtil; import ru.javawebinar.topjava.util.MealsUtil; -import java.util.Collection; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; -import static org.slf4j.LoggerFactory.getLogger; +import static ru.javawebinar.topjava.util.ValidationUtil.checkIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; @Controller public class MealRestController { - private static final Logger LOG = getLogger(MealRestController.class); - @Autowired - private MealService service; + private static final Logger LOG = LoggerFactory.getLogger(MealRestController.class); public Meal save(Meal meal) { return service.save(meal, AuthorizedUser.id()); } - public void delete(int id) { - service.delete(id, AuthorizedUser.id()); + private final MealService service; + + @Autowired + public MealRestController(MealService service) { + this.service = service; } public Meal get(int id) { - return service.get(id, AuthorizedUser.id()); + int userId = AuthorizedUser.id(); + LOG.info("get meal {} for User {}", id, userId); + return service.get(id, userId); + } + + public void delete(int id) { + int userId = AuthorizedUser.id(); + LOG.info("delete meal {} for User {}", id, userId); + service.delete(id, userId); } - public Collection getAll() { - Collection result = MealsUtil.getWithExceeded(service.getAll(AuthorizedUser.id()), AuthorizedUser.getCaloriesPerDay()); - result.forEach(u->LOG.info(u.toString())); - return result; + public List getAll() { + int userId = AuthorizedUser.id(); + LOG.info("getAll for User {}", userId); + return MealsUtil.getWithExceeded(service.getAll(userId), AuthorizedUser.getCaloriesPerDay()); } + public Meal create(Meal meal) { + int userId = AuthorizedUser.id(); + LOG.info("create {} for User {}", meal, userId); + checkNew(meal); + return service.save(meal, userId); + } + + public void update(Meal meal, int id) { + int userId = AuthorizedUser.id(); + LOG.info("update {} for User {}", meal, userId); + checkIdConsistent(meal, id); + service.update(meal, userId); + } + + public List getBetween(LocalDate startDate, LocalTime startTime, LocalDate endDate, LocalTime endTime) { + int userId = AuthorizedUser.id(); + LOG.info("getBetween dates({} - {}) time({} - {}) for User {}", startDate, endDate, startTime, endTime, userId); + + return MealsUtil.getFilteredWithExceeded( + service.getBetweenDates( + startDate != null ? startDate : DateTimeUtil.MIN_DATE, + endDate != null ? endDate : DateTimeUtil.MAX_DATE, userId), + startTime != null ? startTime : LocalTime.MIN, + endTime != null ? endTime : LocalTime.MAX, + AuthorizedUser.getCaloriesPerDay() + ); + } } \ No newline at end of file diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 45b470903183..942a24513dc5 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -32,7 +32,7 @@ - + <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> From 296acf27e5fe6911d5a5befeaa93b3440b8dd7b3 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:47:41 +0200 Subject: [PATCH 20/32] 3 01 HW2 repository --- .../javawebinar/topjava/web/MealServlet.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 6d75b5062aff..1cd59eb5a9fd 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -2,10 +2,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ru.javawebinar.topjava.AuthorizedUser; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.meal.MealRestController; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -20,12 +22,20 @@ public class MealServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class); - private MealRepository repository; + private ConfigurableApplicationContext springContext; + private MealRestController mealController; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - //repository = new InMemoryMealRepositoryImpl(); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + mealController = springContext.getBean(MealRestController.class); + } + + @Override + public void destroy() { + springContext.close(); + super.destroy(); } @Override @@ -38,8 +48,13 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); - LOG.info(meal.isNew() ? "Create {}" : "Update {}", meal); - repository.save(meal, AuthorizedUser.id()); + if (meal.isNew()) { + LOG.info("Create {}", meal); + mealController.create(meal); + } else { + LOG.info("Update {}", meal); + mealController.update(meal, getId(request)); + } response.sendRedirect("meals"); } @@ -51,22 +66,21 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t case "delete": int id = getId(request); LOG.info("Delete {}", id); - repository.delete(id, AuthorizedUser.id()); + mealController.delete(id); response.sendRedirect("meals"); break; case "create": case "update": final Meal meal = "create".equals(action) ? new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : - repository.get(getId(request), AuthorizedUser.id()); + mealController.get(getId(request)); request.setAttribute("meal", meal); request.getRequestDispatcher("meal.jsp").forward(request, response); break; case "all": default: LOG.info("getAll"); - request.setAttribute("meals", - MealsUtil.getWithExceeded(repository.getAll(AuthorizedUser.id()), MealsUtil.DEFAULT_CALORIES_PER_DAY)); + request.setAttribute("meals", mealController.getAll()); request.getRequestDispatcher("meals.jsp").forward(request, response); break; } From 65fb737f420e85f47b8b66c55e28175744919f79 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:50:50 +0200 Subject: [PATCH 21/32] 3 04 HW2 optional filter --- .../topjava/util/DateTimeUtil.java | 11 ++++++ .../javawebinar/topjava/web/MealServlet.java | 36 +++++++++++++------ src/main/webapp/css/style.css | 24 +++++++++++++ src/main/webapp/meal.jsp | 19 +--------- src/main/webapp/meals.jsp | 30 +++++++++++----- 5 files changed, 83 insertions(+), 37 deletions(-) create mode 100644 src/main/webapp/css/style.css diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 8f57ba58cad3..7b89340de1f5 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -1,7 +1,10 @@ package ru.javawebinar.topjava.util; +import org.springframework.util.StringUtils; + import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; public class DateTimeUtil { @@ -17,4 +20,12 @@ public static > boolean isBetween(T value, T sta public static String toString(LocalDateTime ldt) { return ldt == null ? "" : ldt.format(DATE_TIME_FORMATTER); } + + public static LocalDate parseLocalDate(String str) { + return StringUtils.isEmpty(str) ? null : LocalDate.parse(str); + } + + public static LocalTime parseLocalTime(String str) { + return StringUtils.isEmpty(str) ? null : LocalTime.parse(str); + } } diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index 1cd59eb5a9fd..bfd1a085664b 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -5,8 +5,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.util.DateTimeUtil; import ru.javawebinar.topjava.web.meal.MealRestController; import javax.servlet.ServletConfig; @@ -15,7 +14,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.temporal.ChronoUnit; import java.util.Objects; @@ -43,19 +44,34 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) request.setCharacterEncoding("UTF-8"); String id = request.getParameter("id"); - Meal meal = new Meal( + Meal meal = new Meal(id.isEmpty() ? null : Integer.valueOf(id), LocalDateTime.parse(request.getParameter("dateTime")), request.getParameter("description"), Integer.valueOf(request.getParameter("calories"))); + String action = request.getParameter("action"); + if (action == null) { + meal = new Meal( + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.valueOf(request.getParameter("calories"))); + + if (request.getParameter("id").isEmpty()) { + LOG.info("Create {}", meal); + mealController.create(meal); + } else { + LOG.info("Update {}", meal); + mealController.update(meal, getId(request)); + } + response.sendRedirect("meals"); - if (meal.isNew()) { - LOG.info("Create {}", meal); - mealController.create(meal); - } else { - LOG.info("Update {}", meal); - mealController.update(meal, getId(request)); + } else if ("filter".equals(action)) { + LocalDate startDate = DateTimeUtil.parseLocalDate(request.getParameter("startDate")); + LocalDate endDate = DateTimeUtil.parseLocalDate(request.getParameter("endDate")); + LocalTime startTime = DateTimeUtil.parseLocalTime(request.getParameter("startTime")); + LocalTime endTime = DateTimeUtil.parseLocalTime(request.getParameter("endTime")); + request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); } - response.sendRedirect("meals"); } @Override diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css new file mode 100644 index 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/meal.jsp b/src/main/webapp/meal.jsp index f23bfc9fcf73..bff5db7860ef 100644 --- a/src/main/webapp/meal.jsp +++ b/src/main/webapp/meal.jsp @@ -4,24 +4,7 @@ Meal - +
diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp index 942a24513dc5..090370b650e8 100644 --- a/src/main/webapp/meals.jsp +++ b/src/main/webapp/meals.jsp @@ -5,20 +5,32 @@ Meal list - +

Home

Meal list

+ +
+
From Date:
+
+
+
+
To Date:
+
+
+
+
From Time:
+
+
+
+
To Time:
+
+
+ + +
Add Meal
From b21336e8f0f2a83039ebc47673ade2dbbe791b04 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 05:51:47 +0200 Subject: [PATCH 22/32] 3 05 HW2 optional select user --- .../java/ru/javawebinar/topjava/AuthorizedUser.java | 7 ++++++- .../java/ru/javawebinar/topjava/web/UserServlet.java | 7 +++++++ src/main/webapp/index.html | 12 ++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index 177a05931540..07886b10ab6e 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -3,9 +3,14 @@ import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; public class AuthorizedUser { + public static int id = 1; public static int id() { - return 1; + return id; + } + + public static void setId(int id) { + AuthorizedUser.id = id; } public static int getCaloriesPerDay() { diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java index a0313d1f3582..d6762aa891a9 100644 --- a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -1,6 +1,7 @@ package ru.javawebinar.topjava.web; import org.slf4j.Logger; +import ru.javawebinar.topjava.AuthorizedUser; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -13,6 +14,12 @@ public class UserServlet extends HttpServlet { private static final Logger LOG = getLogger(UserServlet.class); + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + int userId = Integer.valueOf(request.getParameter("userId")); + AuthorizedUser.setId(userId); + response.sendRedirect("meals"); + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LOG.debug("forward to users"); diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 589a1e5548be..b8b7bd50c4c7 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -7,9 +7,13 @@

Проект Java Enterprise (Topjava)


- +
+ Meal list of  + + + \ No newline at end of file From 6cd488e11589a08e75384e035a89f89d9d7fd66f Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:00:14 +0200 Subject: [PATCH 23/32] 3 06 HW2 bean life cycle --- .../mock/InMemoryMealRepositoryImpl.java | 117 ++++++++++++++++++ .../mock/InMemoryUserRepositoryImpl.java | 58 +++++++++ 2 files changed, 175 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java new file mode 100644 index 000000000000..4ef347cbeab9 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -0,0 +1,117 @@ +package ru.javawebinar.topjava.repository.mock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; +import ru.javawebinar.topjava.util.DateTimeUtil; +import ru.javawebinar.topjava.util.MealsUtil; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * GKislin + * 15.09.2015. + */ +@Repository +public class InMemoryMealRepositoryImpl implements MealRepository { + private static final Logger LOG = LoggerFactory.getLogger(InMemoryUserRepositoryImpl.class); + private static final Comparator MEAL_COMPARATOR = Comparator.comparing(Meal::getDateTime).reversed(); + + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + { + MealsUtil.MEALS.forEach(m->this.save(m, 2)); + } + + @Override + public Meal save(Meal Meal) { + return null; + } + + @Override + public Meal save(Meal meal, int userId) { + if (meal.isNew()) { + meal.setId(counter.incrementAndGet()); + } else { + if (this.get(meal.getId(), userId) == null) { + return null; + } + } + meal.setUserId(userId); + repository.put(meal.getId(), meal); + return meal; + } + + @Override + public void delete(int id) { + + } + + @PostConstruct + public void postConstruct() { + LOG.info("+++ PostConstruct"); + } + + @PreDestroy + public void preDestroy() { + LOG.info("+++ PreDestroy"); + } + + @Override + public boolean delete(int id, int userId) { + if (this.get(id, userId) == null) { + return false; + } + repository.remove(id); + return true; + } + + @Override + public Meal get(int id) { + return null; + } + + @Override + public Meal get(int id, int userId) { + Meal meal = repository.get(id); + return (!meal.getUserId().equals(userId)) ? null : meal; + } + + @Override + public List getAll() { + return null; + } + + @Override + public List getAll(int userId) { + return null; + } + + @Override + public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { + return null; + } + + public List getAll(int userId, LocalDate startDate, LocalDate endDate) { + List result = new ArrayList<>( + repository + .values() + .stream() + .filter(meal -> meal.getUserId()==userId) + .filter(meal -> DateTimeUtil.isBetween(meal.getDateTime().toLocalDate(), startDate, endDate)) + .collect(Collectors.toList()) + ); + result.sort((o1, o2) -> o2.getDateTime().compareTo(o1.getDateTime())); + return result; + } +} \ No newline at end of file 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..4d5b7cc45d1d --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -0,0 +1,58 @@ +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.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +@Repository +public class InMemoryUserRepositoryImpl implements UserRepository { + private Map repository = new ConcurrentHashMap<>(); + private AtomicInteger counter = new AtomicInteger(0); + + { + save(new User(null,"user","user@domain.com","pwd", Role.ROLE_USER)); + save(new User(null,"John","john@domain.com","pwd", Role.ROLE_USER)); + save(new User(null,"Chris","chris@domain.com","pwd", Role.ROLE_USER)); + save(new User(null,"admin","admin@domain.com","pwd", Role.ROLE_ADMIN, Role.ROLE_USER)); + } + + @Override + public boolean delete(int id) { + User user = repository.remove(id); + return user != null; + } + + @Override + public User save(User user) { + if (user.isNew()){ + user.setId(counter.incrementAndGet()); + } + repository.put(user.getId(),user); + return user; + } + + @Override + public User get(int id) { + return repository.get(id); + } + + @Override + public List getAll() { + List result = new ArrayList<>(repository.values()); + Collections.sort(result, (o1, o2) -> o1.getName().compareTo(o2.getName())); + return result; + } + + @Override + public User getByEmail(String email) { + return repository.values().stream() + .filter(x -> email.equals(x.getEmail())) + .findAny() + .orElse(null); + } +} \ No newline at end of file From d93a76ca7dbc3febaa0516106ba8b7266e4f1a13 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:01:52 +0200 Subject: [PATCH 24/32] 3 07 add junit --- pom.xml | 19 +++++++ .../ru/javawebinar/topjava/UserTestData.java | 12 ++++ .../web/InMemoryAdminRestControllerTest.java | 55 +++++++++++++++++++ .../ru/javawebinar/topjava/SpringMain.java | 0 .../mock/InMemoryMealRepositoryImpl.java | 9 +-- .../mock/InMemoryUserRepositoryImpl.java | 4 ++ 6 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/UserTestData.java create mode 100644 src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java rename src/{main => test}/java/ru/javawebinar/topjava/SpringMain.java (100%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java (91%) rename src/{main => test}/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java (89%) diff --git a/pom.xml b/pom.xml index 711746d72c41..0e909d6b8563 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,9 @@ 1.2.2 1.7.25 + + + 4.12 @@ -36,6 +39,14 @@ ${java.version} + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + -Dfile.encoding=UTF-8 + + @@ -88,6 +99,14 @@ jstl 1.2 + + + + junit + junit + ${junit.version} + test + diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/main/java/ru/javawebinar/topjava/UserTestData.java new file mode 100644 index 000000000000..196b09362719 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/UserTestData.java @@ -0,0 +1,12 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; + +public class UserTestData { + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + + public static final User USER = new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER); + public static final User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); +} diff --git a/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java b/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java new file mode 100644 index 000000000000..becff75a3425 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java @@ -0,0 +1,55 @@ +package ru.javawebinar.topjava.web; + +import org.junit.*; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.repository.UserRepository; +import ru.javawebinar.topjava.util.exception.NotFoundException; +import ru.javawebinar.topjava.web.user.AdminRestController; + +import java.util.Arrays; +import java.util.Collection; + +import static ru.javawebinar.topjava.UserTestData.ADMIN; +import static ru.javawebinar.topjava.UserTestData.USER; + +public class InMemoryAdminRestControllerTest { + private static ConfigurableApplicationContext appCtx; + private static AdminRestController controller; + + @BeforeClass + public static void beforeClass() { + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + System.out.println("\n" + Arrays.toString(appCtx.getBeanDefinitionNames()) + "\n"); + controller = appCtx.getBean(AdminRestController.class); + } + + @AfterClass + public static void afterClass() { + appCtx.close(); + } + + @Before + public void setUp() throws Exception { + // Re-initialize + UserRepository repository = appCtx.getBean(UserRepository.class); + repository.getAll().forEach(u -> repository.delete(u.getId())); + repository.save(USER); + repository.save(ADMIN); + } + + @Test + public void testDelete() throws Exception { + controller.delete(UserTestData.USER_ID); + Collection users = controller.getAll(); + Assert.assertEquals(users.size(), 1); + Assert.assertEquals(users.iterator().next(), ADMIN); + } + + @Test(expected = NotFoundException.class) + public void testDeleteNotFound() throws Exception { + controller.delete(10); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java similarity index 100% rename from src/main/java/ru/javawebinar/topjava/SpringMain.java rename to src/test/java/ru/javawebinar/topjava/SpringMain.java diff --git a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java similarity index 91% rename from src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java rename to src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java index 4ef347cbeab9..d4010f351f85 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryMealRepositoryImpl.java @@ -17,10 +17,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -/** - * GKislin - * 15.09.2015. - */ +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; + @Repository public class InMemoryMealRepositoryImpl implements MealRepository { private static final Logger LOG = LoggerFactory.getLogger(InMemoryUserRepositoryImpl.class); 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 89% 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 4d5b7cc45d1d..34bad4e88784 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java +++ b/src/test/java/ru/javawebinar/topjava/repository/mock/InMemoryUserRepositoryImpl.java @@ -11,6 +11,10 @@ @Repository public class InMemoryUserRepositoryImpl implements UserRepository { + private static final Comparator USER_COMPARATOR = Comparator.comparing(User::getName).thenComparing(User::getEmail); + public static final int USER_ID = 1; + public static final int ADMIN_ID = 2; + private Map repository = new ConcurrentHashMap<>(); private AtomicInteger counter = new AtomicInteger(0); From 01cedc6028cf460fca247a64578e42bfebe1ce7f Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:02:14 +0200 Subject: [PATCH 25/32] 3 08 add spring test --- pom.xml | 7 +++ ...InMemoryAdminRestControllerSpringTest.java | 50 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java diff --git a/pom.xml b/pom.xml index 0e909d6b8563..88b24219a886 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,13 @@ ${junit.version} test + + org.springframework + spring-test + ${spring.version} + test + + diff --git a/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java new file mode 100644 index 000000000000..4d98c0418e8d --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerSpringTest.java @@ -0,0 +1,50 @@ +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.SpringRunner; +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; + +@ContextConfiguration("classpath:spring/spring-app.xml") +@RunWith(SpringRunner.class) +public class InMemoryAdminRestControllerSpringTest { + + @Autowired + private AdminRestController controller; + + @Autowired + private UserRepository repository; + + @Before + public void setUp() throws Exception { + repository.getAll().forEach(u -> repository.delete(u.getId())); + repository.save(USER); + repository.save(ADMIN); + } + + @Test + public void testDelete() throws Exception { + controller.delete(UserTestData.USER_ID); + Collection users = controller.getAll(); + Assert.assertEquals(users.size(), 1); + Assert.assertEquals(users.iterator().next(), ADMIN); + } + + @Test(expected = NotFoundException.class) + public void testDeleteNotFound() throws Exception { + controller.delete(10); + } +} From b574d162c95c21942e9d3d5098ecd84c6c0829e4 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:02:35 +0200 Subject: [PATCH 26/32] 3 09 add postgresql --- pom.xml | 9 +++++++++ src/main/resources/db/postgres.properties | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/resources/db/postgres.properties diff --git a/pom.xml b/pom.xml index 88b24219a886..c08724e6b3a9 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,8 @@ 1.2.2 1.7.25 + + 42.0.0 4.12 @@ -86,6 +88,13 @@ + + + org.postgresql + postgresql + ${postgresql.version} + + javax.servlet diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties new file mode 100644 index 000000000000..44dd64eb26b8 --- /dev/null +++ b/src/main/resources/db/postgres.properties @@ -0,0 +1,7 @@ +#database.url=jdbc:postgresql://ec2-54-217-202-110.eu-west-1.compute.amazonaws.com:5432/dehm6lvm8bink0?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory +#database.username=wegxlfzjjgxaxy +#database.password=SSQyKKE_e93kiUCR-ehzMcKCxZ + +database.url=jdbc:postgresql://localhost:5432/topjava +database.username=user +database.password=password From e02a5b0f779b14b8458ae47697d2a7f13bde6620 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:03:06 +0200 Subject: [PATCH 27/32] 3 10 db implementation --- pom.xml | 5 ++ .../jdbc/JdbcUserRepositoryImpl.java | 82 +++++++++++++++++++ src/main/resources/db/initDB.sql | 25 ++++++ src/main/resources/db/populateDB.sql | 13 +++ src/main/resources/spring/spring-db.xml | 25 ++++++ 5 files changed, 150 insertions(+) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java create mode 100644 src/main/resources/db/initDB.sql create mode 100644 src/main/resources/db/populateDB.sql create mode 100644 src/main/resources/spring/spring-db.xml diff --git a/pom.xml b/pom.xml index c08724e6b3a9..5cc5cbb55267 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,11 @@ + + org.springframework + spring-jdbc + ${spring.version} + diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java new file mode 100644 index 000000000000..aa869a59a4fd --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepositoryImpl.java @@ -0,0 +1,82 @@ +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; + +@Repository +public class JdbcUserRepositoryImpl implements UserRepository { + + private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); + + private final JdbcTemplate jdbcTemplate; + + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + private final SimpleJdbcInsert insertUser; + + @Autowired + public JdbcUserRepositoryImpl(DataSource dataSource, JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { + this.insertUser = new SimpleJdbcInsert(dataSource) + .withTableName("users") + .usingGeneratedKeyColumns("id"); + + this.jdbcTemplate = jdbcTemplate; + this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; + } + + @Override + public User save(User user) { + MapSqlParameterSource map = new MapSqlParameterSource() + .addValue("id", user.getId()) + .addValue("name", user.getName()) + .addValue("email", user.getEmail()) + .addValue("password", user.getPassword()) + .addValue("registered", user.getRegistered()) + .addValue("enabled", user.isEnabled()) + .addValue("caloriesPerDay", user.getCaloriesPerDay()); + + if (user.isNew()) { + Number newKey = insertUser.executeAndReturnKey(map); + user.setId(newKey.intValue()); + } else { + namedParameterJdbcTemplate.update( + "UPDATE users SET name=:name, email=:email, password=:password, " + + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", map); + } + return user; + } + + @Override + public boolean delete(int id) { + return jdbcTemplate.update("DELETE FROM users WHERE id=?", id) != 0; + } + + @Override + public User get(int id) { + List users = jdbcTemplate.query("SELECT * FROM users WHERE id=?", ROW_MAPPER, id); + return DataAccessUtils.singleResult(users); + } + + @Override + public User getByEmail(String email) { +// return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); + List users = jdbcTemplate.query("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); + return DataAccessUtils.singleResult(users); + } + + @Override + public List getAll() { + return jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); + } +} diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql new file mode 100644 index 000000000000..a0631c79567b --- /dev/null +++ b/src/main/resources/db/initDB.sql @@ -0,0 +1,25 @@ +DROP TABLE IF EXISTS user_roles; +DROP TABLE IF EXISTS users; +DROP SEQUENCE IF EXISTS global_seq; + +CREATE SEQUENCE global_seq START 100000; + +CREATE TABLE users +( + id INTEGER PRIMARY KEY DEFAULT nextval('global_seq'), + name VARCHAR NOT NULL, + email VARCHAR NOT NULL, + password VARCHAR NOT NULL, + registered TIMESTAMP DEFAULT now(), + enabled BOOL DEFAULT TRUE, + calories_per_day INTEGER DEFAULT 2000 NOT NULL +); +CREATE UNIQUE INDEX users_unique_email_idx ON users (email); + +CREATE TABLE user_roles +( + user_id INTEGER NOT NULL, + role VARCHAR, + CONSTRAINT user_roles_idx UNIQUE (user_id, role), + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE +); diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql new file mode 100644 index 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); 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 d6b2fc4486b42aa9816d00c1dd84f1dadca04c86 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:03:28 +0200 Subject: [PATCH 28/32] 3 11 test UserService --- .../ru/javawebinar/topjava/UserTestData.java | 21 ++++- .../javawebinar/topjava/model/BaseEntity.java | 20 ++++ .../ru/javawebinar/topjava/model/User.java | 4 + .../jdbc/JdbcMealRepositoryImpl.java | 37 ++++++++ .../javawebinar/topjava/util/DbPopulator.java | 23 +++++ src/main/resources/spring/spring-app.xml | 2 +- src/main/resources/spring/spring-db.xml | 5 + .../ru/javawebinar/topjava/MealTestData.java | 10 ++ .../topjava/matcher/ModelMatcher.java | 72 ++++++++++++++ .../topjava/service/UserServiceTest.java | 94 +++++++++++++++++++ 10 files changed, 285 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java create mode 100644 src/main/java/ru/javawebinar/topjava/util/DbPopulator.java create mode 100644 src/test/java/ru/javawebinar/topjava/MealTestData.java create mode 100644 src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java create mode 100644 src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java diff --git a/src/main/java/ru/javawebinar/topjava/UserTestData.java b/src/main/java/ru/javawebinar/topjava/UserTestData.java index 196b09362719..e1151110c95e 100644 --- a/src/main/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/main/java/ru/javawebinar/topjava/UserTestData.java @@ -1,12 +1,29 @@ package ru.javawebinar.topjava; import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.matcher.ModelMatcher; import ru.javawebinar.topjava.model.User; +import java.util.Objects; + +import static ru.javawebinar.topjava.model.BaseEntity.START_SEQ; + public class UserTestData { - public static final int USER_ID = 1; - public static final int ADMIN_ID = 2; + public static final int USER_ID = START_SEQ; + public static final int ADMIN_ID = START_SEQ + 1; public static final User USER = new User(USER_ID, "User", "user@yandex.ru", "password", Role.ROLE_USER); public static final User ADMIN = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ROLE_ADMIN); + + public static final ModelMatcher MATCHER = new ModelMatcher<>( + (expected, actual) -> expected == actual || + (Objects.equals(expected.getPassword(), actual.getPassword()) + && Objects.equals(expected.getId(), actual.getId()) + && Objects.equals(expected.getName(), actual.getName()) + && Objects.equals(expected.getEmail(), actual.getEmail()) + && Objects.equals(expected.getCaloriesPerDay(), actual.getCaloriesPerDay()) + && Objects.equals(expected.isEnabled(), actual.isEnabled()) +// && Objects.equals(expected.getRoles(), actual.getRoles()) + ) + ); } diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java index f15234bd6353..bf80ce6d4de1 100644 --- a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java @@ -1,6 +1,8 @@ package ru.javawebinar.topjava.model; public class BaseEntity { + public static final int START_SEQ = 100000; + protected Integer id; public BaseEntity() { @@ -21,4 +23,22 @@ public Integer getId() { public boolean isNew() { return (this.id == null); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BaseEntity that = (BaseEntity) o; + return id != null && id.equals(that.id); + } + + @Override + public int hashCode() { + return (id == null) ? 0 : id; + } + } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index f0971e1ea1ed..39192fe2d38b 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -23,6 +23,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, DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles)); } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java new file mode 100644 index 000000000000..3e48b40b46bb --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -0,0 +1,37 @@ +package ru.javawebinar.topjava.repository.jdbc; + +import org.springframework.stereotype.Repository; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.repository.MealRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public class JdbcMealRepositoryImpl implements MealRepository { + + @Override + public Meal save(Meal meal, int userId) { + return null; + } + + @Override + public boolean delete(int id, int userId) { + return false; + } + + @Override + public Meal get(int id, int userId) { + return null; + } + + @Override + public List getAll(int userId) { + return null; + } + + @Override + public List getBetween(LocalDateTime startDate, LocalDateTime endDate, int userId) { + return null; + } +} diff --git a/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java b/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java new file mode 100644 index 000000000000..e04432818ad6 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/DbPopulator.java @@ -0,0 +1,23 @@ +package ru.javawebinar.topjava.util; + +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; + +public class DbPopulator extends ResourceDatabasePopulator { + private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + + private final DataSource dataSource; + + public DbPopulator(String scriptLocation, DataSource dataSource) { + super(RESOURCE_LOADER.getResource(scriptLocation)); + this.dataSource = dataSource; + } + + 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 96e40e902b66..04810da82e92 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..a9a60329eaee 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -6,6 +6,11 @@ + + + + + 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..c0973e8eb538 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -0,0 +1,10 @@ +package ru.javawebinar.topjava; + +import ru.javawebinar.topjava.matcher.ModelMatcher; +import ru.javawebinar.topjava.model.Meal; + +public class MealTestData { + + public static final ModelMatcher MATCHER = new ModelMatcher<>(); + +} 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..6d688e5c26b9 --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/matcher/ModelMatcher.java @@ -0,0 +1,72 @@ +package ru.javawebinar.topjava.matcher; + +import org.junit.Assert; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This test Matcher assert equality of beans and collections + * + * It wrap every entity by Wrapper before apply to Assert.assertEquals + * in order to compare them by custom comparator. + * Default comparator is String.valueOf(entity) + * + * @param : Entity + */ +public class ModelMatcher { + public interface Comparator { + boolean compare(T expected, T actual); + } + + private static final Comparator DEFAULT_COMPARATOR = + (Object expected, Object actual) -> expected == actual || String.valueOf(expected).equals(String.valueOf(actual)); + + private Comparator comparator; + + public ModelMatcher() { + this((Comparator) DEFAULT_COMPARATOR); + } + + public ModelMatcher(Comparator comparator) { + this.comparator = comparator; + } + + private class Wrapper { + private T entity; + + private Wrapper(T entity) { + this.entity = entity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Wrapper that = (Wrapper) o; + return entity != null ? comparator.compare(entity, that.entity) : that.entity == null; + } + + @Override + public String toString() { + return String.valueOf(entity); + } + } + + public void assertEquals(T expected, T actual) { + Assert.assertEquals(wrap(expected), wrap(actual)); + } + + public void assertCollectionEquals(Collection expected, Collection actual) { + Assert.assertEquals(wrap(expected), wrap(actual)); + } + + public Wrapper wrap(T entity) { + return new Wrapper(entity); + } + + public List wrap(Collection collection) { + return collection.stream().map(this::wrap).collect(Collectors.toList()); + } +} diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java new file mode 100644 index 000000000000..969e022cbdcc --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -0,0 +1,94 @@ +package ru.javawebinar.topjava.service; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.DbPopulator; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import static ru.javawebinar.topjava.UserTestData.*; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringJUnit4ClassRunner.class) +public class UserServiceTest { + + @Autowired + private UserService service; + + @Autowired + private DbPopulator dbPopulator; + + @Before + public void setUp() throws Exception { + dbPopulator.execute(); + } + + @Test + public void testSave() throws Exception { + User newUser = new User(null, "New", "new@gmail.com", "newPass", 1555, false, Collections.singleton(Role.ROLE_USER)); + User created = service.save(newUser); + newUser.setId(created.getId()); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, newUser, USER), service.getAll()); + } + + @Test(expected = DataAccessException.class) + public void testDuplicateMailSave() throws Exception { + service.save(new User(null, "Duplicate", "user@yandex.ru", "newPass", Role.ROLE_USER)); + } + + @Test + public void testDelete() throws Exception { + service.delete(USER_ID); + MATCHER.assertCollectionEquals(Collections.singletonList(ADMIN), service.getAll()); + } + + @Test(expected = NotFoundException.class) + public void testNotFoundDelete() throws Exception { + service.delete(1); + } + + @Test + public void testGet() throws Exception { + User user = service.get(USER_ID); + MATCHER.assertEquals(USER, user); + } + + @Test(expected = NotFoundException.class) + public void testGetNotFound() throws Exception { + service.get(1); + } + + @Test + public void testGetByEmail() throws Exception { + User user = service.getByEmail("user@yandex.ru"); + MATCHER.assertEquals(USER, user); + } + + @Test + public void testGetAll() throws Exception { + Collection all = service.getAll(); + MATCHER.assertCollectionEquals(Arrays.asList(ADMIN, USER), all); + } + + @Test + public void testUpdate() throws Exception { + User updated = new User(USER); + updated.setName("UpdatedName"); + updated.setCaloriesPerDay(330); + service.update(updated); + MATCHER.assertEquals(updated, service.get(USER_ID)); + } +} \ No newline at end of file From 6977d7d5c5d3dd98bf7b099a878690d583b11f3f Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:03:41 +0200 Subject: [PATCH 29/32] 3 12 test logging --- pom.xml | 7 ++++++ .../topjava/service/UserServiceTest.java | 5 +++++ src/test/resources/logback-test.xml | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index 5cc5cbb55267..7e7dd6e7115d 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,13 @@ runtime + + org.slf4j + jul-to-slf4j + ${slf4j.version} + runtime + + ch.qos.logback logback-classic diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java index 969e022cbdcc..8f3de6ec2839 100644 --- a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -3,6 +3,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; @@ -25,6 +26,10 @@ @RunWith(SpringJUnit4ClassRunner.class) public class UserServiceTest { + static { + SLF4JBridgeHandler.install(); + } + @Autowired private UserService service; diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..6b1fa3682055 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,22 @@ + + + + true + + + + + UTF-8 + %-5level %logger{0} - %msg%n + + + + + + + + + + + + \ No newline at end of file From 80d4d9cb1941bd7e8462e856c276fe5584d79f27 Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:04:00 +0200 Subject: [PATCH 30/32] 3 13 fix servlet --- src/main/java/ru/javawebinar/topjava/AuthorizedUser.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/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index 07886b10ab6e..c058757ab404 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -1,9 +1,11 @@ package ru.javawebinar.topjava; +import ru.javawebinar.topjava.model.BaseEntity; + import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; public class AuthorizedUser { - public static int id = 1; + public static int id = BaseEntity.START_SEQ; public static int id() { return id; diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java index bfd1a085664b..32cea99f28d1 100644 --- a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -29,7 +29,7 @@ public class MealServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { super.init(config); - springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml"); + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/spring-db.xml"); mealController = springContext.getBean(MealRestController.class); } diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index b8b7bd50c4c7..1dca093389d2 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -10,8 +10,8 @@

Проект Meal list of  From 5246b930981004e8d96416006ece27a1a685358a Mon Sep 17 00:00:00 2001 From: abwr Date: Fri, 28 Apr 2017 06:24:04 +0200 Subject: [PATCH 31/32] Prepare for HW3 --- pom.xml | 3 ++- .../jdbc/JdbcMealRepositoryImpl.java | 21 +++++++++++++++++++ .../web/InMemoryAdminRestControllerTest.java | 5 +++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7e7dd6e7115d..f95c5347546e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,8 @@ 42.0.0 - + + 4.12 diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java index 3e48b40b46bb..b9ac02f67f44 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepositoryImpl.java @@ -5,26 +5,47 @@ import ru.javawebinar.topjava.repository.MealRepository; import java.time.LocalDateTime; +import java.util.Collection; import java.util.List; @Repository public class JdbcMealRepositoryImpl implements MealRepository { + @Override + public Meal save(Meal Meal) { + return null; + } + @Override public Meal save(Meal meal, int userId) { return null; } + @Override + public void delete(int id) { + + } + @Override public boolean delete(int id, int userId) { return false; } + @Override + public Meal get(int id) { + return null; + } + @Override public Meal get(int id, int userId) { return null; } + @Override + public Collection getAll() { + return null; + } + @Override public List getAll(int userId) { return null; diff --git a/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java b/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java index becff75a3425..0d88d9c0cc7b 100644 --- a/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java +++ b/src/main/java/ru/javawebinar/topjava/web/InMemoryAdminRestControllerTest.java @@ -31,7 +31,7 @@ public static void afterClass() { appCtx.close(); } - @Before + //@Before public void setUp() throws Exception { // Re-initialize UserRepository repository = appCtx.getBean(UserRepository.class); @@ -48,7 +48,8 @@ public void testDelete() throws Exception { Assert.assertEquals(users.iterator().next(), ADMIN); } - @Test(expected = NotFoundException.class) + @Test + //@Test(expected = NotFoundException.class) public void testDeleteNotFound() throws Exception { controller.delete(10); } From 497f0564fd18277e81c1a7b0119ff48246d1c3f5 Mon Sep 17 00:00:00 2001 From: Anastasia Grineva Date: Fri, 28 Apr 2017 06:26:54 +0200 Subject: [PATCH 32/32] Update README.md --- README.md | 164 +----------------------------------------------------- 1 file changed, 2 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index b34db3ebb631..2556e1b0082d 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,5 @@ Java Enterprise Online Project =============================== -Наиболее востребованные технологии /инструменты / фреймворки Java Enterprise: -Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + plugins. - Когда вы слышите что-то, вы забываете это. - Когда вы видите что-то, вы запоминаете это. - Но только когда вы начинаете делать это, - вы начинаете понимать это - - Старинная китайская поговорка - -## Описание и план проекта -### Демо разрабатываемого приложения -### [Изменения проекта (Release Notes)](ReleaseNotes.md) -### Требования к участникам, Wiki -### Составление резюме, подготовка к интервью, поиск работы - -Вводное занятие -=============== -## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Осваиваем Java Enterprise. Трудоустройство. Ответы на вопросы. -- Слайды презентации -- Java Tools and Technologies Landscape for 2014 -- Java Tools and Technologies Landscape Report 2016 -- Из юниоров в разработчики: получаем первую работу - -#### Spring Pet-Clinic -- Spring PetClinic Sample Application -- Demo -- Presentation - -## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Системы управления версиями. Git. -- **Wiki по ведению проекта в Git** -- Система управления версиями. VCS/DVSC. -- Ресурсы: - - Интерактивная Git обучалка - - Еще одна интерактивная обучалка, по русски - - Книга Git - - Working with remote repositories - - Видео по обучению Git - - Git Overview - - Видеокурс по Git - -## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Работа с проектом (выполнять инструкции) -- **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 - - Выставить кодировку UTF-8 в консоли - - Поставить кодировку UTF-8 - - Поменять фонт по умолчанию (DejaVu) -- По ходу видео сделать Apply Patch... скаченного патча Prepare_ to_ HW0.patch -- Закоммитить и запушить изменения (commit + push) -- Сделать ветку домашнего задания -- Выполнить задание и залить на GitHub (commit + push) -- Переключиться в основную ветку проекта master. - -## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Домашнее задание HW0 -``` -Реализовать метод UserMealsUtil.getFilteredWithExceeded: -- должны возвращаться только записи между startTime и endTime -- поле UserMealWithExceed.exceed должно показывать, - превышает ли сумма калорий за весь день параметра метода caloriesPerDay - -Т.е UserMealWithExceed - это запись одной еды, но поле exceeded будет одинаково для всех записей за этот день. - -- Проверте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды) -- Оцените Time complexity вашего алгоритма, если он O(N*N)- попробуйте сделать O(N). -``` -- Java 8 Date and Time API -- Алгоритмы и структуры данных для начинающих: сложность алгоритмов -- Time complexity -- Временная сложность алгоритма -- Вычислительная сложность - -#### Optional (Java 8 Stream API) -``` -Сделать реализация через Java 8 Stream API. Заменяйте forEach оператором stream.map(..) -``` -- Java 8: Lambda выражения -- Java 8: Потоки -- Pуководство по Java 8 Stream -- Лямбда-выражения в Java 8 -- A Guide to Java 8 -- Шпаргалка Java Stream API -- Алексея Владыкин: Элементы функционального программирования в Java -- Yakov Fain о новом в Java 8 -- stream.map vs forEach -- Дополнительно - - [Сергей Куксенко — Stream API, часть 1](https://www.youtube.com/watch?v=O8oN4KSZEXE) - - [Сергей Куксенко — Stream API, часть 2](https://www.youtube.com/watch?v=i0Jr2l3jrDA) - -## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Замечания к HW0 -- Код проекта менять можно! Одна из распространенных ошибок как в тестовых заданиях на собеседовании, так и при работе на проекте, что ничего нельзя менять. Конечно при правках в рабочем проекте обязятельно нужно проконсультироваться/проревьюится у авторов кода (находится по истории VCS) -- Наследовать `UserMealWithExceed` от `UserMeal` я не буду, т.к. это разные сущности: Transfer Object и Entity. Мы будет их проходить на 2м уроке. -- Правильная реализация должна быть простой и красивой, можно сделать 2-мя способами: через стримы и через циклы. Сложность должна быть O(N), т.е. без вложенных стримов и циклов. -- При реализации через циклы посмотрите в `Map` на методы `getOrDefault` или `merge` -- При реализации через `Stream` заменяйте `forEach` оператором `stream.map(..)` -- Объявляйте переменные непосредственно перед использованием (если возможно - сразу с инициализацией). При объявлении коллекций используйте тип переменной - интерфейс (Map, List, ..) -- Если IDEA предлагает оптимизацию (желтым подчеркивает), нарпимер заменить лямбду на метод-референс, соглашайтесь (Alt+Enter) -- Пользуйтесь форматированием кода в IDEA: `Alt+Ctrl+L` -- Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), делайте ему revert. -- `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. -- Результаты, возвращаемые `UserMealsUtil.getFilteredWithExceeded` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом. -- Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные (лучше на english) - ------ - -### Полезные ресурсы -> ВНИМАНИЕ: основы JavaSсript необходимы для понимания проекта, начиная с 8-го занятия! - -Все остальное - опционально. - -#### HTML, JavaScript, CSS -- HTML, CSS, JAVASCRIPT, SQL, JQUERY, BOOTSTRAP -- Введение в программирование на JavaScript -- Стандарты кодирования для HTML, CSS и JavaScript’a -- Основы работы с HTML/CSS/JavaScript -- JavaScript - Основы -- Основы JavaScript -- Bootstrap 3 - Основы -- jQuery для начинающих - -#### Java (базовые вещи) -- Интуит. Программирование на Java -- 1й урок MasterJava: Многопоточность -- Основы Java garbage collection -- Размер Java объектов -- Введение в Java Reflection API -- Структуры данных в картинках -- Обзор java.util.concurrent.* -- Синхронизация потоков -- String literal pool -- Маленькие хитрости Java -- A Guide to Java 8 - -#### Сервлеты -- Как создать Servlet? Полное руководство. -- Простое CRUD приложение на JSP и сервлетах - -#### JDBC, SQL -- Основы SQL на примере задачи -- Уроки по JDBC -- Learn SQL -- Интуит. Основы SQL -- Try SQL -- Курс "Введение в базы данных" - -#### Разное -- Эффективная работа с кодом в IntelliJ IDEA -- Quizful- тесты онлайн -- Введение в Linux - -#### Книги -- Джошуа Блох: Java. Эффективное программирование. Второе издание -- Гамма, Хелм, Джонсон: Приемы объектно-ориентированного проектирования. Паттерны проектирования -- Редмонд Э.: Семь баз данных за семь недель. Введение в современные базы данных и идеологию NoSQL -- Brian Goetz: Java Concurrency in Practice -- G.L. McDowell: Cracking the Coding Interview +Used tools: +Maven / Spring / Security / JPA(Hibernate) / REST(Jackson) / Bootstrap(CSS) / jQuery + plugins.