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); + } + } +}