diff --git a/README.md b/README.md
new file mode 100644
index 000000000000..786ec1b557bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,106 @@
+Java Enterprise Online Project
+===============================
+Наиболее востребованные технологии /инструменты / фреймворки Java Enterprise:
+Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + plugins.
+
+ Когда вы слышите что-то, вы забываете это.
+ Когда вы видите что-то, вы запоминаете это.
+ Но только когда вы начинаете делать это,
+ вы начинаете понимать это
+
+ Старинная китайская поговорка
+
+## Описание и план проекта
+### Демо разрабатываемого приложения
+### Требования к участникам, Wiki
+
+Вводное занятие
+===============
+##  Системы управления версиями. Git.
+
+- Система управления версиями. VCS/DVSC.
+- Ресурсы:
+ - Интерактивная Git обучалка
+ - Основы Git
+ - Working with remote repositories
+ - Видео по обучению Git
+ - Как начать работать с GitHub: быстрый старт
+ - Справочник в графическом виде
+ - Git Overview
+ - Видеокурс по Git
+
+##  Работа с проектом (выполнять инструкции)
+- **Prepare_ to_ HW0.patch (скачать и положить в каталог вашего проекта)**
+- Доступно о Java 8 Lambda
+
+## Инструкция по шагам (из видео):
+- Установить ПО (git, JDK8, IntelliJ IDEA, Maven)
+- Создать аккаунт на GitHub
+- Сделать Fork ЭТОГО проекта (https://github.com/JavaOPs/topjava)
+- Сделать локальный репозиторий проекта:
+
git clone https://github.com/[Ваш аккаунт]/topjava.git
+- Открыть и настроить проект в IDEA
+- По ходу видео сделать Apply Patch... скаченного патча Prepare_ to_ HW0.patch
+- Закоммитить и запушить изменения (commit + push)
+- Сделать ветку домашнего задания
+- Выполнить задание и залить на GitHub (commit + push)
+- Переключиться в основную ветку проекта master.
+
+## Домашнее задание HW0
+
+- Java 8 Date and Time API
+- Time complexity
+- Временная сложность алгоритма
+- Вычислительная сложность
+```
+Реализовать UserMealsUtil.getFilteredMealsWithExceeded:
+- должны возвращаться только записи между startTime и endTime
+- поле UserMealWithExceed.exceed должно показывать,
+ превышает ли сумма калорий за весь день параметра метода caloriesPerDay
+
+Т.е UserMealWithExceed - это запись одной еды, но поле exceeded будет одинаково для всех записей за этот день.
+
+- Проверте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды)
+- Оцените Time complexity вашего алгоритма (если он O(N*N)- попробуйте сделать O(N).
+```
+Optional
+
+- Java 8: Lambda выражения
+- Java 8: Потоки
+- Pуководство по Java 8 Stream
+- Лямбда-выражения в Java 8
+- Шпаргалка Java Stream API
+- stream.map vs forEach
+
+```
+Сделать реализация через Java 8 Stream API. Заменяйте forEach оператором stream.map(..)
+```
+### Составление резюме, подготовка к интервью, поиск работы
+### Ресурсы для подготовки к проекту
+#### (желательно иметь представление для лучшего усвоения материала)
+
+#### Java Web (Servlets, JSP)
+- Введение в сетевое программирование
+- Основы Java на реальном примере (Servlets, JSP)
+- Как создать Servlet? Полное руководство.
+- Технологии Java для разработки веб-приложений
+
+#### HTML, JavaScript, CSS
+- Основы работы с HTML/CSS/JavaScript
+- jQuery для начинающих
+- Уроки Bootstrap 3
+
+#### Java Core (Reflection API, JUnit)
+- Введение в Java Reflection API
+- Java JUnit
+
+#### JDBC, SQL
+- Уроки по JDBC
+- Learn SQL
+- Try SQL
+
+#### Разное
+- Интуит. Программирование на Java
+- Oracle Java tutorial на русском.
+- Эффективная работа с кодом в IntelliJ IDEA
diff --git a/config/setenv.bat b/config/setenv.bat
new file mode 100644
index 000000000000..ebbd659386f6
--- /dev/null
+++ b/config/setenv.bat
@@ -0,0 +1,4 @@
+rem run tomcat with JMX ability
+rem Run Tomcat as admin
+rem for remote connection add -Djava.rmi.server.hostname=TomcatServer_IP
+set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
diff --git a/cv.md b/cv.md
new file mode 100644
index 000000000000..95a53c28f61a
--- /dev/null
+++ b/cv.md
@@ -0,0 +1,55 @@
+## Составление резюме, подготовка к интервью, поиск работы
+
+
+
+- Научиться программировать сложнее, чем кажется
+
+### Составление резюме:
+- Идеальное резюме Junior’а
+- Как продать свое резюме в 2 раза дороже
+- Как правильно составить резюме
+- Резюме программистов. Часть 1 (плохие)
+- Резюме программистов. Часть 2 (хорошие)
+- Как составить резюме на английском
+
+### Интервью:
+- Михаил Портнов. Собеседование на работу: как продать себя грамотно
+- Михаил Портнов. Какие вопросы мы задаем на собеседовании?
+- Канал: Резюме, поиск работы, интервью
+- Яков Файн: Как стать профессиональным Java разработчиком
+- Quizful- тесты онлайн
+- Сборка по вопросам на интервью
+- Тест на знание SQL
+- Сборка вопросов от 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.
+ - отлично будет, если вы его еще как то сами доработаете
+
+- По поводу обучения на курсах- можно упомянуть в разделе образования. Но на собеседовании меньше смотрят на то, что вы заканчивали, больше - на ваш опыт и то что вы знаете.
+
+### В течении проекта заполните форму соискателя "Разработчик Java"
+Я ее рассылаю всем знакомым HR, уже есть пинги и трудоустройство.
+
+### Основные сайты поиска работы:
+- HH
+- LinkedIn
+- djinni.co (более актуально для Украины)
+- moikrug
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
diff --git a/pom.xml b/pom.xml
index c8a1c78f3b29..af41b1894a11 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.1.6
+ 1.7.18
topjava
- install
+ package
org.apache.maven.plugins
@@ -34,6 +38,47 @@
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+ compile
+
+
+
+ org.slf4j
+ jcl-over-slf4j
+ ${slf4j.version}
+ runtime
+
+
+
+ org.slf4j
+ jul-to-slf4j
+ ${slf4j.version}
+ runtime
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ runtime
+
+
+
+ javax.servlet
+ jstl
+ 1.2
+
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+ provided
+
diff --git a/src/main/java/ru/javawebinar/topjava/LoggedUser.java b/src/main/java/ru/javawebinar/topjava/LoggedUser.java
new file mode 100644
index 000000000000..eb68d99a0d6e
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/LoggedUser.java
@@ -0,0 +1,18 @@
+package ru.javawebinar.topjava;
+
+import ru.javawebinar.topjava.util.UserMealsUtil;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class LoggedUser {
+
+ public static int id() {
+ return 1;
+ }
+
+ public static int getCaloriesPerDay() {
+ return UserMealsUtil.DEFAULT_CALORIES_PER_DAY;
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java
new file mode 100644
index 000000000000..60f4b46f1d8e
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/BaseEntity.java
@@ -0,0 +1,28 @@
+package ru.javawebinar.topjava.model;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public class BaseEntity {
+ protected Integer id;
+
+ public BaseEntity() {
+ }
+
+ protected BaseEntity(Integer id) {
+ this.id = id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public boolean isNew() {
+ return (this.id == null);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java
new file mode 100644
index 000000000000..ca92e394dc98
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/NamedEntity.java
@@ -0,0 +1,31 @@
+package ru.javawebinar.topjava.model;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public class NamedEntity extends BaseEntity {
+
+ protected String name;
+
+ public NamedEntity() {
+ }
+
+ protected NamedEntity(Integer id, String name) {
+ super(id);
+ this.name = name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/Role.java b/src/main/java/ru/javawebinar/topjava/model/Role.java
new file mode 100644
index 000000000000..f0de2b4c4a81
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/Role.java
@@ -0,0 +1,10 @@
+package ru.javawebinar.topjava.model;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public enum Role {
+ ROLE_USER,
+ ROLE_ADMIN
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java
new file mode 100644
index 000000000000..34b5977a5917
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/User.java
@@ -0,0 +1,98 @@
+package ru.javawebinar.topjava.model;
+
+import ru.javawebinar.topjava.util.UserMealsUtil;
+
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public class User extends NamedEntity {
+
+ protected String email;
+
+ protected String password;
+
+ protected boolean enabled = true;
+
+ protected Date registered = new Date();
+
+ protected Set roles;
+
+ protected int caloriesPerDay = UserMealsUtil.DEFAULT_CALORIES_PER_DAY;
+
+ public User() {
+ }
+
+ public User(Integer id, String name, String email, String password, Role role, Role... roles) {
+ this(id, name, email, password, UserMealsUtil.DEFAULT_CALORIES_PER_DAY, true, EnumSet.of(role, roles));
+ }
+
+ public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Set roles) {
+ super(id, name);
+ this.email = email;
+ this.password = password;
+ this.caloriesPerDay = caloriesPerDay;
+ this.enabled = enabled;
+ this.roles = roles;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public Date getRegistered() {
+ return registered;
+ }
+
+ public void setRegistered(Date registered) {
+ this.registered = registered;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public int getCaloriesPerDay() {
+ return caloriesPerDay;
+ }
+
+ public void setCaloriesPerDay(int caloriesPerDay) {
+ this.caloriesPerDay = caloriesPerDay;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public Set getRoles() {
+ return roles;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String toString() {
+ return "User (" +
+ "id=" + id +
+ ", email=" + email +
+ ", name=" + name +
+ ", enabled=" + enabled +
+ ", roles=" + roles +
+ ", caloriesPerDay=" + caloriesPerDay +
+ ')';
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMeal.java b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java
new file mode 100644
index 000000000000..fc43ba8b8b32
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/UserMeal.java
@@ -0,0 +1,62 @@
+package ru.javawebinar.topjava.model;
+
+import java.time.LocalDateTime;
+
+/**
+ * GKislin
+ * 11.01.2015.
+ */
+public class UserMeal {
+ private Integer id;
+
+ private final LocalDateTime dateTime;
+
+ private final String description;
+
+ private final int calories;
+
+ public UserMeal(LocalDateTime dateTime, String description, int calories) {
+ this(null, dateTime, description, calories);
+ }
+
+ public UserMeal(Integer id, LocalDateTime dateTime, String description, int calories) {
+ this.id = id;
+ this.dateTime = dateTime;
+ this.description = description;
+ this.calories = calories;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public LocalDateTime getDateTime() {
+ return dateTime;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getCalories() {
+ return calories;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public boolean isNew() {
+ return id == null;
+ }
+
+ @Override
+ public String toString() {
+ return "UserMeal{" +
+ "id=" + id +
+ ", dateTime=" + dateTime +
+ ", description='" + description + '\'' +
+ ", calories=" + calories +
+ '}';
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java
new file mode 100644
index 000000000000..4f1fe2f6ae0f
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java
@@ -0,0 +1,62 @@
+package ru.javawebinar.topjava.model;
+
+import java.time.LocalDateTime;
+
+/**
+ * GKislin
+ * 11.01.2015.
+ */
+public class UserMealWithExceed {
+ protected final Integer id;
+
+ 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(null, dateTime, description, calories, exceed);
+ }
+
+ public UserMealWithExceed(Integer id, LocalDateTime dateTime, String description, int calories, boolean exceed) {
+ this.id = id;
+ this.dateTime = dateTime;
+ this.description = description;
+ this.calories = calories;
+ this.exceed = exceed;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public LocalDateTime getDateTime() {
+ return dateTime;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getCalories() {
+ return calories;
+ }
+
+ public boolean isExceed() {
+ return exceed;
+ }
+
+ @Override
+ public String toString() {
+ return "UserMealWithExceed{" +
+ "id=" + id +
+ ", dateTime=" + dateTime +
+ ", description='" + description + '\'' +
+ ", calories=" + calories +
+ ", exceed=" + exceed +
+ '}';
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java b/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java
new file mode 100644
index 000000000000..4da0792bb19d
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/repository/InMemoryUserMealRepositoryImpl.java
@@ -0,0 +1,47 @@
+package ru.javawebinar.topjava.repository;
+
+import ru.javawebinar.topjava.model.UserMeal;
+import ru.javawebinar.topjava.util.UserMealsUtil;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * GKislin
+ * 15.09.2015.
+ */
+public class InMemoryUserMealRepositoryImpl implements UserMealRepository {
+ private Map repository = new ConcurrentHashMap<>();
+ private AtomicInteger counter = new AtomicInteger(0);
+
+ {
+ UserMealsUtil.MEAL_LIST.forEach(this::save);
+ }
+
+ @Override
+ public UserMeal save(UserMeal userMeal) {
+ if (userMeal.isNew()) {
+ userMeal.setId(counter.incrementAndGet());
+ }
+ repository.put(userMeal.getId(), userMeal);
+ return userMeal;
+ }
+
+ @Override
+ public void delete(int id) {
+ repository.remove(id);
+ }
+
+ @Override
+ public UserMeal get(int id) {
+ return repository.get(id);
+ }
+
+ @Override
+ public Collection getAll() {
+ return repository.values();
+ }
+}
+
diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java
new file mode 100644
index 000000000000..ad656ea6cd19
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/repository/UserMealRepository.java
@@ -0,0 +1,19 @@
+package ru.javawebinar.topjava.repository;
+
+import ru.javawebinar.topjava.model.UserMeal;
+
+import java.util.Collection;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public interface UserMealRepository {
+ UserMeal save(UserMeal userMeal);
+
+ void delete(int id);
+
+ UserMeal get(int id);
+
+ Collection getAll();
+}
diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java
new file mode 100644
index 000000000000..24c1c45beca6
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java
@@ -0,0 +1,24 @@
+package ru.javawebinar.topjava.repository;
+
+import ru.javawebinar.topjava.model.User;
+
+import java.util.List;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public interface UserRepository {
+ User save(User user);
+
+ // false if not found
+ boolean delete(int id);
+
+ // null if not found
+ User get(int id);
+
+ // null if not found
+ User getByEmail(String email);
+
+ List getAll();
+}
diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealService.java b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java
new file mode 100644
index 000000000000..b17fec811065
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/service/UserMealService.java
@@ -0,0 +1,8 @@
+package ru.javawebinar.topjava.service;
+
+/**
+ * GKislin
+ * 15.06.2015.
+ */
+public interface UserMealService {
+}
diff --git a/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java
new file mode 100644
index 000000000000..5730b8bfb130
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/service/UserMealServiceImpl.java
@@ -0,0 +1,13 @@
+package ru.javawebinar.topjava.service;
+
+import ru.javawebinar.topjava.repository.UserMealRepository;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class UserMealServiceImpl implements UserMealService {
+
+ private UserMealRepository repository;
+
+}
diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java
new file mode 100644
index 000000000000..85c15e48f951
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java
@@ -0,0 +1,26 @@
+package ru.javawebinar.topjava.service;
+
+
+import ru.javawebinar.topjava.model.User;
+import ru.javawebinar.topjava.util.exception.NotFoundException;
+
+import java.util.List;
+
+/**
+ * User: gkislin
+ * Date: 22.08.2014
+ */
+public interface UserService {
+
+ User save(User user);
+
+ void delete(int id) throws NotFoundException;
+
+ User get(int id) throws NotFoundException;
+
+ User getByEmail(String email) throws NotFoundException;
+
+ List getAll();
+
+ void update(User user);
+}
diff --git a/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java
new file mode 100644
index 000000000000..27a4b171c406
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/service/UserServiceImpl.java
@@ -0,0 +1,41 @@
+package ru.javawebinar.topjava.service;
+
+import ru.javawebinar.topjava.model.User;
+import ru.javawebinar.topjava.repository.UserRepository;
+import ru.javawebinar.topjava.util.exception.ExceptionUtil;
+import ru.javawebinar.topjava.util.exception.NotFoundException;
+
+import java.util.List;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class UserServiceImpl implements UserService {
+
+ private UserRepository repository;
+
+ public User save(User user) {
+ return repository.save(user);
+ }
+
+ public void delete(int id) {
+ ExceptionUtil.check(repository.delete(id), id);
+ }
+
+ public User get(int id) throws NotFoundException {
+ return ExceptionUtil.check(repository.get(id), id);
+ }
+
+ public User getByEmail(String email) throws NotFoundException {
+ return ExceptionUtil.check(repository.getByEmail(email), "email=" + email);
+ }
+
+ public List getAll() {
+ return repository.getAll();
+ }
+
+ public void update(User user) {
+ repository.save(user);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.java
new file mode 100644
index 000000000000..115311fad6b7
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/util/TimeUtil.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 TimeUtil {
+ public static final DateTimeFormatter DATE_TME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+
+ public static boolean isBetween(LocalTime lt, LocalTime startTime, LocalTime endTime) {
+ return lt.compareTo(startTime) >= 0 && lt.compareTo(endTime) <= 0;
+ }
+
+ public static String toString(LocalDateTime ldt) {
+ return ldt == null ? "" : ldt.format(DATE_TME_FORMATTER);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java
new file mode 100644
index 000000000000..6f22256c7807
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java
@@ -0,0 +1,71 @@
+package ru.javawebinar.topjava.util;
+
+import ru.javawebinar.topjava.model.UserMeal;
+import ru.javawebinar.topjava.model.UserMealWithExceed;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * GKislin
+ * 31.05.2015.
+ */
+public class UserMealsUtil {
+ public static final List MEAL_LIST = Arrays.asList(
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500),
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000),
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500),
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000),
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500),
+ new UserMeal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510)
+ );
+
+ public static final int DEFAULT_CALORIES_PER_DAY = 2000;
+
+ public static void main(String[] args) {
+ List filteredMealsWithExceeded = getFilteredWithExceeded(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY);
+ filteredMealsWithExceeded.forEach(System.out::println);
+
+ System.out.println(getFilteredWithExceededByCycle(MEAL_LIST, LocalTime.of(7, 0), LocalTime.of(12, 0), DEFAULT_CALORIES_PER_DAY));
+ }
+
+ public static List getWithExceeded(Collection mealList, int caloriesPerDay) {
+ return getFilteredWithExceeded(mealList, LocalTime.MIN, LocalTime.MAX, caloriesPerDay);
+ }
+
+ public static List getFilteredWithExceeded(Collection mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
+ Map caloriesSumByDate = mealList.stream()
+ .collect(
+ Collectors.groupingBy(um -> um.getDateTime().toLocalDate(),
+ Collectors.summingInt(UserMeal::getCalories))
+ );
+
+ return mealList.stream()
+ .filter(um -> TimeUtil.isBetween(um.getDateTime().toLocalTime(), startTime, endTime))
+ .map(um -> createWithExceed(um, caloriesSumByDate.get(um.getDateTime().toLocalDate()) > caloriesPerDay))
+ .collect(Collectors.toList());
+ }
+
+ public static List getFilteredWithExceededByCycle(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
+
+ final Map caloriesSumPerDate = new HashMap<>();
+ mealList.forEach(meal -> caloriesSumPerDate.merge(meal.getDateTime().toLocalDate(), meal.getCalories(), Integer::sum));
+
+ final List mealExceeded = new ArrayList<>();
+ mealList.forEach(meal -> {
+ final LocalDateTime dateTime = meal.getDateTime();
+ if (TimeUtil.isBetween(dateTime.toLocalTime(), startTime, endTime)) {
+ mealExceeded.add(createWithExceed(meal, caloriesSumPerDate.get(dateTime.toLocalDate()) > caloriesPerDay));
+ }
+ });
+ return mealExceeded;
+ }
+
+ public static UserMealWithExceed createWithExceed(UserMeal um, boolean exceeded) {
+ return new UserMealWithExceed(um.getId(), um.getDateTime(), um.getDescription(), um.getCalories(), exceeded);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java b/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java
new file mode 100644
index 000000000000..de97182f9d87
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/util/exception/ExceptionUtil.java
@@ -0,0 +1,25 @@
+package ru.javawebinar.topjava.util.exception;
+
+
+/**
+ * User: gkislin
+ * Date: 14.05.2014
+ */
+public class ExceptionUtil {
+ public static void check(boolean found, int id) {
+ check(found, "id=" + id);
+ }
+
+ public static T check(T object, int id) {
+ return check(object, "id=" + id);
+ }
+
+ public static T check(T object, String msg) {
+ check(object != null, msg);
+ return object;
+ }
+
+ public static void check(boolean found, String msg) {
+ if (!found) throw new NotFoundException("Not found entity with " + msg);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java
new file mode 100644
index 000000000000..7a770f0db834
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/util/exception/NotFoundException.java
@@ -0,0 +1,11 @@
+package ru.javawebinar.topjava.util.exception;
+
+/**
+ * User: gkislin
+ * Date: 19.08.2014
+ */
+public class NotFoundException extends RuntimeException {
+ public NotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java
new file mode 100644
index 000000000000..614c2a6d44bd
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java
@@ -0,0 +1,72 @@
+package ru.javawebinar.topjava.web;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import ru.javawebinar.topjava.model.UserMeal;
+import ru.javawebinar.topjava.repository.InMemoryUserMealRepositoryImpl;
+import ru.javawebinar.topjava.repository.UserMealRepository;
+import ru.javawebinar.topjava.util.UserMealsUtil;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * User: gkislin
+ * Date: 19.08.2014
+ */
+public class MealServlet extends HttpServlet {
+ private static final Logger LOG = LoggerFactory.getLogger(MealServlet.class);
+
+ private UserMealRepository repository;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ repository = new InMemoryUserMealRepositoryImpl();
+ }
+
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException {
+ request.setCharacterEncoding("UTF-8");
+ String id = request.getParameter("id");
+ UserMeal userMeal = new UserMeal(id.isEmpty() ? null : Integer.valueOf(id),
+ LocalDateTime.parse(request.getParameter("dateTime")),
+ request.getParameter("description"),
+ Integer.valueOf(request.getParameter("calories")));
+ LOG.info(userMeal.isNew() ? "Create {}" : "Update {}", userMeal);
+ repository.save(userMeal);
+ response.sendRedirect("meals");
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String action = request.getParameter("action");
+
+ if (action == null) {
+ LOG.info("getAll");
+ request.setAttribute("mealList",
+ UserMealsUtil.getWithExceeded(repository.getAll(), UserMealsUtil.DEFAULT_CALORIES_PER_DAY));
+ request.getRequestDispatcher("/mealList.jsp").forward(request, response);
+ } else if (action.equals("delete")) {
+ int id = getId(request);
+ LOG.info("Delete {}", id);
+ repository.delete(id);
+ response.sendRedirect("meals");
+ } else {
+ final UserMeal meal = action.equals("create") ?
+ new UserMeal(LocalDateTime.now(), "", 1000) :
+ repository.get(getId(request));
+ request.setAttribute("meal", meal);
+ request.getRequestDispatcher("mealEdit.jsp").forward(request, response);
+ }
+ }
+
+ private int getId(HttpServletRequest request) {
+ String paramId = Objects.requireNonNull(request.getParameter("id"));
+ return Integer.valueOf(paramId);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java
new file mode 100644
index 000000000000..559eb0cd4b91
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java
@@ -0,0 +1,26 @@
+package ru.javawebinar.topjava.web;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 userList");
+
+ request.getRequestDispatcher("/userList.jsp").forward(request, response);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java
new file mode 100644
index 000000000000..b3c1008f1d00
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/meal/UserMealRestController.java
@@ -0,0 +1,12 @@
+package ru.javawebinar.topjava.web.meal;
+
+import ru.javawebinar.topjava.service.UserMealService;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class UserMealRestController {
+ private UserMealService service;
+
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java
new file mode 100644
index 000000000000..aac911f5864d
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java
@@ -0,0 +1,49 @@
+package ru.javawebinar.topjava.web.user;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import ru.javawebinar.topjava.model.User;
+import ru.javawebinar.topjava.service.UserService;
+
+import java.util.List;
+
+/**
+ * User: gkislin
+ */
+public abstract class AbstractUserController {
+ protected final Logger LOG = LoggerFactory.getLogger(getClass());
+
+ private UserService service;
+
+ public List getAll() {
+ LOG.info("getAll");
+ return service.getAll();
+ }
+
+ public User get(int id) {
+ LOG.info("get " + id);
+ return service.get(id);
+ }
+
+ public User create(User user) {
+ user.setId(null);
+ LOG.info("create " + user);
+ return service.save(user);
+ }
+
+ public void delete(int id) {
+ LOG.info("delete " + id);
+ service.delete(id);
+ }
+
+ public void update(User user, int id) {
+ user.setId(id);
+ LOG.info("update " + user);
+ service.update(user);
+ }
+
+ public User getByMail(String email) {
+ LOG.info("getByEmail " + email);
+ return service.getByEmail(email);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java
new file mode 100644
index 000000000000..cdd1e6bf11f8
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java
@@ -0,0 +1,36 @@
+package ru.javawebinar.topjava.web.user;
+
+import ru.javawebinar.topjava.model.User;
+
+import java.util.List;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class AdminRestController extends AbstractUserController {
+
+ public List getAll() {
+ return super.getAll();
+ }
+
+ public User get(int id) {
+ return super.get(id);
+ }
+
+ public User create(User user) {
+ return super.create(user);
+ }
+
+ public void delete(int id) {
+ super.delete(id);
+ }
+
+ public void update(User user, int id) {
+ super.update(user, id);
+ }
+
+ public User getByMail(String email) {
+ return super.getByMail(email);
+ }
+}
diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java
new file mode 100644
index 000000000000..6f0c970152af
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java
@@ -0,0 +1,23 @@
+package ru.javawebinar.topjava.web.user;
+
+import ru.javawebinar.topjava.LoggedUser;
+import ru.javawebinar.topjava.model.User;
+
+/**
+ * GKislin
+ * 06.03.2015.
+ */
+public class ProfileRestController extends AbstractUserController {
+
+ public User get() {
+ return super.get(LoggedUser.id());
+ }
+
+ public void delete() {
+ super.delete(LoggedUser.id());
+ }
+
+ public void update(User user) {
+ super.update(user, LoggedUser.id());
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 000000000000..8ee5f21b023e
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ true
+
+
+
+
+
+
+ ${TOPJAVA_ROOT}/log/topjava.log
+
+
+ UTF-8
+ %date %-5level %logger{0} [%file:%line] %msg%n
+
+
+
+
+
+ UTF-8
+ %-5level %logger{0} [%file:%line] %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000000..6306688d0998
--- /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
+
+
+
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
new file mode 100644
index 000000000000..e85ec56ddf0d
--- /dev/null
+++ b/src/main/webapp/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Top Java.
+
+
+
+
+
+
+
diff --git a/src/main/webapp/mealEdit.jsp b/src/main/webapp/mealEdit.jsp
new file mode 100644
index 000000000000..443933549130
--- /dev/null
+++ b/src/main/webapp/mealEdit.jsp
@@ -0,0 +1,51 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+ Meal
+
+
+
+
+
+
diff --git a/src/main/webapp/mealList.jsp b/src/main/webapp/mealList.jsp
new file mode 100644
index 000000000000..5d6a5dbfc9ef
--- /dev/null
+++ b/src/main/webapp/mealList.jsp
@@ -0,0 +1,50 @@
+<%@ page import="ru.javawebinar.topjava.util.TimeUtil" %>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+
+
+ Meal list
+
+
+
+
+
+ Meal list
+ Add Meal
+
+
+
+
+ | Date |
+ Description |
+ Calories |
+ |
+ |
+
+
+
+
+
+ |
+ ${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}
+ <%--<%=TimeUtil.toString(meal.getDateTime())%>--%>
+ |
+ ${meal.description} |
+ ${meal.calories} |
+ Update |
+ Delete |
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/userList.jsp b/src/main/webapp/userList.jsp
new file mode 100644
index 000000000000..eae4726d4400
--- /dev/null
+++ b/src/main/webapp/userList.jsp
@@ -0,0 +1,10 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+ User list
+
+
+
+User list
+
+