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 + +Вводное занятие +=============== +## ![video](http://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico) Системы управления версиями. Git. + +- Система управления версиями. VCS/DVSC. +- Ресурсы: + - Интерактивная Git обучалка + - Основы Git + - Working with remote repositories + - Видео по обучению Git + - Как начать работать с GitHub: быстрый старт + - Справочник в графическом виде + - Git Overview + - Видеокурс по Git + +## ![video](http://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico) Работа с проектом (выполнять инструкции) +- **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 @@ +## Составление резюме, подготовка к интервью, поиск работы + +![cv](https://cloud.githubusercontent.com/assets/13649199/10877471/93ea86b8-8157-11e5-9bfa-95e3fba75c58.jpg) + +- Научиться программировать сложнее, чем кажется + +### Составление резюме: +- Идеальное резюме 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. + + +

Проект "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 + + + +
+

Home

+

Edit meal

+
+ +
+ +
+
DateTime:
+
+
+
+
Description:
+
+
+
+
Calories:
+
+
+ + +
+
+ + 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 + + + +
+

Home

+

Meal list

+ Add Meal +
+ + + + + + + + + + + + + + + + + + + + +
DateDescriptionCalories
+ ${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()} + <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> + ${meal.description}${meal.calories}UpdateDelete
+
+ + \ 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 + + +

Home

+

User list

+ +