diff --git a/.gitignore b/.gitignore index 6ca05c4a5184..b4860155a096 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ out target *.iml log - - - +*.patch \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000000..e2fa6d19905b --- /dev/null +++ b/README.md @@ -0,0 +1,193 @@ +Java Enterprise Online Project +=============================== +Разработка полнофункционального 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 и хранением в базах данных Postgresql и HSQLDB. + +![topjava_structure](https://user-images.githubusercontent.com/13649199/27433714-8294e6fe-575e-11e7-9c41-7f6e16c5ebe5.jpg) + + Когда вы слышите что-то, вы забываете это. + Когда вы видите что-то, вы запоминаете это. + Но только когда вы начинаете делать это, + вы начинаете понимать это + + Старинная китайская поговорка + +## Описание и план проекта +### Демо разрабатываемого приложения +### [Изменения проекта (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 Report 2016 +- [Java in 2017 Survey](http://www.baeldung.com/java-in-2017) +- Из юниоров в разработчики: получаем первую работу + +#### Spring Pet-Clinic +- Spring PetClinic Sample Application +- 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 + - [Основы Git за 20 минут](https://www.youtube.com/watch?v=TMeZGvtQnT8) + - [Git - для новичков](https://www.youtube.com/watch?list=PLY4rE9dstrJyTdVJpv7FibSaXB4BHPInb&v=PEKN8NtBDQ0) + +## ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) Работа с проектом (выполнять инструкции) +> Проект постоянно улучшается, поэтому видео иногда отличается от кода проекта. Изменения указываю после видео: в `UserMeals/UserMealWithExceed` поля изменились на `private` +- **Prepare_ to_ HW0.patch (скачать и положить в каталог вашего проекта)** + +## Инструкция по шагам (из видео): +- Установить ПО (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. +``` +- Видео: Доступно о Java 8 Lambda +- 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) + +#### Optional 2 (+5 бонусов) +``` +Сделать реализацию со сложностью O(N): +- циклом за 1 проход по List. Обратите внимание на п.13 замечаний +- через Stream API за 1 проход по Stream +``` +#### Замечания по использованию Stream API: +- Когда встречаешь что-то непривычное, приходится перестраивать мозги. Например переход с процедурного на ООП программирование дается непросто. Те, кто не знает шаблонов (и не хоят учить) также их встречают плохо. Хорошая новость в том, что если это принять и начать использовать, то начинаешь получать от этого удовольствие. И тут главное не впасть в другую крайность: + - [Используйте Stream API проще (или не используйте вообще)](https://habrahabr.ru/post/337350/) +- Если вас беспокоить производительность стримов, обязательно прочитайте про оптимизацию + - ["Что? Где? Когда?"](http://optimization.guide/intro.html) + - [Перформанс: что в имени тебе моём?](https://habrahabr.ru/company/jugru/blog/338732/) + - [Performance это праздник](https://habrahabr.ru/post/326242/) + +При использовании Stream API производительность улучшиться только на больших задачах, где возможно распараллеливание. +Еще - просто так запустить и померять скорость JVM нельзя (как минимум дать прогреться и запустить очень большое число раз). Лучше использовать какие-нибудь бенчмарки, например [JMH](http://tutorials.jenkov.com/java-performance/jmh.html), который мы юзаем на другом проекте (Mastejava). + +## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Замечания к HW0 +- 1: Код проекта менять можно! Одна из распространенных ошибок как в тестовых заданиях на собеседовании, так и при работе на проекте, что ничего нельзя менять. Конечно при правках в рабочем проекте обязятельно нужно проконсультироваться/проревьюится у авторов кода (находится по истории VCS) +- 2: Наследовать `UserMealWithExceed` от `UserMeal` я не буду, т.к. это разные сущности: Transfer Object и Entity. Мы будет их проходить на 2м уроке. +- 3: Правильная реализация должна быть простой и красивой, можно сделать 2-мя способами: через стримы и через циклы. Сложность должна быть O(N), т.е. без вложенных стримов и циклов. +- 4: При реализации через циклы посмотрите в `Map` на методы `getOrDefault` или `merge` +- 5: **При реализации через `Stream` заменяйте `forEach` оператором `stream.map(..)`** +- 6: Объявляйте переменные непосредственно перед использованием (если возможно - сразу с инициализацией). При объявлении коллекций используйте тип переменной - интерфейс (Map, List, ..) +- 7: Если IDEA предлагает оптимизацию (желтым подчеркивает), нарпимер заменить лямбду на метод-референс, соглашайтесь (Alt+Enter) +- 8: Пользуйтесь форматированием кода в IDEA: `Alt+Ctrl+L` +- 9: Перед check-in проверяйте чендж-лист (курсор на файл и Ctrl+D): не оставляйте в коде ничего лишнего (закомментированный код, TODO и пр.). Если файл не меняется (например только пробелы или переводы строк), не надо его чекинить, делайте ему `revert` (Git -> Revert / `Ctrl+Alt+Z`). +- 10: `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование. +- 11: Результаты, возвращаемые `UserMealsUtil.getFilteredWithExceeded` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом. +- 12: Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные (лучше на english) +- 13: Не полагайтесь в решениии на то, что список будет подаваться отсортированным. Такого условия нет. +----- + +### Полезные ресурсы +> ВНИМАНИЕ: +> - ДЗ первого урока будет связано с созданием небольшого CRUD приложения (в памяти, без DB) на JSP и сервлетах. Введение будет, но предварительное знакомство не помешает. +> - основы JavaSсript необходимы для понимания проекта, начиная с 8-го занятия! + +Все остальное - опционально. + +#### HTML, JavaScript, CSS +- [Справочник по WEB](https://developer.mozilla.org/ru/) +- [Видео по WEB технологиям](https://www.youtube.com/user/WebMagistersRu/playlists) +- [Изучение JavaScript в одном видео уроке за час](https://www.youtube.com/watch?v=QBWWplFkdzw) +- 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 + +### Туториалы, разное +[Что нужно знать о бэкенде новичку в веб-разработке](https://tproger.ru/translations/backend-web-development) +[Туториалы: Spring Framework, Hibernate, Java Core, JDBC](http://proselyte.net/tutorials/) + +#### Сервлеты +- Как создать Servlet? Полное руководство. + +#### 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..9f794333656b --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,142 @@ +# TopJava Release Notes +### Topjava 12 +- добавилось [видео решений HW0 с одним проходом](https://drive.google.com/open?id=0B9Ye2auQ_NsFcUVfU1hmYUx3Rzg) +- поправил видео [Обзор Spring Framework. Spring Context](https://drive.google.com/file/d/1fBSLGEbc7YXBbmr_EwEHltCWNW_pUmIH). Дописал про Constructor injection. +- заменил видео про тетсирование сервисов. Вместо самодельных матчеров стали использовать [AssertJ](http://joel-costigliola.github.io/assertj/index.html). Видео [Тестирование UserService через AssertJ](https://drive.google.com/open?id=1SPMkWMYPvpk9i0TA7ioa-9Sn1EGBtClD), время 1:53 + +### Topjava 11 +- добавил + - доп. решение HW1 через одним return и O(N) + - раскрасил лог ([Logback layouts coloring](https://logback.qos.ch/manual/layouts.html#coloring)) +- рефакторинг + - починил коммит формы по cancel (`history.back()`) в FireFox + - заменил неработающий DependencyCi на [VersionEye](https://www.versioneye.com/) c проверкой зависимостей на uptodate + - починил `CrudUserRepository.getWithMeals()` через `@EntityGraph`. С неколькими ролями (у админа) еда дублируется + - починил тесты контроллеров с профилем JDBC (`JpaUtil` отсутствует в контексте JDBC) + - переименовал `meal.jsp/user.jsp` в `mealForm.jsp/userForm.jsp` + - в `InMemoryMealRepositoryImpl.save()` сделал update атомарным + - переименовал методы сервисов `save` в `create` + - переименовал и cделал классы `BaseEntity` и `NamedEntity` абстрактными + - обновил Noty и API с ним до 3.1.0. Добавил glyphicon в сообщения Noty + - заменил `MATCHER_WITH_EXCEED` на валидацию через [JSONassert](https://github.com/skyscreamer/JSONassert). + - поменял Deprecated валидаторы `org.hibernate.validator.constraints` на `javax.validation.constraints` + - убрал пароль из результатов REST через [@JsonProperty READ_ONLY / WRITE_ONLY](https://stackoverflow.com/questions/12505141/only-using-jsonignore-during-serialization-but-not-deserialization/12505165#12505165). Тесты на REST пришлось починить добавлением добавлением в JSON пароля как дополнительного параметра (`JsonUtil.writeWithExtraProps`) + - **убрал JSON View и сделал преобразование времени на UI с помощью [jQuery converters](http://api.jquery.com/jQuery.ajax/#using-converters)** + - **поменял [группу валидации по умолчанию при сохранении через JPA](https://stackoverflow.com/questions/16930623/16930663#16930663).** Теперь + все валидаторы в модели работаю по умолчанию (`groups` не требуется). + - Добавил в `ErrorInfo` тип ошибки `ErrorType` + i18n. + +- правки + - переименовал `ModelMatcher` в `BeanMatcher` и починил: можно сравнивать только упорядоченные коллекции (List) + - поменял зависимость `org.hibernate:hibernate-validator` на `org.hibernate.validator:hibernate-validator` (warning при сборке) + +### Topjava 10 +- добавил + - доступ к AuthorizedUser через [`@AuthenticationPrincipal`](http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#mvc-authentication-principal) и [authentication Tag](http://docs.spring.io/spring-security/site/docs/current/reference/html/taglibs.html#the-authentication-tag) + - [Обработку 404 NotFound](https://stackoverflow.com/questions/18322279/spring-mvc-spring-security-and-error-handling) + - локализацию ошибок валидации + - проверки json в тестах через [JSONassert](https://github.com/skyscreamer/JSONassert) и [через jsonPath](https://www.petrikainulainen.net/programming/spring-framework/integration-testing-of-spring-mvc-applications-write-clean-assertions-with-jsonpath/) + - [логирование от Postgres Driver](http://stackoverflow.com/a/43242620/548473) + - в `.travis.yml` [сборку только ветки master](https://docs.travis-ci.com/user/customizing-the-build#Building-Specific-Branches) + - [защиту от кэширование ajax запросов в IE](https://stackoverflow.com/a/4303862/548473) + - обработку запрета модификации системный юзеров через универсальный `ApplicationException` + - рефакторинг + - сделал `@EntityGraph` через `attributePaths` + - реализаовал обработку дублирования `user.email` и `meal.dateTime` через [Controller Based Exception Handling](https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#controller-based-exception-handling) + - поменял отключение транзакционности в тестах через `@Transactional(propagation = Propagation.NEVER)` + - сделал выбор в сервлете через switch + - [все логгирование сделал через {} форматирование](http://stackoverflow.com/questions/10555409/logger-slf4j-advantages-of-formatting-with-instead-of-string-concatenation) и поправил его в контроллерах (поле проверки id) + - [перешел на конструктор DI](http://stackoverflow.com/questions/39890849/what-exactly-is-field-injection-and-how-to-avoid-it) + - в `ModelMatcher` переименовал `Comparator` -> `Equality` + - [заинлайнил все лямбды](http://stackoverflow.com/questions/19718353/is-repeatedly-instantiating-an-anonymous-class-wasteful) (компараторы, ModelMatcher.equality) + - поменялась реализация `JdbcUserRepositoryImpl.getAll()` + - на UI кнопки в таблице заменились на линки, поправил сообщения локализации + - [сделал кастомизацию JSON (@JsonView) и валидацию (groups)](https://drive.google.com/file/d/0B9Ye2auQ_NsFRTFsTjVHR2dXczA) для данных еды, отдаваемых на UI + - в `JdbcUserRepositoryImpl` поменял `MapSqlParameterSource` на `BeanPropertySqlParameterSource` +- удалил + - зависимость `javax.transaction.jta` (уже не нужна) + - `${spring.version}` в `pom.xml` зависимостях (уже есть в `spring-framework-bom`) + - distinct из запроса Hibernate на пользователей с ролями. [Оптимизация запроса distinct: 15.16.2](https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#hql-distinct) + - лишние `
` тэги (`shadow` и `view-box`) + +### 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 + - сделал универсальную обработку исключений дублирования email и dateTime + +### 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..4b51e6d7fd8b --- /dev/null +++ b/cv.md @@ -0,0 +1,104 @@ +## Составление резюме, подготовка к интервью, поиск работы + +![cv](https://cloud.githubusercontent.com/assets/13649199/10877471/93ea86b8-8157-11e5-9bfa-95e3fba75c58.jpg) + +- Научиться программировать сложнее, чем кажется +- [Собеседование. Разработка ПО. Вопросы.](https://drive.google.com/open?id=0B9Ye2auQ_NsFQVc2WUdCR0xvLWM) +- [Набор ссылок для тренировки и прохождения интервью](https://github.com/andreis/interview) + +### Составление резюме: +- [VisualCV: create resume in minutes](https://www.visualcv.com/) +- Выбрать шаблон для резюме +- Как продать свое резюме в 2 раза дороже +- Как правильно составить резюме +- Резюме программистов. Часть 1 (плохие) +- Резюме программистов. Часть 2 (хорошие) +- Как составить резюме на английском +- ОФОРМЛЕНИЕ IT-РЕЗЮМЕ для USA + +### Наши истории (делимся опытом и успехом) + +### Тесты/задачи онлайн: +- [Java Programming Test](https://tests4geeks.com/java) +- game: test Java skills +- Codility lesson tests +- Quizful- тесты онлайн +- LeetCode Online Judge +- Sphere online judge +- Codility programmers lessons +- Hackerrank practice coding + +## [Тестовое собеседование, самые спрашиваемые темы](http://javaops.ru/interview/test.html) + +### Интервью: +- Михаил Портнов. Собеседование на работу: как продать себя грамотно +- Михаил Портнов. Какие вопросы мы задаем на собеседовании? +- Михаил Портнов. Собеседование на работу: жизненный путь +- Канал: Резюме, поиск работы, интервью +- Яков Файн: Как стать профессиональным 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: +- Обязательно убери из резюме **любое упоминание Junior**. Количество обращений возрастет на порядок. Ссылку на стажировку можно поставить: http://javaops.ru (в linkedin: https://www.linkedin.com/company/java-online-projects). +- После завершения проекта ты освошь все заявленные в нем технологии - вставь их в квалификацию (включая 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. + - собственная доработка проекта + +- Делай упор не на обучение, а на **участие в проекте**. Выполнение домашних заданий это полноценное участие с написанием функционала по всем пройденным технологиям. На собеседовании смотрят не на то, что ты заканчивал, а на опыт и знания. + +### В процессе обучения +- Если рассмотриваешь предложения по работе, подними в своем профиле этот флаг и обязательно заполни ссылку на резюме. Обновления нашей базы выпускников смотрят уже более 125 партнеров по трудоустройству (компании и индивидуальные рекрутеры). Проверь содержание "Информация для HR": по нему принимают решение, открывать резюме или нет. + +- Вступайте в нашу группу участников Slack: каналы помощи с Java, отзывы о работодателях, обсуждение тестовых заданий, вакансии, цены на рынке труда, IT события, интересные видео и многое другое. + +- Подпишитесь на рассылку вакансий под себя + +### После прохождения испытательного срока жду твою [историю успеха](http://javaops.ru/view/story) + +### Основные сайты поиска работы: +- Яндекс агрегатор +- HH +- LinkedIn +- djinni.co (более актуально для Украины) + +## Как выжить на испытательном сроке +- Учись грамотно формулировать проблему. Проблема "у меня не работает" может иметь тысячи причин. В + процессе формулирования очень часто приходит ее решение. +- Учись инвестигировать проблему. Внимательное чтение логов и умение дебажить - основные навыки + разработчика. В логах надо читать верх самого нижнего эксепшена - там причина всей портянки. +- Грамотно уделяй время каждой проблеме. Две крайности - сразу бросаться за помощью и + бится нам ней часами. + Пробуй решить ее сам и в зависимости от проблемы выделяй на это разумное время. +- Если тебе что-то объясняют по проекту - обязательно записывай. +- Когда получаешь задачу - уточни все очень подробно. +- Получай в процессе решения обратную связь - в том ли направлении ты идешь. +- Не игнорируй совместные ланчи (курилки) +- Готовься к стендапам/летучкам. Задавай на них вменяемые вопросы. Выказывай заинтересованность +- Выдели самое главное путем опроса босса и важных коллег. Не распыляйся на мелочи. +- [**Советы новичкам**](http://blog.csssr.ru/2016/09/19/how-to-be-a-beginner-developer) + +## [Отзывы по стажировке Topjava](https://vk.com/topic-74381644_30447246) diff --git a/description.md b/description.md new file mode 100644 index 000000000000..d01afc2e22a7 --- /dev/null +++ b/description.md @@ -0,0 +1,76 @@ +#### Разработка полнофункционального 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 и сохранением в базах данных Postgresql и HSQLDB. + +- Основное внимание будет уделяться способам решения многочисленных проблем разработки в Spring/JPA, а также структурному (красивому и надежному) java кодированию и архитектуре приложения. +- Каждая итерация проекта закрепляется домашним заданием по реализации схожей функциональности. Следующее занятие начинается с разбора домашних заданий. +- Большое внимание уделяется тестированию кода: в проекте более 100 JUnit тестов. +- Несмотря на относительно небольшой размер, приложение разрабатывается с нуля как большой проект (например мы используем кэш 2-го уровня Hibernate, настраиваем Jackson для работы с ленивой загрузкой +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. jQuery datatables. +- AJAX. jQuery. Notifications. +- Spring Security +- Spring Binding/Validation +- Работа с datatables через Ajax. +- Spring Security Test +- [Кастомизация JSON (@JsonView) и валидации (groups)](https://drive.google.com/open?id=0B9Ye2auQ_NsFRTFsTjVHR2dXczA) +- Encoding password +- CSRF (добавление в проект защиты от межсайтовой подделки запроса) +- form-login. Spring Security Taglib +- Handler interceptor +- Spring Exception Handling +- Смена локали +- Фильтрация JSON через @JsonView +- Защита от XSS (Cross Site Scripting) +- Деплой в Heroku +- Локализация datatables, ошибок валидации +- Обработка ошибок 404 (NotFound) +- Доступ к AuthorizedUser +- Собеседование. Разработка ПО diff --git a/pom.xml b/pom.xml index c8a1c78f3b29..e3b8143e3e63 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.7.0 ${java.version} ${java.version} diff --git a/src/main/Patches/hv0/Prepare_to_HW0.patch b/src/main/Patches/hv0/Prepare_to_HW0.patch new file mode 100644 index 000000000000..de0e3a4091f7 --- /dev/null +++ b/src/main/Patches/hv0/Prepare_to_HW0.patch @@ -0,0 +1,137 @@ +Index: src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java (revision ) +@@ -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; ++ } ++} +Index: src/main/java/ru/javawebinar/topjava/util/TimeUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/TimeUtil.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/util/TimeUtil.java (revision ) +@@ -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; ++ } ++} +Index: src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java (revision ) +@@ -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; ++ } ++} +Index: src/main/java/ru/javawebinar/topjava/model/UserMeal.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/UserMeal.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/model/UserMeal.java (revision ) +@@ -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/Patches/hv1/1_0_fix.patch b/src/main/Patches/hv1/1_0_fix.patch new file mode 100644 index 000000000000..d4b8257bc45e --- /dev/null +++ b/src/main/Patches/hv1/1_0_fix.patch @@ -0,0 +1,183 @@ +Index: src/main/java/ru/javawebinar/topjava/Main.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/Main.java (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ src/main/java/ru/javawebinar/topjava/Main.java (revision ) +@@ -1,11 +1,8 @@ + package ru.javawebinar.topjava; + + /** +- * User: gkislin +- * Date: 05.08.2015 +- * +- * @link http://caloriesmng.herokuapp.com/ +- * @link https://github.com/JavaOPs/topjava ++ * @see Demo ++ * @see Initial project + */ + public class Main { + public static void main(String[] args) { +Index: src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/UserMealWithExceed.java (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java (revision ) +@@ -2,11 +2,7 @@ + + import java.time.LocalDateTime; + +-/** +- * GKislin +- * 11.01.2015. +- */ +-public class UserMealWithExceed { ++public class MealWithExceed { + private final LocalDateTime dateTime; + + private final String description; +@@ -15,7 +11,7 @@ + + private final boolean exceed; + +- public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { ++ public MealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; +Index: src/main/java/ru/javawebinar/topjava/model/UserMeal.java +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/UserMeal.java (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ src/main/java/ru/javawebinar/topjava/model/Meal.java (revision ) +@@ -2,18 +2,14 @@ + + import java.time.LocalDateTime; + +-/** +- * GKislin +- * 11.01.2015. +- */ +-public class UserMeal { ++public class Meal { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + +- public UserMeal(LocalDateTime dateTime, String description, int calories) { ++ public Meal(LocalDateTime dateTime, String description, int calories) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; +Index: .gitignore +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .gitignore (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ .gitignore (revision ) +@@ -3,6 +3,4 @@ + target + *.iml + log +- +- +- ++*.patch +\ No newline at end of file +Index: src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ src/main/java/ru/javawebinar/topjava/util/UserMealsUtil.java (revision d1f6a45bc89fb192025b1627657554edd55fa205) +@@ -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; +- } +-} +Index: pom.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- pom.xml (revision d1f6a45bc89fb192025b1627657554edd55fa205) ++++ pom.xml (revision ) +@@ -24,7 +24,7 @@ + + org.apache.maven.plugins + maven-compiler-plugin +- 3.1 ++ 3.7.0 + + ${java.version} + ${java.version} +Index: src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (revision ) +@@ -0,0 +1,31 @@ ++package ru.javawebinar.topjava.util; ++ ++import ru.javawebinar.topjava.model.Meal; ++import ru.javawebinar.topjava.model.MealWithExceed; ++ ++import java.time.LocalDateTime; ++import java.time.LocalTime; ++import java.time.Month; ++import java.util.Arrays; ++import java.util.List; ++ ++public class MealsUtil { ++ public static void main(String[] args) { ++ List mealList = 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) ++ ); ++ 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/Patches/hv1/1_1_HW0_stream.patch b/src/main/Patches/hv1/1_1_HW0_stream.patch new file mode 100644 index 000000000000..2f9f4effa454 --- /dev/null +++ b/src/main/Patches/hv1/1_1_HW0_stream.patch @@ -0,0 +1,117 @@ +Index: src/main/java/ru/javawebinar/topjava/model/Meal.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/Meal.java (date 1499897563000) ++++ src/main/java/ru/javawebinar/topjava/model/Meal.java (revision ) +@@ -1,6 +1,8 @@ + package ru.javawebinar.topjava.model; + ++import java.time.LocalDate; + import java.time.LocalDateTime; ++import java.time.LocalTime; + + public class Meal { + private final LocalDateTime dateTime; +@@ -26,4 +28,12 @@ + public int getCalories() { + return calories; + } ++ ++ public LocalDate getDate() { ++ return dateTime.toLocalDate(); ++ } ++ ++ public LocalTime getTime() { ++ return dateTime.toLocalTime(); ++ } + } +Index: src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (date 1499897563000) ++++ src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (revision ) +@@ -3,29 +3,41 @@ + import ru.javawebinar.topjava.model.Meal; + import ru.javawebinar.topjava.model.MealWithExceed; + ++import java.time.LocalDate; + import java.time.LocalDateTime; + import java.time.LocalTime; + import java.time.Month; + import java.util.Arrays; + import java.util.List; ++import java.util.Map; ++import java.util.stream.Collectors; + + public class MealsUtil { + public static void main(String[] args) { +- List mealList = Arrays.asList( +- new Meal(LocalDateTime.of(2015, Month.MAY, 30,10,0), "Завтрак", 500), +- new Meal(LocalDateTime.of(2015, Month.MAY, 30,13,0), "Обед", 1000), +- new Meal(LocalDateTime.of(2015, Month.MAY, 30,20,0), "Ужин", 500), +- new Meal(LocalDateTime.of(2015, Month.MAY, 31,10,0), "Завтрак", 1000), +- new Meal(LocalDateTime.of(2015, Month.MAY, 31,13,0), "Обед", 500), +- new Meal(LocalDateTime.of(2015, Month.MAY, 31,20,0), "Ужин", 510) ++ List 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) + ); +- getFilteredWithExceeded(mealList, LocalTime.of(7, 0), LocalTime.of(12,0), 2000); +-// .toLocalDate(); +-// .toLocalTime(); ++ List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); ++ mealsWithExceeded.forEach(System.out::println); + } + +- public static List getFilteredWithExceeded(List mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { +- // TODO return filtered list with correctly exceeded field +- return null; ++ public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { ++ Map caloriesSumByDate = meals.stream() ++ .collect( ++ Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) ++// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum) ++ ); ++ ++ return meals.stream() ++ .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) ++ .map(meal -> ++ new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), ++ caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) ++ .collect(Collectors.toList()); + } +-} ++} +\ No newline at end of file +Index: src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java (date 1499897563000) ++++ src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java (revision ) +@@ -17,4 +17,14 @@ + this.calories = calories; + this.exceed = exceed; + } +-} ++ ++ @Override ++ public String toString() { ++ return "UserMealWithExceed{" + ++ "dateTime=" + dateTime + ++ ", description='" + description + '\'' + ++ ", calories=" + calories + ++ ", exceed=" + exceed + ++ '}'; ++ } ++} +\ No newline at end of file diff --git a/src/main/Patches/hv1/1_2_HW0_cycle.patch b/src/main/Patches/hv1/1_2_HW0_cycle.patch new file mode 100644 index 000000000000..4ecbb830ff9e --- /dev/null +++ b/src/main/Patches/hv1/1_2_HW0_cycle.patch @@ -0,0 +1,57 @@ +Index: src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (date 1499897832000) ++++ src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (revision ) +@@ -7,9 +7,7 @@ + import java.time.LocalDateTime; + import java.time.LocalTime; + import java.time.Month; +-import java.util.Arrays; +-import java.util.List; +-import java.util.Map; ++import java.util.*; + import java.util.stream.Collectors; + + public class MealsUtil { +@@ -24,6 +22,8 @@ + ); + List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + mealsWithExceeded.forEach(System.out::println); ++ ++ System.out.println(getFilteredWithExceededByCycle(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + } + + public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { +@@ -35,9 +35,25 @@ + + return meals.stream() + .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) +- .map(meal -> +- new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), +- caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) ++ .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .collect(Collectors.toList()); + } ++ ++ public static List getFilteredWithExceededByCycle(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { ++ ++ final Map caloriesSumByDate = new HashMap<>(); ++ meals.forEach(meal -> caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum)); ++ ++ final List mealsWithExceeded = new ArrayList<>(); ++ meals.forEach(meal -> { ++ if (TimeUtil.isBetween(meal.getTime(), startTime, endTime)) { ++ mealsWithExceeded.add(createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)); ++ } ++ }); ++ return mealsWithExceeded; ++ } ++ ++ public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { ++ return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); ++ } + } +\ No newline at end of file diff --git a/src/main/Patches/hv1/1_3_HW0_optional2.patch b/src/main/Patches/hv1/1_3_HW0_optional2.patch new file mode 100644 index 000000000000..26dec67cb4e9 --- /dev/null +++ b/src/main/Patches/hv1/1_3_HW0_optional2.patch @@ -0,0 +1,90 @@ +Index: src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (date 1509555460000) ++++ src/main/java/ru/javawebinar/topjava/util/MealsUtil.java (revision ) +@@ -8,7 +8,12 @@ + import java.time.LocalTime; + import java.time.Month; + import java.util.*; ++import java.util.stream.Collector; + import java.util.stream.Collectors; ++import java.util.stream.Stream; ++ ++import static java.util.function.Function.identity; ++import static java.util.stream.Collectors.toList; + + public class MealsUtil { + public static void main(String[] args) { +@@ -24,6 +29,8 @@ + mealsWithExceeded.forEach(System.out::println); + + System.out.println(getFilteredWithExceededByCycle(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); ++ System.out.println(getFilteredWithExceededInOnePass(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); ++ System.out.println(getFilteredWithExceededInOnePass2(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + } + + public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { +@@ -36,7 +43,7 @@ + return meals.stream() + .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) + .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) +- .collect(Collectors.toList()); ++ .collect(toList()); + } + + public static List getFilteredWithExceededByCycle(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { +@@ -53,6 +60,51 @@ + return mealsWithExceeded; + } + ++ public static List getFilteredWithExceededInOnePass(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { ++ Collection> list = meals.stream() ++ .collect(Collectors.groupingBy(Meal::getDate)).values(); ++ ++ return list.stream().flatMap(dayMeals -> { ++ boolean exceed = dayMeals.stream().mapToInt(Meal::getCalories).sum() > caloriesPerDay; ++ return dayMeals.stream().filter(meal -> ++ TimeUtil.isBetween(meal.getTime(), startTime, endTime)) ++ .map(meal -> createWithExceed(meal, exceed)); ++ }).collect(toList()); ++ } ++ ++ public static List getFilteredWithExceededInOnePass2(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { ++ final class Aggregate { ++ private final List dailyMeals = new ArrayList<>(); ++ private int dailySumOfCalories; ++ ++ private void accumulate(Meal meal) { ++ dailySumOfCalories += meal.getCalories(); ++ if (TimeUtil.isBetween(meal.getDateTime().toLocalTime(), startTime, endTime)) { ++ dailyMeals.add(meal); ++ } ++ } ++ ++ // never invoked if the upstream is sequential ++ private Aggregate combine(Aggregate that) { ++ this.dailySumOfCalories += that.dailySumOfCalories; ++ this.dailyMeals.addAll(that.dailyMeals); ++ return this; ++ } ++ ++ private Stream finisher() { ++ final boolean exceed = dailySumOfCalories > caloriesPerDay; ++ return dailyMeals.stream().map(meal -> createWithExceed(meal, exceed)); ++ } ++ } ++ ++ Collection> values = meals.stream() ++ .collect(Collectors.groupingBy(Meal::getDate, ++ Collector.of(Aggregate::new, Aggregate::accumulate, Aggregate::combine, Aggregate::finisher)) ++ ).values(); ++ ++ return values.stream().flatMap(identity()).collect(toList()); ++ } ++ + public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { + return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); + } diff --git a/src/main/Patches/hv1/1_4_switch_to_war.patch b/src/main/Patches/hv1/1_4_switch_to_war.patch new file mode 100644 index 000000000000..ed7403bcf79b --- /dev/null +++ b/src/main/Patches/hv1/1_4_switch_to_war.patch @@ -0,0 +1,83 @@ +Index: src/main/webapp/users.jsp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/webapp/users.jsp (revision ) ++++ src/main/webapp/users.jsp (revision ) +@@ -0,0 +1,10 @@ ++<%@ page contentType="text/html;charset=UTF-8" language="java" %> ++ ++ ++ Users ++ ++ ++

Home

++

Users

++ ++ +\ No newline at end of file +Index: src/main/webapp/index.html +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/webapp/index.html (revision ) ++++ src/main/webapp/index.html (revision ) +@@ -0,0 +1,13 @@ ++ ++ ++ ++ Java Enterprise (Topjava) ++ ++ ++

Проект Java Enterprise (Topjava)

++
++ ++ ++ +Index: src/main/webapp/WEB-INF/web.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/webapp/WEB-INF/web.xml (revision ) ++++ src/main/webapp/WEB-INF/web.xml (revision ) +@@ -0,0 +1,19 @@ ++ ++ ++ Topjava ++ ++ ++ userServlet ++ ru.javawebinar.topjava.web.UserServlet ++ 0 ++ ++ ++ userServlet ++ /users ++ ++ ++ +Index: pom.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- pom.xml (date 1499898097000) ++++ pom.xml (revision ) +@@ -4,7 +4,7 @@ + + ru.javawebinar + topjava +- jar ++ war + + 1.0-SNAPSHOT + diff --git a/src/main/Patches/hv1/1_5_add_servlet_api.patch b/src/main/Patches/hv1/1_5_add_servlet_api.patch new file mode 100644 index 000000000000..9bd9d2658d69 --- /dev/null +++ b/src/main/Patches/hv1/1_5_add_servlet_api.patch @@ -0,0 +1,45 @@ +Index: src/main/java/ru/javawebinar/topjava/web/UserServlet.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/web/UserServlet.java (revision ) ++++ src/main/java/ru/javawebinar/topjava/web/UserServlet.java (revision ) +@@ -0,0 +1,15 @@ ++package ru.javawebinar.topjava.web; ++ ++import javax.servlet.ServletException; ++import javax.servlet.http.HttpServlet; ++import javax.servlet.http.HttpServletRequest; ++import javax.servlet.http.HttpServletResponse; ++import java.io.IOException; ++ ++public class UserServlet extends HttpServlet { ++ ++ @Override ++ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ++ request.getRequestDispatcher("/users.jsp").forward(request, response); ++ } ++} +Index: pom.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- pom.xml (date 1499898685000) ++++ pom.xml (revision ) +@@ -34,6 +34,14 @@ + + + ++ ++ ++ ++ javax.servlet ++ javax.servlet-api ++ 3.1.0 ++ provided ++ + + + diff --git a/src/main/Patches/hv1/1_6_forward_to_redirect.patch b/src/main/Patches/hv1/1_6_forward_to_redirect.patch new file mode 100644 index 000000000000..35fc459218a3 --- /dev/null +++ b/src/main/Patches/hv1/1_6_forward_to_redirect.patch @@ -0,0 +1,32 @@ +Index: pom.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- pom.xml (date 1481146514000) ++++ pom.xml (revision ) +@@ -19,7 +19,7 @@ + + + topjava +- install ++ package + + + org.apache.maven.plugins +Index: src/main/java/ru/javawebinar/topjava/web/UserServlet.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/web/UserServlet.java (date 1481146514000) ++++ src/main/java/ru/javawebinar/topjava/web/UserServlet.java (revision ) +@@ -13,6 +13,7 @@ + public class UserServlet extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { +- request.getRequestDispatcher("/users.jsp").forward(request, response); ++// request.getRequestDispatcher("/users.jsp").forward(request, response); ++ response.sendRedirect("users.jsp"); + } + } diff --git a/src/main/Patches/hv1/1_7_logging.patch b/src/main/Patches/hv1/1_7_logging.patch new file mode 100644 index 000000000000..72fad180b032 --- /dev/null +++ b/src/main/Patches/hv1/1_7_logging.patch @@ -0,0 +1,113 @@ +Index: src/main/resources/logback.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/resources/logback.xml (revision ) ++++ src/main/resources/logback.xml (revision ) +@@ -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 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +Index: pom.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- pom.xml (date 1499899119000) ++++ pom.xml (revision ) +@@ -15,6 +15,10 @@ + 1.8 + UTF-8 + UTF-8 ++ ++ ++ 1.2.3 ++ 1.7.25 + + + +@@ -34,6 +38,27 @@ + + + ++ ++ ++ org.slf4j ++ slf4j-api ++ ${slf4j.version} ++ compile ++ ++ ++ ++ org.slf4j ++ jcl-over-slf4j ++ ${slf4j.version} ++ runtime ++ ++ ++ ++ ch.qos.logback ++ logback-classic ++ ${logback.version} ++ runtime ++ + + + +Index: src/main/java/ru/javawebinar/topjava/web/UserServlet.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- src/main/java/ru/javawebinar/topjava/web/UserServlet.java (date 1499899119000) ++++ src/main/java/ru/javawebinar/topjava/web/UserServlet.java (revision ) +@@ -1,15 +1,22 @@ + 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; ++ + 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"); + } diff --git a/src/main/java/ru/javawebinar/topjava/Main.java b/src/main/java/ru/javawebinar/topjava/Main.java index b23a2f0961fc..cb7e35af6afa 100644 --- a/src/main/java/ru/javawebinar/topjava/Main.java +++ b/src/main/java/ru/javawebinar/topjava/Main.java @@ -1,11 +1,8 @@ package ru.javawebinar.topjava; /** - * User: gkislin - * Date: 05.08.2015 - * - * @link http://caloriesmng.herokuapp.com/ - * @link https://github.com/JavaOPs/topjava + * @see Demo + * @see Initial project */ public class Main { public static void main(String[] args) { 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..943ff5cd59fa --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -0,0 +1,39 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +public class Meal { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + public Meal(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; + } + + public LocalDate getDate() { + return dateTime.toLocalDate(); + } + + public LocalTime getTime() { + return dateTime.toLocalTime(); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java new file mode 100644 index 000000000000..4751c9e4fd69 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/model/MealWithExceed.java @@ -0,0 +1,30 @@ +package ru.javawebinar.topjava.model; + +import java.time.LocalDateTime; + +public class MealWithExceed { + private final LocalDateTime dateTime; + + private final String description; + + private final int calories; + + private final boolean exceed; + + public MealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) { + this.dateTime = dateTime; + this.description = description; + this.calories = calories; + this.exceed = exceed; + } + + @Override + public String toString() { + return "UserMealWithExceed{" + + "dateTime=" + dateTime + + ", description='" + description + '\'' + + ", calories=" + calories + + ", exceed=" + exceed + + '}'; + } +} \ 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..2ef2768e90bc --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -0,0 +1,59 @@ +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 void main(String[] args) { + List meals = Arrays.asList( + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 10, 0), "Завтрак", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 13, 0), "Обед", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 30, 20, 0), "Ужин", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 10, 0), "Завтрак", 1000), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 13, 0), "Обед", 500), + new Meal(LocalDateTime.of(2015, Month.MAY, 31, 20, 0), "Ужин", 510) + ); + List mealsWithExceeded = getFilteredWithExceeded(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000); + mealsWithExceeded.forEach(System.out::println); + + System.out.println(getFilteredWithExceededByCycle(meals, LocalTime.of(7, 0), LocalTime.of(12, 0), 2000)); + } + + public static List getFilteredWithExceeded(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + Map caloriesSumByDate = meals.stream() + .collect( + Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)) +// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum) + ); + + return meals.stream() + .filter(meal -> TimeUtil.isBetween(meal.getTime(), startTime, endTime)) + .map(meal -> createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) + .collect(Collectors.toList()); + } + + public static List getFilteredWithExceededByCycle(List meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) { + + final Map caloriesSumByDate = new HashMap<>(); + meals.forEach(meal -> caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum)); + + final List mealsWithExceeded = new ArrayList<>(); + meals.forEach(meal -> { + if (TimeUtil.isBetween(meal.getTime(), startTime, endTime)) { + mealsWithExceeded.add(createWithExceed(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)); + } + }); + return mealsWithExceeded; + } + + public static MealWithExceed createWithExceed(Meal meal, boolean exceeded) { + return new MealWithExceed(meal.getDateTime(), meal.getDescription(), meal.getCalories(), exceeded); + } +} \ No newline at end of file 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; + } +}