diff --git a/templates/hw02-annotation-config/.gitignore b/templates/hw02-annotation-config/.gitignore
new file mode 100644
index 00000000..2170ddb4
--- /dev/null
+++ b/templates/hw02-annotation-config/.gitignore
@@ -0,0 +1,25 @@
+target/
+dependency-reduced-pom.xml
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
diff --git a/templates/hw02-annotation-config/pom.xml b/templates/hw02-annotation-config/pom.xml
new file mode 100644
index 00000000..c99477bc
--- /dev/null
+++ b/templates/hw02-annotation-config/pom.xml
@@ -0,0 +1,112 @@
+
+
+ 4.0.0
+
+
+ ru.otus.hw
+ hw02-annotation-config
+ 1.0
+
+
+ 17
+ 17
+ UTF-8
+ 6.0.12
+ 5.8
+ 5.10.0
+ 5.4.0
+ 3.24.2
+ 1.18.28
+ 3.2.2
+ 10.11.0
+
+ https://raw.githubusercontent.com/OtusTeam/Spring/master/checkstyle.xml
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+ com.opencsv
+ opencsv
+ ${opencsv.version}
+
+
+ commons-collections
+ commons-collections
+
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+ ${junit.version}
+
+
+
+ org.mockito
+ mockito-core
+ test
+ ${mockito.version}
+
+
+
+ org.assertj
+ assertj-core
+ test
+ ${assertj.version}
+
+
+
+ org.projectlombok
+ lombok
+ true
+ ${lombok.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${checkstyle-plugin.version}
+
+
+ com.puppycrawl.tools
+ checkstyle
+ ${checkstyle.version}
+
+
+
+ ${checkstyle.config.url}
+
+
+
+
+ check
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.3.0
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/Application.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/Application.java
new file mode 100644
index 00000000..2468c475
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/Application.java
@@ -0,0 +1,15 @@
+package ru.otus.hw;
+
+import org.springframework.context.ApplicationContext;
+import ru.otus.hw.service.TestRunnerService;
+
+public class Application {
+ public static void main(String[] args) {
+
+ //Создать контекст на основе Annotation/Java конфигурирования
+ ApplicationContext context = null;
+ var testRunnerService = context.getBean(TestRunnerService.class);
+ testRunnerService.run();
+
+ }
+}
\ No newline at end of file
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/AppConfig.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/AppConfig.java
new file mode 100644
index 00000000..97457488
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/AppConfig.java
@@ -0,0 +1,22 @@
+package ru.otus.hw.config;
+
+import org.springframework.beans.factory.annotation.Value;
+
+public class AppConfig implements TestConfig, TestFileNameProvider {
+
+ // внедрить свойство из application.properties
+ private int rightAnswersCountToPass;
+
+ // внедрить свойство из application.properties
+ private String testFileName;
+
+ @Override
+ public int getRightAnswersCountToPass() {
+ return rightAnswersCountToPass;
+ }
+
+ @Override
+ public String getTestFileName() {
+ return testFileName;
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestConfig.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestConfig.java
new file mode 100644
index 00000000..716bd231
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestConfig.java
@@ -0,0 +1,5 @@
+package ru.otus.hw.config;
+
+public interface TestConfig {
+ int getRightAnswersCountToPass();
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestFileNameProvider.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestFileNameProvider.java
new file mode 100644
index 00000000..c798b220
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/config/TestFileNameProvider.java
@@ -0,0 +1,5 @@
+package ru.otus.hw.config;
+
+public interface TestFileNameProvider {
+ String getTestFileName();
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/CsvQuestionDao.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/CsvQuestionDao.java
new file mode 100644
index 00000000..3941a647
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/CsvQuestionDao.java
@@ -0,0 +1,22 @@
+package ru.otus.hw.dao;
+
+import lombok.RequiredArgsConstructor;
+import ru.otus.hw.config.TestFileNameProvider;
+import ru.otus.hw.domain.Question;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RequiredArgsConstructor
+public class CsvQuestionDao implements QuestionDao {
+ private final TestFileNameProvider fileNameProvider;
+
+ @Override
+ public List findAll() {
+ // Использовать CsvToBean
+ // https://opencsv.sourceforge.net/#collection_based_bean_fields_one_to_many_mappings
+ // Использовать QuestionReadException
+
+ return new ArrayList<>();
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/QuestionDao.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/QuestionDao.java
new file mode 100644
index 00000000..0e80fc33
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/QuestionDao.java
@@ -0,0 +1,9 @@
+package ru.otus.hw.dao;
+
+import ru.otus.hw.domain.Question;
+
+import java.util.List;
+
+public interface QuestionDao {
+ List findAll();
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/AnswerCsvConverter.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/AnswerCsvConverter.java
new file mode 100644
index 00000000..0f7ccb56
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/AnswerCsvConverter.java
@@ -0,0 +1,13 @@
+package ru.otus.hw.dao.dto;
+
+import com.opencsv.bean.AbstractCsvConverter;
+import ru.otus.hw.domain.Answer;
+
+public class AnswerCsvConverter extends AbstractCsvConverter {
+
+ @Override
+ public Object convertToRead(String value) {
+ var valueArr = value.split("%");
+ return new Answer(valueArr[0], Boolean.parseBoolean(valueArr[1]));
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/QuestionDto.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/QuestionDto.java
new file mode 100644
index 00000000..73758e28
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/dao/dto/QuestionDto.java
@@ -0,0 +1,25 @@
+package ru.otus.hw.dao.dto;
+
+import com.opencsv.bean.CsvBindAndSplitByPosition;
+import com.opencsv.bean.CsvBindByPosition;
+import lombok.Data;
+import ru.otus.hw.domain.Answer;
+import ru.otus.hw.domain.Question;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class QuestionDto {
+
+ @CsvBindByPosition(position = 0)
+ private String text;
+
+ @CsvBindAndSplitByPosition(position = 1, collectionType = ArrayList.class, elementType = Answer.class,
+ converter = AnswerCsvConverter.class, splitOn = "\\|")
+ private List answers;
+
+ public Question toDomainObject() {
+ return new Question(text, answers);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Answer.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Answer.java
new file mode 100644
index 00000000..ca182ae5
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Answer.java
@@ -0,0 +1,4 @@
+package ru.otus.hw.domain;
+
+public record Answer(String text, boolean isCorrect) {
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Question.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Question.java
new file mode 100644
index 00000000..c24e330e
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Question.java
@@ -0,0 +1,6 @@
+package ru.otus.hw.domain;
+
+import java.util.List;
+
+public record Question(String text, List answers) {
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Student.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Student.java
new file mode 100644
index 00000000..102585cd
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/Student.java
@@ -0,0 +1,7 @@
+package ru.otus.hw.domain;
+
+public record Student(String firstName, String lastName) {
+ public String getFullName() {
+ return String.format("%s %s", firstName, lastName);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/TestResult.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/TestResult.java
new file mode 100644
index 00000000..438255bd
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/domain/TestResult.java
@@ -0,0 +1,27 @@
+package ru.otus.hw.domain;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class TestResult {
+ private final Student student;
+
+ private final List answeredQuestions;
+
+ private int rightAnswersCount;
+
+ public TestResult(Student student) {
+ this.student = student;
+ this.answeredQuestions = new ArrayList<>();
+ }
+
+ public void applyAnswer(Question question, boolean isRightAnswer) {
+ answeredQuestions.add(question);
+ if (isRightAnswer) {
+ rightAnswersCount++;
+ }
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/exceptions/QuestionReadException.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/exceptions/QuestionReadException.java
new file mode 100644
index 00000000..9a44efbb
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/exceptions/QuestionReadException.java
@@ -0,0 +1,7 @@
+package ru.otus.hw.exceptions;
+
+public class QuestionReadException extends RuntimeException {
+ public QuestionReadException(String message, Throwable ex) {
+ super(message, ex);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/IOService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/IOService.java
new file mode 100644
index 00000000..00395233
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/IOService.java
@@ -0,0 +1,15 @@
+package ru.otus.hw.service;
+
+public interface IOService {
+ void printLine(String s);
+
+ void printFormattedLine(String s, Object ...args);
+
+ String readString();
+
+ String readStringWithPrompt(String prompt);
+
+ int readIntForRange(int min, int max, String errorMessage);
+
+ int readIntForRangeWithPrompt(int min, int max, String prompt, String errorMessage);
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultService.java
new file mode 100644
index 00000000..13856b64
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultService.java
@@ -0,0 +1,7 @@
+package ru.otus.hw.service;
+
+import ru.otus.hw.domain.TestResult;
+
+public interface ResultService {
+ void showResult(TestResult testResult);
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultServiceImpl.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultServiceImpl.java
new file mode 100644
index 00000000..a04c18d8
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/ResultServiceImpl.java
@@ -0,0 +1,28 @@
+package ru.otus.hw.service;
+
+import lombok.RequiredArgsConstructor;
+import ru.otus.hw.config.TestConfig;
+import ru.otus.hw.domain.TestResult;
+
+@RequiredArgsConstructor
+public class ResultServiceImpl implements ResultService {
+
+ private final TestConfig testConfig;
+
+ private final IOService ioService;
+
+ @Override
+ public void showResult(TestResult testResult) {
+ ioService.printLine("");
+ ioService.printLine("Test results: ");
+ ioService.printFormattedLine("Student: %s", testResult.getStudent().getFullName());
+ ioService.printFormattedLine("Answered questions count: %d", testResult.getAnsweredQuestions().size());
+ ioService.printFormattedLine("Right answers count: %d", testResult.getRightAnswersCount());
+
+ if (testResult.getRightAnswersCount() >= testConfig.getRightAnswersCountToPass()) {
+ ioService.printLine("Congratulations! You passed test!");
+ return;
+ }
+ ioService.printLine("Sorry. You fail test.");
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StreamsIOService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StreamsIOService.java
new file mode 100644
index 00000000..fa65cb07
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StreamsIOService.java
@@ -0,0 +1,66 @@
+package ru.otus.hw.service;
+
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Scanner;
+
+public class StreamsIOService implements IOService {
+ private static final int MAX_ATTEMPTS = 10;
+
+ private final PrintStream printStream;
+
+ private final Scanner scanner;
+
+ public StreamsIOService(@Value("#{T(System).out}") PrintStream printStream,
+ @Value("#{T(System).in}") InputStream inputStream) {
+
+ this.printStream = printStream;
+ this.scanner = new Scanner(inputStream);
+ }
+
+ @Override
+ public void printLine(String s) {
+ printStream.println(s);
+ }
+
+ @Override
+ public void printFormattedLine(String s, Object... args) {
+ printStream.printf(s + "%n", args);
+ }
+
+ @Override
+ public String readString() {
+ return scanner.nextLine();
+ }
+
+ @Override
+ public String readStringWithPrompt(String prompt) {
+ printLine(prompt);
+ return scanner.nextLine();
+ }
+
+ @Override
+ public int readIntForRange(int min, int max, String errorMessage) {
+ for (int i = 0; i < MAX_ATTEMPTS; i++) {
+ try {
+ var stringValue = scanner.nextLine();
+ int intValue = Integer.parseInt(stringValue);
+ if (intValue < min || intValue > max) {
+ throw new IllegalArgumentException();
+ }
+ return intValue;
+ } catch (IllegalArgumentException e) {
+ printLine(errorMessage);
+ }
+ }
+ throw new IllegalArgumentException("Error during reading int value");
+ }
+
+ @Override
+ public int readIntForRangeWithPrompt(int min, int max, String prompt, String errorMessage) {
+ printLine(prompt);
+ return readIntForRange(min, max, errorMessage);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentService.java
new file mode 100644
index 00000000..b5f5cad8
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentService.java
@@ -0,0 +1,8 @@
+package ru.otus.hw.service;
+
+import ru.otus.hw.domain.Student;
+
+public interface StudentService {
+
+ Student determineCurrentStudent();
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentServiceImpl.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentServiceImpl.java
new file mode 100644
index 00000000..f8e35450
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/StudentServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.hw.service;
+
+import lombok.RequiredArgsConstructor;
+import ru.otus.hw.domain.Student;
+
+@RequiredArgsConstructor
+public class StudentServiceImpl implements StudentService {
+
+ private final IOService ioService;
+
+ @Override
+ public Student determineCurrentStudent() {
+ var firstName = ioService.readStringWithPrompt("Please input your first name");
+ var lastName = ioService.readStringWithPrompt("Please input your last name");
+ return new Student(firstName, lastName);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerService.java
new file mode 100644
index 00000000..2fb92f9a
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerService.java
@@ -0,0 +1,5 @@
+package ru.otus.hw.service;
+
+public interface TestRunnerService {
+ void run();
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerServiceImpl.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerServiceImpl.java
new file mode 100644
index 00000000..6c2b448f
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestRunnerServiceImpl.java
@@ -0,0 +1,20 @@
+package ru.otus.hw.service;
+
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class TestRunnerServiceImpl implements TestRunnerService {
+
+ private final TestService testService;
+
+ private final StudentService studentService;
+
+ private final ResultService resultService;
+
+ @Override
+ public void run() {
+ var student = studentService.determineCurrentStudent();
+ var testResult = testService.executeTestFor(student);
+ resultService.showResult(testResult);
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestService.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestService.java
new file mode 100644
index 00000000..0b034b4a
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestService.java
@@ -0,0 +1,8 @@
+package ru.otus.hw.service;
+
+import ru.otus.hw.domain.Student;
+import ru.otus.hw.domain.TestResult;
+
+public interface TestService {
+ TestResult executeTestFor(Student student);
+}
diff --git a/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestServiceImpl.java b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestServiceImpl.java
new file mode 100644
index 00000000..49fd8c09
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/java/ru/otus/hw/service/TestServiceImpl.java
@@ -0,0 +1,27 @@
+package ru.otus.hw.service;
+
+import lombok.RequiredArgsConstructor;
+import ru.otus.hw.dao.QuestionDao;
+import ru.otus.hw.domain.Student;
+import ru.otus.hw.domain.TestResult;
+
+@RequiredArgsConstructor
+public class TestServiceImpl implements TestService {
+
+ private final IOService ioService;
+ private final QuestionDao questionDao;
+
+ @Override
+ public TestResult executeTestFor(Student student) {
+ ioService.printLine("");
+ ioService.printFormattedLine("Please answer the questions below%n");
+ var questions = questionDao.findAll();
+ var testResult = new TestResult(student);
+
+ for (var question: questions) {
+ var isAnswerValid = false; // Задать вопрос, получить ответ
+ testResult.applyAnswer(question, isAnswerValid);
+ }
+ return testResult;
+ }
+}
diff --git a/templates/hw02-annotation-config/src/main/resources/application.properties b/templates/hw02-annotation-config/src/main/resources/application.properties
new file mode 100644
index 00000000..4dc0c614
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+test.rightAnswersCountToPass=3
+test.fileName=questions.csv
\ No newline at end of file
diff --git a/templates/hw02-annotation-config/src/main/resources/questions.csv b/templates/hw02-annotation-config/src/main/resources/questions.csv
new file mode 100644
index 00000000..fc0f548b
--- /dev/null
+++ b/templates/hw02-annotation-config/src/main/resources/questions.csv
@@ -0,0 +1,4 @@
+# Добавить сюда своих вопросов. Эту строку надо пропустить при настройке CsvToBean (withSkipLines)
+Is there life on Mars?;Science doesn't know this yet%true|Certainly. The red UFO is from Mars. And green is from Venus%false|Absolutely not%false
+How should resources be loaded form jar in Java?;ClassLoader#geResourceAsStream or ClassPathResource#getInputStream%true|ClassLoader#geResource#getFile + FileReader%false|Wingardium Leviosa%false
+Which option is a good way to handle the exception?;@SneakyThrow%false|e.printStackTrace()%false|Rethrow with wrapping in business exception (for example, QuestionReadException)%true|Ignoring exception%false
\ No newline at end of file