diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..530d0056a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+.idea
+out
+target
+*.iml
+log
+backup
+
+
+
diff --git a/README.md b/README.md
index ed139bed8..514a7de35 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,8 @@
-#### Написание с нуля полнофункционального многомодульного Maven проекта:
-веб приложения (Tomcat, JSP, jQuery),
-многопоточного почтового сервиса (JavaMail, java.util.concurrent.*) и вспомогательных модулей связанных по Веб и REST сервисам (SOAP, JAX-WS, Axis, JAX-RS)
-c сохранением данных в RMDBS и динамическим конфигурирование модулей по JMX.
+## Разработка полнофункционального многомодульного Maven проекта
-## Сервис-ориентированная архитектура, Микросервисы
-- JMS, альтернативы
-- Варианты разворачивания сервисов. Работа с базой. Связывание сервисов.
+- веб приложение (Tomcat, JSP, jQuery),
+- многопоточный почтовый сервиса (JavaMail, java.util.concurrent.*)
+- вспомогательные модули, связанных по веб-сервисам (SOAP, JAX-WS, Axis) и по REST (JAX-RS)
+- сохранение данных в RMDBS и динамическое конфигурирование модулей по JMX.
-## Maven. Многомодульный Maven проект
-- Build Lifecycle
-- Dependency Mechanism
-- Зависимости, профили, написание плагина
-- The Reactor. Snapshots
-
-## Создание/тестирование веб-приложения.
-- Сборка, запуск, локальный и удаленный debug проекта, способы деплоя в Tomcat
-- tomcat7-maven-plugin
-
-### Веб-сервисы
-- Веб-сервисы. SOAP. Преимущества/недостатки веб-сервисов. Расширения.
-- Реализация веб-сервисов в Java. JAX-RPC, JAX-WS, CFX, Axis. Стили WSDL
-- Создание API и реализации веб-сервиса MailService.
-- Деплой и тестирование через SoapUI.
-
-## Доработка веб-сервиса. Кастомизация WSDL.
-- Работа с JAXB.
-- Передача по SOAP Exception
-- Включение wsdl в сервис для публикации.
-- Генерация java кода по WSDL
-
-## Реализация клиент веб-сервиса.
-- Публикация веб сервиса из main(). Дабавление wsdl
-- Выделение из wsdl общей части
-- Создание клиента почтового сервиса.
-- Тестирование с помощью JUnit 4
-- Интеграционное тестирование, maven-failsafe-plugin
-
-## JAX-WS Handlers
-- Logical/protocol handlers.
-- Логирование SOAP на стороне клиента.
-- Логирование и статистика трафика опубликованного веб-сервиса.
-- wsimport binding.
-- SoapHandler аутентификация.
-Добавляем файлы вложения. Mail-Service.
-
-## Создаем вложения почты
-- Генерация обновленного WSDL через wsgen
-- Веб-сервисы: JAX-WS attachment with MTOM
-- Тестирование вложений через SoapUi.
-
-## Загрузка файлов.
-- Стандарт MIME. Обрабатываем вложения на форме: commons-fileupload
-- Загрузка файла вместе в полями формы.
-- Вызов клиента с вложениями.
-
-## Персистентность.
-- NoSQL or RDBMS. Обзор NoSQL систем. CAP
-- Обзор Java persistence solution: commons-dbutils, Spring JdbcTemplate, MyBatis, JOOQ, ORM (Hibernate, TopLink, ElipseLink, EBean used in Playframework). JPA. JPA Performance Benchmark
-- Работа с базой: создание базы, настройка IDEA Database.
-- Работа с DB через DataSource, настройка tomcat. HikariCP
-- Настройка работы с DataSource из JUnit.
-
-## REST веб сервис.
-- JAX-RS. Интеграция с Jersey
-- Поддержка Json. Jackson
-
-## Асинхронность.
-- @OneWay vs Java Execution framework
-- Добавление в клиенте асинхронных вызовов.
-- Асинхронные сервлеты 3.x в Tomcat
-
-## Динамическое конфигурирование. JMX
-- Maven Groovy cкрптинг. groovy-maven-plugin
-- Настройка Tomcat на удаленное администрирование по JMX
-
-## Отправка email в многопоточном приложении
-- Initialization on demand holder / Double-checked locking
-- java.util.concurrent.*: Executors , Synchronizers, Concurrent Collections, Lock
-
-## Проблема MemoryLeak. Поиск утечки памяти.
+## Описание курса
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..39e811e08
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,44 @@
+
+ 4.0.0
+
+ ru.javaops
+ masterjava
+ jar
+
+ 1.0-SNAPSHOT
+
+ Master Java
+ https://github.com/JavaOPs/masterjava
+
+
+ 1.8
+ UTF-8
+ UTF-8
+
+
+
+ masterjava
+ install
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ ${java.version}
+ ${java.version}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/ru/javaops/masterjava/LazySingleton.java b/src/main/java/ru/javaops/masterjava/LazySingleton.java
new file mode 100644
index 000000000..1e4498317
--- /dev/null
+++ b/src/main/java/ru/javaops/masterjava/LazySingleton.java
@@ -0,0 +1,32 @@
+package ru.javaops.masterjava;
+
+/**
+ * gkislin
+ * 26.07.2016
+ */
+public class LazySingleton {
+ private static LazySingleton instance;
+ private final int i;
+
+ private static class LazyHolder {
+ private static final LazySingleton INSTANCE = new LazySingleton();
+ }
+
+ public static LazySingleton getInstance() {
+ return LazyHolder.INSTANCE;
+/*
+ if (instance == null) {
+ synchronized (LazySingleton.class) {
+ if (instance == null) {
+ instance = new LazySingleton();
+ }
+ }
+ }
+ return instance;
+*/
+ }
+
+ private LazySingleton() {
+ i = 5 + 8;
+ }
+}
diff --git a/src/main/java/ru/javaops/masterjava/Main.java b/src/main/java/ru/javaops/masterjava/Main.java
new file mode 100644
index 000000000..a849258c4
--- /dev/null
+++ b/src/main/java/ru/javaops/masterjava/Main.java
@@ -0,0 +1,14 @@
+package ru.javaops.masterjava;
+
+/**
+ * User: gkislin
+ * Date: 05.08.2015
+ *
+ * @link http://caloriesmng.herokuapp.com/
+ * @link https://github.com/JavaOPs/topjava
+ */
+public class Main {
+ public static void main(String[] args) {
+ System.out.format("Hello MasterJava!");
+ }
+}
diff --git a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java
new file mode 100644
index 000000000..5cbe46326
--- /dev/null
+++ b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java
@@ -0,0 +1,66 @@
+package ru.javaops.masterjava.matrix;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * gkislin
+ * 03.07.2016
+ */
+public class MainMatrix {
+ private static final int MATRIX_SIZE = 1000;
+ private static final int THREAD_NUMBER = 10;
+
+ private final static ExecutorService executor = Executors.newFixedThreadPool(MainMatrix.THREAD_NUMBER);
+
+ public static void main(String[] args) throws ExecutionException, InterruptedException {
+ final int[][] matrixA = MatrixUtil.create(MATRIX_SIZE);
+ final int[][] matrixB = MatrixUtil.create(MATRIX_SIZE);
+
+ double singleThreadSum = 0.;
+ double singleThreadImprovedSum = 0.;
+ double concurrentThreadSum = 0.;
+ double concurrentImprovedThreadSum = 0.;
+ for (int i = 0; i < 5; i++) {
+
+ long start = System.currentTimeMillis();
+ final int[][] matrixC = MatrixUtil.singleThreadMultiply(matrixA, matrixB);
+ double duration = (System.currentTimeMillis() - start) / 1000.;
+ out("Single thread time, sec: %.3f", duration);
+ singleThreadSum += duration;
+
+ start = System.currentTimeMillis();
+ final int[][] improvedMatrixC = MatrixUtil.singleThreadMultiplyImproved(matrixA, matrixB);
+ duration = (System.currentTimeMillis() - start) / 1000.;
+ out("Single thread improved time, sec: %.3f", duration);
+ singleThreadImprovedSum += duration;
+
+ start = System.currentTimeMillis();
+ final int[][] concurrentMatrixC = MatrixUtil.concurrentMultiply(matrixA, matrixB, executor);
+ duration = (System.currentTimeMillis() - start) / 1000.;
+ out("Concurrent thread time, sec: %.3f", duration);
+ concurrentThreadSum += duration;
+
+ start = System.currentTimeMillis();
+ final int[][] concurrentImprovedMatrixC = MatrixUtil.concurrentMultiplyImproved(matrixA, matrixB, executor);
+ duration = (System.currentTimeMillis() - start) / 1000.;
+ out("Concurrent thread improved time, sec: %.3f", duration);
+ concurrentImprovedThreadSum += duration;
+
+ if (!MatrixUtil.compare(matrixC, concurrentMatrixC)) {
+ System.err.println("Comparison failed");
+ break;
+ }
+ }
+ executor.shutdown();
+ out("\nAverage single thread time, sec: %.3f", singleThreadSum / 5.);
+ out("Average single thread improved time, sec: %.3f", singleThreadImprovedSum / 5.);
+ out("Average concurrent thread time, sec: %.3f", concurrentThreadSum / 5.);
+ out("Average concurrent thread improved time, sec: %.3f", concurrentImprovedThreadSum / 5.);
+ }
+
+ private static void out(String format, double ms) {
+ System.out.println(String.format(format, ms));
+ }
+}
diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java
new file mode 100644
index 000000000..8d339b728
--- /dev/null
+++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java
@@ -0,0 +1,126 @@
+package ru.javaops.masterjava.matrix;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.*;
+
+/**
+ * gkislin
+ * 03.07.2016
+ */
+public class MatrixUtil {
+
+ public static int[][] concurrentMultiply(final int[][] matrixA, final int[][] matrixB, final ExecutorService executor) throws InterruptedException, ExecutionException {
+
+ final int matrixSize = matrixA.length;
+ final int[][] matrixC = new int[matrixSize][matrixSize];
+ List> taskList = new ArrayList<>();
+ for (int i = 0; i < matrixSize; i++) {
+ final int i2 = i;
+ for (int j = 0; j < matrixSize; j++) {
+ final int j2 = j;
+ taskList.add(() ->
+ {
+ int sum = 0;
+ for (int k = 0; k < matrixSize; k++) {
+ sum += matrixA[i2][k] * matrixB[k][j2];
+ }
+ matrixC[i2][j2] = sum;
+ return true;
+ });
+ }
+ }
+ executor.invokeAll(taskList);
+ return matrixC;
+ }
+
+ public static int[][] concurrentMultiplyImproved(final int[][] matrixA, final int[][] matrixB, final ExecutorService executor) throws InterruptedException, ExecutionException {
+
+ final int matrixSize = matrixA.length;
+ final int[][] matrixC = new int[matrixSize][matrixSize];
+ int thatColumn[] = new int[matrixSize];
+ List> taskList = new ArrayList<>();
+ for (int j = 0; j < matrixSize; j++) {
+ final int j2 = j;
+ for (int k = 0; k < matrixSize; k++) {
+ thatColumn[k] = matrixB[k][j];
+ }
+ for (int i = 0; i < matrixSize; i++) {
+ final int i2 = i;
+ int thisRow[] = matrixA[i];
+ taskList.add(() ->
+ {
+ int sum = 0;
+ for (int k = 0; k < matrixSize; k++) {
+ sum += thisRow[k] * thatColumn[k];
+ }
+ matrixC[i2][j2] = sum;
+ return true;
+ });
+ }
+ }
+ executor.invokeAll(taskList);
+ return matrixC;
+ }
+
+ public static int[][] singleThreadMultiply(int[][] matrixA, int[][] matrixB) {
+ final int matrixSize = matrixA.length;
+ final int[][] matrixC = new int[matrixSize][matrixSize];
+
+ for (int i = 0; i < matrixSize; i++) {
+ for (int j = 0; j < matrixSize; j++) {
+ int sum = 0;
+ for (int k = 0; k < matrixSize; k++) {
+ sum += matrixA[i][k] * matrixB[k][j];
+ }
+ matrixC[i][j] = sum;
+ }
+ }
+ return matrixC;
+ }
+
+ public static int[][] singleThreadMultiplyImproved(int[][] matrixA, int[][] matrixB) {
+ final int matrixSize = matrixA.length;
+ final int[][] matrixC = new int[matrixSize][matrixSize];
+ int thatColumn[] = new int[matrixSize];
+ for (int j = 0; j < matrixSize; j++) {
+ for (int k = 0; k < matrixSize; k++) {
+ thatColumn[k] = matrixB[k][j];
+ }
+ for (int i = 0; i < matrixSize; i++) {
+ int thisRow[] = matrixA[i];
+ int sum = 0;
+ for (int k = 0; k < matrixSize; k++) {
+ sum += thisRow[k] * thatColumn[k];
+ }
+ matrixC[i][j] = sum;
+ }
+ }
+ return matrixC;
+ }
+
+ public static int[][] create(int size) {
+ int[][] matrix = new int[size][size];
+ Random rn = new Random();
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ matrix[i][j] = rn.nextInt(10);
+ }
+ }
+ return matrix;
+ }
+
+ public static boolean compare(int[][] matrixA, int[][] matrixB) {
+ final int matrixSize = matrixA.length;
+ for (int i = 0; i < matrixSize; i++) {
+ for (int j = 0; j < matrixSize; j++) {
+ if (matrixA[i][j] != matrixB[i][j]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/ru/javaops/masterjava/service/MailService.java b/src/main/java/ru/javaops/masterjava/service/MailService.java
new file mode 100644
index 000000000..c8fae6d6a
--- /dev/null
+++ b/src/main/java/ru/javaops/masterjava/service/MailService.java
@@ -0,0 +1,141 @@
+package ru.javaops.masterjava.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
+public class MailService {
+ private static final String OK = "OK";
+
+ private static final String INTERRUPTED_BY_FAULTS_NUMBER = "+++ Interrupted by faults number";
+ private static final String INTERRUPTED_BY_TIMEOUT = "+++ Interrupted by timeout";
+ private static final String INTERRUPTED_EXCEPTION = "+++ InterruptedException";
+
+ private final ExecutorService mailExecutor = Executors.newFixedThreadPool(8);
+
+ public GroupResult sendToList(final String template, final Set emails) throws Exception {
+ final CompletionService completionService = new ExecutorCompletionService<>(mailExecutor);
+
+ List> futures = emails.stream()
+ .map(email -> mailExecutor.submit(() -> sendToUser(template, email)))
+ .collect(Collectors.toList());
+
+ return new Callable() {
+ private int success = 0;
+ private List failed = new ArrayList<>();
+
+ @Override
+ public GroupResult call() {
+ while (!futures.isEmpty()) {
+ try {
+ Future future = completionService.poll(10, TimeUnit.SECONDS);
+ if (future == null) {
+ return cancelWithFail(INTERRUPTED_BY_TIMEOUT);
+ }
+ futures.remove(future);
+ MailResult mailResult = future.get();
+ if (mailResult.isOk()) {
+ success++;
+ } else {
+ failed.add(mailResult);
+ if (failed.size() >= 5) {
+ return cancelWithFail(INTERRUPTED_BY_FAULTS_NUMBER);
+ }
+ }
+ } catch (ExecutionException e) {
+ return cancelWithFail(e.getCause().toString());
+ } catch (InterruptedException e) {
+ return cancelWithFail(INTERRUPTED_EXCEPTION);
+ }
+ }
+/*
+ for (Future future : futures) {
+ MailResult mailResult;
+ try {
+ mailResult = future.get(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return cancelWithFail(INTERRUPTED_EXCEPTION);
+ } catch (ExecutionException e) {
+ return cancelWithFail(e.getCause().toString());
+ } catch (TimeoutException e) {
+ return cancelWithFail(INTERRUPTED_BY_TIMEOUT);
+ }
+ if (mailResult.isOk()) {
+ success++;
+ } else {
+ failed.add(mailResult);
+ if (failed.size() >= 5) {
+ return cancelWithFail(INTERRUPTED_BY_FAULTS_NUMBER);
+ }
+ }
+ }
+*/
+ return new GroupResult(success, failed, null);
+ }
+
+ private GroupResult cancelWithFail(String cause) {
+ futures.forEach(f -> f.cancel(true));
+ return new GroupResult(success, failed, cause);
+ }
+ }.call();
+ }
+
+ // dummy realization
+ public MailResult sendToUser(String template, String email) throws Exception {
+ try {
+ Thread.sleep(500); //delay
+ } catch (InterruptedException e) {
+ // log cancel;
+ return null;
+ }
+ return Math.random() < 0.7 ? MailResult.ok(email) : MailResult.error(email, "Error");
+ }
+
+ public static class MailResult {
+ private final String email;
+ private final String result;
+
+ private static MailResult ok(String email) {
+ return new MailResult(email, OK);
+ }
+
+ private static MailResult error(String email, String error) {
+ return new MailResult(email, error);
+ }
+
+ public boolean isOk() {
+ return OK.equals(result);
+ }
+
+ private MailResult(String email, String cause) {
+ this.email = email;
+ this.result = cause;
+ }
+
+ @Override
+ public String toString() {
+ return '(' + email + ',' + result + ')';
+ }
+ }
+
+ public static class GroupResult {
+ private final int success; // number of successfully sent email
+ private final List failed; // failed emails with causes
+ private final String failedCause; // global fail cause
+
+ public GroupResult(int success, List failed, String failedCause) {
+ this.success = success;
+ this.failed = failed;
+ this.failedCause = failedCause;
+ }
+
+ @Override
+ public String toString() {
+ return "Success: " + success + '\n' +
+ "Failed: " + failed.toString() + '\n' +
+ (failedCause == null ? "" : "Failed cause" + failedCause);
+ }
+ }
+}