From 22fc85d3f3eeee18024c3ed4b46047f52e92d811 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Tue, 17 Oct 2017 11:50:59 +0300 Subject: [PATCH 01/87] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 127b1304a..a6de25e17 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - используя асинхронные сервлеты 3.0 - сохранение данных в PostgreSQL используя [jDBI](http://jdbi.org/) - миграция базы [LiquiBase](http://www.liquibase.org/) -- использование в проекте [Guava](https://github.com/google/guava/wiki), [Thymleaf](http://www.thymeleaf.org/), [Lombook](https://projectlombok.org/), [StreamEx](https://github.com/amaembo/streamex), +- использование в проекте [Guava](https://github.com/google/guava/wiki), [Thymleaf](http://www.thymeleaf.org/), [Lombok](https://projectlombok.org/), [StreamEx](https://github.com/amaembo/streamex), [Typesafe Config](https://github.com/typesafehub/config), [Java Microbenchmark JMH](http://openjdk.java.net/projects/code-tools/jmh) ### Требование к участникам @@ -134,14 +134,14 @@ MatrixBenchmark.concurrentMultiply3 1000 ss 100 186,827 ± 11,882 - Maven. Поиск и разрешение конфликтов зависимостей - Подключаем логирование с общими настройкам - Библиотеки и фреймворки для работы с JDBC. -- Модуль persist +- Модуль persistence ## Занятие 5 - Разбор ДЗ - Сохранение в базу в batch-моде с обработкой конфликтов - Вставка в несколько потоков - Конфигурирование приложения (Typesafe config) -- Lombook +- Lombok ## Занятие 6 - Разбор ДЗ (доработка модели и модуля export) From 66dbb57f307e8ac7f01f61af948a3a315a40b4f5 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Mon, 23 Oct 2017 05:31:40 +0300 Subject: [PATCH 02/87] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a6de25e17..2d26e390c 100644 --- a/README.md +++ b/README.md @@ -99,9 +99,9 @@ ----- ## ![error](https://cloud.githubusercontent.com/assets/13649199/13672935/ef09ec1e-e6e7-11e5-9f79-d1641c05cbe6.png) Подсказки по HW1 -- не делайте 1000 000 тасок, лучше их сделать крупнее -- у меня разница между 4 и 1000 тасками по времени незаметна, поэтому делайте просто и не делайте сложно -- наконец: можно не считать значение элемента результирующей матрицы C за раз, а накапливать (`concurrentMultiply3`). Мои результаты: +- 1: не делайте 1000 000 тасок, лучше их сделать крупнее +- 2: у меня разница между 4 и 1000 тасками по времени незаметна, поэтому делайте просто и не делайте сложно +- 3: наконец: можно не считать значение элемента результирующей матрицы C за раз, а накапливать (`concurrentMultiply3`). Тогда трансформация B не нужна. Мои результаты: ``` Benchmark (matrixSize) Mode Cnt Score Error Units MatrixBenchmark.singleThreadMultiplyOpt 1000 ss 100 837,867 ± 25,530 ms/op From 4b3a183635f38c7190971038b7a720395f808f41 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Thu, 22 Feb 2018 18:07:56 +0300 Subject: [PATCH 03/87] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2d26e390c..6157a56d3 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ #### Скачайте [1_1_MailService.patch](https://drive.google.com/open?id=0B9Ye2auQ_NsFTE5ZV3pzWElxTWM), положите его в проект, правой мышкой на нем сделайте Apply Patch ... ---------------------------- +- [Как сделать Java код проще и нагляднее](https://habrahabr.ru/company/wrike/blog/349652/) ### Ресурсы (основы) - Intuit, Потоки выполнения. Синхронизация From 389d911b60d0e1ddca77dbde8c9d34311e5f115a Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Thu, 8 Nov 2018 16:37:46 +0300 Subject: [PATCH 04/87] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6157a56d3..e5fe3209b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ > В видео в `LazySingleton` ошибка: должно быть как в коде проекта `instance == null` ### Структура памяти: куча, стек, permanent/metaspace - - JVM изнутри - оптимизация и профилирование. + - JVM изнутри - оптимизация и профилирование. - Stack and Heap - Дополнительно: - Из каких частей состоит память java процесса. From 45f97151852c296e41061236c0c9b5ab3e5dd331 Mon Sep 17 00:00:00 2001 From: Java Online Projects Date: Thu, 27 Dec 2018 19:32:21 +0300 Subject: [PATCH 05/87] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e5fe3209b..2ece5d267 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ - Permanent область памяти - Java thread stack - Размер Java объектов + - Оптимизация памяти + - [Escape analysis и скаляризация: Пусть GC отдохнет](https://habr.com/company/jugru/blog/322348) + - [Условия для размещения объекта в стеке](https://stackoverflow.com/a/43002529/548473) ### Ленивая инициализация - Реализация Singleton в JAVA From cbd0d5d641b96c006646886b15a059681c474a8f Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Mon, 7 Jan 2019 20:21:03 +0300 Subject: [PATCH 06/87] 2 01 HW1 singleThreadMultiplyOpt --- .../javaops/masterjava/matrix/MainMatrix.java | 2 +- .../javaops/masterjava/matrix/MatrixUtil.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java index ec1c8a691..0695132ef 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java @@ -24,7 +24,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc while (count < 6) { System.out.println("Pass " + count); long start = System.currentTimeMillis(); - final int[][] matrixC = MatrixUtil.singleThreadMultiply(matrixA, matrixB); + final int[][] matrixC = MatrixUtil.singleThreadMultiplyOpt(matrixA, matrixB); double duration = (System.currentTimeMillis() - start) / 1000.; out("Single thread time, sec: %.3f", duration); singleThreadSum += duration; diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java index 80a344ac2..64cfdbe81 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java @@ -18,18 +18,24 @@ public static int[][] concurrentMultiply(int[][] matrixA, int[][] matrixB, Execu return matrixC; } - // TODO optimize by https://habrahabr.ru/post/114797/ - public static int[][] singleThreadMultiply(int[][] matrixA, int[][] matrixB) { + // Optimized by https://habrahabr.ru/post/114797/ + public static int[][] singleThreadMultiplyOpt(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++) { + for (int col = 0; col < matrixSize; col++) { + final int[] columnB = new int[matrixSize]; + for (int k = 0; k < matrixSize; k++) { + columnB[k] = matrixB[k][col]; + } + + for (int row = 0; row < matrixSize; row++) { int sum = 0; + final int[] rowA = matrixA[row]; for (int k = 0; k < matrixSize; k++) { - sum += matrixA[i][k] * matrixB[k][j]; + sum += rowA[k] * columnB[k]; } - matrixC[i][j] = sum; + matrixC[row][col] = sum; } } return matrixC; From e5cee080fd181654889f5198179f75c710405674 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Mon, 7 Jan 2019 20:35:04 +0300 Subject: [PATCH 07/87] 2 02 HW1 concurrentMultiply --- .../javaops/masterjava/matrix/MatrixUtil.java | 153 +++++++++++++++++- 1 file changed, 148 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java index 64cfdbe81..46fd00ede 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java @@ -1,8 +1,9 @@ package ru.javaops.masterjava.matrix; -import java.util.Random; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * gkislin @@ -10,11 +11,153 @@ */ public class MatrixUtil { - // TODO implement parallel multiplication matrixA*matrixB public static int[][] concurrentMultiply(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { final int matrixSize = matrixA.length; final int[][] matrixC = new int[matrixSize][matrixSize]; + class ColumnMultipleResult { + private final int col; + private final int[] columnC; + + private ColumnMultipleResult(int col, int[] columnC) { + this.col = col; + this.columnC = columnC; + } + } + + final CompletionService completionService = new ExecutorCompletionService<>(executor); + + for (int j = 0; j < matrixSize; j++) { + final int col = j; + final int[] columnB = new int[matrixSize]; + for (int k = 0; k < matrixSize; k++) { + columnB[k] = matrixB[k][col]; + } + completionService.submit(() -> { + final int[] columnC = new int[matrixSize]; + + for (int row = 0; row < matrixSize; row++) { + final int[] rowA = matrixA[row]; + int sum = 0; + for (int k = 0; k < matrixSize; k++) { + sum += rowA[k] * columnB[k]; + } + columnC[row] = sum; + } + return new ColumnMultipleResult(col, columnC); + }); + } + + for (int i = 0; i < matrixSize; i++) { + ColumnMultipleResult res = completionService.take().get(); + for (int k = 0; k < matrixSize; k++) { + matrixC[k][res.col] = res.columnC[k]; + } + } + return matrixC; + } + + public static int[][] concurrentMultiplyCayman(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { + final int matrixSize = matrixA.length; + final int[][] matrixResult = new int[matrixSize][matrixSize]; + final int threadCount = Runtime.getRuntime().availableProcessors(); + final int maxIndex = matrixSize * matrixSize; + final int cellsInThread = maxIndex / threadCount; + final int[][] matrixBFinal = new int[matrixSize][matrixSize]; + + for (int i = 0; i < matrixSize; i++) { + for (int j = 0; j < matrixSize; j++) { + matrixBFinal[i][j] = matrixB[j][i]; + } + } + + Set> threads = new HashSet<>(); + int fromIndex = 0; + for (int i = 1; i <= threadCount; i++) { + final int toIndex = i == threadCount ? maxIndex : fromIndex + cellsInThread; + final int firstIndexFinal = fromIndex; + threads.add(() -> { + for (int j = firstIndexFinal; j < toIndex; j++) { + final int row = j / matrixSize; + final int col = j % matrixSize; + + int sum = 0; + for (int k = 0; k < matrixSize; k++) { + sum += matrixA[row][k] * matrixBFinal[col][k]; + } + matrixResult[row][col] = sum; + } + return true; + }); + fromIndex = toIndex; + } + executor.invokeAll(threads); + return matrixResult; + } + + public static int[][] concurrentMultiplyDarthVader(int[][] matrixA, int[][] matrixB, ExecutorService executor) + throws InterruptedException, ExecutionException { + + final int matrixSize = matrixA.length; + final int[][] matrixC = new int[matrixSize][matrixSize]; + + List> tasks = IntStream.range(0, matrixSize) + .parallel() + .mapToObj(i -> new Callable() { + private final int[] tempColumn = new int[matrixSize]; + + @Override + public Void call() throws Exception { + for (int c = 0; c < matrixSize; c++) { + tempColumn[c] = matrixB[c][i]; + } + for (int j = 0; j < matrixSize; j++) { + int row[] = matrixA[j]; + int sum = 0; + for (int k = 0; k < matrixSize; k++) { + sum += tempColumn[k] * row[k]; + } + matrixC[j][i] = sum; + } + return null; + } + }) + .collect(Collectors.toList()); + + executor.invokeAll(tasks); + return matrixC; + } + + public static int[][] concurrentMultiply2(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { + final int matrixSize = matrixA.length; + final int[][] matrixC = new int[matrixSize][]; + + final int[][] matrixBT = new int[matrixSize][matrixSize]; + for (int i = 0; i < matrixSize; i++) { + for (int j = 0; j < matrixSize; j++) { + matrixBT[i][j] = matrixB[j][i]; + } + } + + List> tasks = new ArrayList<>(matrixSize); + for (int j = 0; j < matrixSize; j++) { + final int row = j; + tasks.add(() -> { + final int[] rowC = new int[matrixSize]; + for (int col = 0; col < matrixSize; col++) { + final int[] rowA = matrixA[row]; + final int[] columnB = matrixBT[col]; + int sum = 0; + for (int k = 0; k < matrixSize; k++) { + sum += rowA[k] * columnB[k]; + } + rowC[col] = sum; + } + matrixC[row] = rowC; + return null; + }); + } + executor.invokeAll(tasks); return matrixC; } @@ -64,4 +207,4 @@ public static boolean compare(int[][] matrixA, int[][] matrixB) { } return true; } -} +} \ No newline at end of file From 5c34f7f10b3e9aa9ee6a69b7a29748289905a987 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Mon, 7 Jan 2019 20:40:39 +0300 Subject: [PATCH 08/87] 2 03 HW1 concurrentMultiply2 --- .../javaops/masterjava/matrix/MainMatrix.java | 6 +- .../javaops/masterjava/matrix/MatrixUtil.java | 177 ++++++------------ 2 files changed, 63 insertions(+), 120 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java index 0695132ef..1f1380674 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java @@ -4,10 +4,6 @@ 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; @@ -30,7 +26,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc singleThreadSum += duration; start = System.currentTimeMillis(); - final int[][] concurrentMatrixC = MatrixUtil.concurrentMultiply(matrixA, matrixB, executor); + final int[][] concurrentMatrixC = MatrixUtil.concurrentMultiplyStreams(matrixA, matrixB, Runtime.getRuntime().availableProcessors() - 1); duration = (System.currentTimeMillis() - start) / 1000.; out("Concurrent thread time, sec: %.3f", duration); concurrentThreadSum += duration; diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java index 46fd00ede..d00a67a05 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java @@ -1,134 +1,39 @@ package ru.javaops.masterjava.matrix; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import java.util.concurrent.*; -import java.util.stream.Collectors; import java.util.stream.IntStream; -/** - * gkislin - * 03.07.2016 - */ public class MatrixUtil { - public static int[][] concurrentMultiply(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { - final int matrixSize = matrixA.length; - final int[][] matrixC = new int[matrixSize][matrixSize]; - - class ColumnMultipleResult { - private final int col; - private final int[] columnC; - - private ColumnMultipleResult(int col, int[] columnC) { - this.col = col; - this.columnC = columnC; - } - } - - final CompletionService completionService = new ExecutorCompletionService<>(executor); - - for (int j = 0; j < matrixSize; j++) { - final int col = j; - final int[] columnB = new int[matrixSize]; - for (int k = 0; k < matrixSize; k++) { - columnB[k] = matrixB[k][col]; - } - completionService.submit(() -> { - final int[] columnC = new int[matrixSize]; - - for (int row = 0; row < matrixSize; row++) { - final int[] rowA = matrixA[row]; - int sum = 0; - for (int k = 0; k < matrixSize; k++) { - sum += rowA[k] * columnB[k]; - } - columnC[row] = sum; - } - return new ColumnMultipleResult(col, columnC); - }); - } - - for (int i = 0; i < matrixSize; i++) { - ColumnMultipleResult res = completionService.take().get(); - for (int k = 0; k < matrixSize; k++) { - matrixC[k][res.col] = res.columnC[k]; - } - } - return matrixC; - } - - public static int[][] concurrentMultiplyCayman(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { - final int matrixSize = matrixA.length; - final int[][] matrixResult = new int[matrixSize][matrixSize]; - final int threadCount = Runtime.getRuntime().availableProcessors(); - final int maxIndex = matrixSize * matrixSize; - final int cellsInThread = maxIndex / threadCount; - final int[][] matrixBFinal = new int[matrixSize][matrixSize]; - - for (int i = 0; i < matrixSize; i++) { - for (int j = 0; j < matrixSize; j++) { - matrixBFinal[i][j] = matrixB[j][i]; - } - } - - Set> threads = new HashSet<>(); - int fromIndex = 0; - for (int i = 1; i <= threadCount; i++) { - final int toIndex = i == threadCount ? maxIndex : fromIndex + cellsInThread; - final int firstIndexFinal = fromIndex; - threads.add(() -> { - for (int j = firstIndexFinal; j < toIndex; j++) { - final int row = j / matrixSize; - final int col = j % matrixSize; - - int sum = 0; - for (int k = 0; k < matrixSize; k++) { - sum += matrixA[row][k] * matrixBFinal[col][k]; - } - matrixResult[row][col] = sum; - } - return true; - }); - fromIndex = toIndex; - } - executor.invokeAll(threads); - return matrixResult; - } - - public static int[][] concurrentMultiplyDarthVader(int[][] matrixA, int[][] matrixB, ExecutorService executor) + public static int[][] concurrentMultiplyStreams(int[][] matrixA, int[][] matrixB, int threadNumber) throws InterruptedException, ExecutionException { final int matrixSize = matrixA.length; final int[][] matrixC = new int[matrixSize][matrixSize]; - List> tasks = IntStream.range(0, matrixSize) - .parallel() - .mapToObj(i -> new Callable() { - private final int[] tempColumn = new int[matrixSize]; - - @Override - public Void call() throws Exception { - for (int c = 0; c < matrixSize; c++) { - tempColumn[c] = matrixB[c][i]; - } - for (int j = 0; j < matrixSize; j++) { - int row[] = matrixA[j]; - int sum = 0; - for (int k = 0; k < matrixSize; k++) { - sum += tempColumn[k] * row[k]; + new ForkJoinPool(threadNumber).submit( + () -> IntStream.range(0, matrixSize) + .parallel() + .forEach(row -> { + final int[] rowA = matrixA[row]; + final int[] rowC = matrixC[row]; + + for (int idx = 0; idx < matrixSize; idx++) { + final int elA = rowA[idx]; + final int[] rowB = matrixB[idx]; + for (int col = 0; col < matrixSize; col++) { + rowC[col] += elA * rowB[col]; + } } - matrixC[j][i] = sum; - } - return null; - } - }) - .collect(Collectors.toList()); + })).get(); - executor.invokeAll(tasks); return matrixC; } - public static int[][] concurrentMultiply2(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { + public static int[][] concurrentMultiply(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException, ExecutionException { final int matrixSize = matrixA.length; final int[][] matrixC = new int[matrixSize][]; @@ -161,7 +66,30 @@ public static int[][] concurrentMultiply2(int[][] matrixA, int[][] matrixB, Exec return matrixC; } - // Optimized by https://habrahabr.ru/post/114797/ + public static int[][] concurrentMultiply2(int[][] matrixA, int[][] matrixB, ExecutorService executor) throws InterruptedException { + final int matrixSize = matrixA.length; + final int[][] matrixC = new int[matrixSize][matrixSize]; + final CountDownLatch latch = new CountDownLatch(matrixSize); + + for (int row = 0; row < matrixSize; row++) { + final int[] rowA = matrixA[row]; + final int[] rowC = matrixC[row]; + + executor.submit(() -> { + for (int idx = 0; idx < matrixSize; idx++) { + final int elA = rowA[idx]; + final int[] rowB = matrixB[idx]; + for (int col = 0; col < matrixSize; col++) { + rowC[col] += elA * rowB[col]; + } + } + latch.countDown(); + }); + } + latch.await(); + return matrixC; + } + public static int[][] singleThreadMultiplyOpt(int[][] matrixA, int[][] matrixB) { final int matrixSize = matrixA.length; final int[][] matrixC = new int[matrixSize][matrixSize]; @@ -184,6 +112,25 @@ public static int[][] singleThreadMultiplyOpt(int[][] matrixA, int[][] matrixB) return matrixC; } + public static int[][] singleThreadMultiplyOpt2(int[][] matrixA, int[][] matrixB) { + final int matrixSize = matrixA.length; + final int[][] matrixC = new int[matrixSize][matrixSize]; + + for (int row = 0; row < matrixSize; row++) { + final int[] rowA = matrixA[row]; + final int[] rowC = matrixC[row]; + + for (int idx = 0; idx < matrixSize; idx++) { + final int elA = rowA[idx]; + final int[] rowB = matrixB[idx]; + for (int col = 0; col < matrixSize; col++) { + rowC[col] += elA * rowB[col]; + } + } + } + return matrixC; + } + public static int[][] create(int size) { int[][] matrix = new int[size][size]; Random rn = new Random(); From 0a546014c35e00ee6179dc5da79b621056e057ca Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 20 Jan 2019 08:27:03 +0300 Subject: [PATCH 09/87] 2 04 JMH Benchmark --- pom.xml | 13 +++- .../masterjava/matrix/MatrixBenchmark.java | 70 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java diff --git a/pom.xml b/pom.xml index 39e811e08..9f0832e4e 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} @@ -34,6 +34,17 @@ + + org.openjdk.jmh + jmh-core + RELEASE + + + org.openjdk.jmh + jmh-generator-annprocess + RELEASE + provided + diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java new file mode 100644 index 000000000..65fc52878 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java @@ -0,0 +1,70 @@ +package ru.javaops.masterjava.matrix; + +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@BenchmarkMode({Mode.SingleShotTime}) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Threads(1) +@Fork(10) +@Timeout(time = 5, timeUnit = TimeUnit.MINUTES) +public class MatrixBenchmark { + + // Matrix size + private static final int MATRIX_SIZE = 1000; + + @Param({"3", "4", "10"}) + private int threadNumber; + + private static int[][] matrixA; + private static int[][] matrixB; + + @Setup + public void setUp() { + matrixA = MatrixUtil.create(MATRIX_SIZE); + matrixB = MatrixUtil.create(MATRIX_SIZE); + } + + private ExecutorService executor; + + // @Benchmark + public int[][] singleThreadMultiplyOpt() throws Exception { + return MatrixUtil.singleThreadMultiplyOpt(matrixA, matrixB); + } + + // @Benchmark + public int[][] singleThreadMultiplyOpt2() throws Exception { + return MatrixUtil.singleThreadMultiplyOpt(matrixA, matrixB); + } + + @Benchmark + public int[][] concurrentMultiplyStreams() throws Exception { + return MatrixUtil.concurrentMultiplyStreams(matrixA, matrixB, threadNumber); + } + + // @Benchmark + public int[][] concurrentMultiply() throws Exception { + return MatrixUtil.concurrentMultiply(matrixA, matrixB, executor); + } + + @Benchmark + public int[][] concurrentMultiply2() throws Exception { + return MatrixUtil.concurrentMultiply2(matrixA, matrixB, executor); + } + + @Setup + public void setup() { + executor = Executors.newFixedThreadPool(threadNumber); + } + + @TearDown + public void tearDown() { + executor.shutdown(); + } +} \ No newline at end of file From 1e179b47496dbd81fe4eb7d182a0f137e2067851 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 20 Jan 2019 08:28:22 +0300 Subject: [PATCH 10/87] 2 05 JMH main jar --- pom.xml | 35 +++++++++++++++++++ .../masterjava/matrix/MatrixBenchmark.java | 15 ++++++++ 2 files changed, 50 insertions(+) diff --git a/pom.xml b/pom.xml index 9f0832e4e..4cadaf85e 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,41 @@ ${java.version} + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + benchmarks + + + org.openjdk.jmh.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java b/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java index 65fc52878..2df2fc674 100644 --- a/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java +++ b/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java @@ -1,6 +1,11 @@ package ru.javaops.masterjava.matrix; import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -33,6 +38,16 @@ public void setUp() { private ExecutorService executor; + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(MatrixBenchmark.class.getSimpleName()) + .threads(1) + .forks(10) + .timeout(TimeValue.minutes(5)) + .build(); + new Runner(options).run(); + } + // @Benchmark public int[][] singleThreadMultiplyOpt() throws Exception { return MatrixUtil.singleThreadMultiplyOpt(matrixA, matrixB); From c4bbbe58a1a957f0d3dbd01a97869c6a52ced86f Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 20 Jan 2019 08:31:54 +0300 Subject: [PATCH 11/87] 2 06 xml scheme --- .../masterjava/xml/schema/CityType.java | 94 +++++++ .../masterjava/xml/schema/FlagType.java | 54 ++++ .../masterjava/xml/schema/ObjectFactory.java | 85 +++++++ .../masterjava/xml/schema/Payload.java | 233 ++++++++++++++++++ .../javaops/masterjava/xml/schema/User.java | 151 ++++++++++++ src/main/resources/payload.xsd | 56 +++++ src/test/resources/payload.xml | 23 ++ 7 files changed, 696 insertions(+) create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/CityType.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/Payload.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/User.java create mode 100644 src/main/resources/payload.xsd create mode 100644 src/test/resources/payload.xml diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java b/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java new file mode 100644 index 000000000..029e352cb --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java @@ -0,0 +1,94 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlID; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + + +/** + *

Java class for cityType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="cityType">
+ *   <simpleContent>
+ *     <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ *       <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}ID" />
+ *     </extension>
+ *   </simpleContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "cityType", namespace = "http://javaops.ru", propOrder = { + "value" +}) +public class CityType { + + @XmlValue + protected String value; + @XmlAttribute(name = "id", required = true) + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + @XmlID + @XmlSchemaType(name = "ID") + protected String id; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setId(String value) { + this.id = value; + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java b/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java new file mode 100644 index 000000000..eda39fa9a --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java @@ -0,0 +1,54 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlEnumValue; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for flagType. + * + *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+ * <simpleType name="flagType">
+ *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ *     <enumeration value="active"/>
+ *     <enumeration value="deleted"/>
+ *     <enumeration value="superuser"/>
+ *   </restriction>
+ * </simpleType>
+ * 
+ * + */ +@XmlType(name = "flagType", namespace = "http://javaops.ru") +@XmlEnum +public enum FlagType { + + @XmlEnumValue("active") + ACTIVE("active"), + @XmlEnumValue("deleted") + DELETED("deleted"), + @XmlEnumValue("superuser") + SUPERUSER("superuser"); + private final String value; + + FlagType(String v) { + value = v; + } + + public String value() { + return value; + } + + public static FlagType fromValue(String v) { + for (FlagType c: FlagType.values()) { + if (c.value.equals(v)) { + return c; + } + } + throw new IllegalArgumentException(v); + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java b/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java new file mode 100644 index 000000000..e8f105e2a --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java @@ -0,0 +1,85 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the ru.javaops.masterjava.xml.schema package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _City_QNAME = new QName("http://javaops.ru", "City"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: ru.javaops.masterjava.xml.schema + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Payload } + * + */ + public Payload createPayload() { + return new Payload(); + } + + /** + * Create an instance of {@link User } + * + */ + public User createUser() { + return new User(); + } + + /** + * Create an instance of {@link Payload.Cities } + * + */ + public Payload.Cities createPayloadCities() { + return new Payload.Cities(); + } + + /** + * Create an instance of {@link Payload.Users } + * + */ + public Payload.Users createPayloadUsers() { + return new Payload.Users(); + } + + /** + * Create an instance of {@link CityType } + * + */ + public CityType createCityType() { + return new CityType(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link CityType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://javaops.ru", name = "City") + public JAXBElement createCity(CityType value) { + return new JAXBElement(_City_QNAME, CityType.class, null, value); + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java b/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java new file mode 100644 index 000000000..2a6276490 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java @@ -0,0 +1,233 @@ + +package ru.javaops.masterjava.xml.schema; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <all>
+ *         <element name="Cities">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence maxOccurs="unbounded">
+ *                   <element ref="{http://javaops.ru}City"/>
+ *                 </sequence>
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="Users">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence maxOccurs="unbounded" minOccurs="0">
+ *                   <element ref="{http://javaops.ru}User"/>
+ *                 </sequence>
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *       </all>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + +}) +@XmlRootElement(name = "Payload", namespace = "http://javaops.ru") +public class Payload { + + @XmlElement(name = "Cities", namespace = "http://javaops.ru", required = true) + protected Payload.Cities cities; + @XmlElement(name = "Users", namespace = "http://javaops.ru", required = true) + protected Payload.Users users; + + /** + * Gets the value of the cities property. + * + * @return + * possible object is + * {@link Payload.Cities } + * + */ + public Payload.Cities getCities() { + return cities; + } + + /** + * Sets the value of the cities property. + * + * @param value + * allowed object is + * {@link Payload.Cities } + * + */ + public void setCities(Payload.Cities value) { + this.cities = value; + } + + /** + * Gets the value of the users property. + * + * @return + * possible object is + * {@link Payload.Users } + * + */ + public Payload.Users getUsers() { + return users; + } + + /** + * Sets the value of the users property. + * + * @param value + * allowed object is + * {@link Payload.Users } + * + */ + public void setUsers(Payload.Users value) { + this.users = value; + } + + + /** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence maxOccurs="unbounded">
+     *         <element ref="{http://javaops.ru}City"/>
+     *       </sequence>
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * 
+ * + * + */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "", propOrder = { + "city" + }) + public static class Cities { + + @XmlElement(name = "City", namespace = "http://javaops.ru", required = true) + protected List city; + + /** + * Gets the value of the city property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the city property. + * + *

+ * For example, to add a new item, do as follows: + *

+         *    getCity().add(newItem);
+         * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link CityType } + * + * + */ + public List getCity() { + if (city == null) { + city = new ArrayList(); + } + return this.city; + } + + } + + + /** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence maxOccurs="unbounded" minOccurs="0">
+     *         <element ref="{http://javaops.ru}User"/>
+     *       </sequence>
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * 
+ * + * + */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "", propOrder = { + "user" + }) + public static class Users { + + @XmlElement(name = "User", namespace = "http://javaops.ru") + protected List user; + + /** + * Gets the value of the user property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the user property. + * + *

+ * For example, to add a new item, do as follows: + *

+         *    getUser().add(newItem);
+         * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link User } + * + * + */ + public List getUser() { + if (user == null) { + user = new ArrayList(); + } + return this.user; + } + + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/User.java b/src/main/java/ru/javaops/masterjava/xml/schema/User.java new file mode 100644 index 000000000..b3430ce71 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/User.java @@ -0,0 +1,151 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlIDREF; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="email" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         <element name="fullName" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       </sequence>
+ *       <attribute name="flag" use="required" type="{http://javaops.ru}flagType" />
+ *       <attribute name="city" use="required" type="{http://www.w3.org/2001/XMLSchema}IDREF" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "email", + "fullName" +}) +@XmlRootElement(name = "User", namespace = "http://javaops.ru") +public class User { + + @XmlElement(namespace = "http://javaops.ru", required = true) + protected String email; + @XmlElement(namespace = "http://javaops.ru", required = true) + protected String fullName; + @XmlAttribute(name = "flag", required = true) + protected FlagType flag; + @XmlAttribute(name = "city", required = true) + @XmlIDREF + @XmlSchemaType(name = "IDREF") + protected Object city; + + /** + * Gets the value of the email property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getEmail() { + return email; + } + + /** + * Sets the value of the email property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setEmail(String value) { + this.email = value; + } + + /** + * Gets the value of the fullName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getFullName() { + return fullName; + } + + /** + * Sets the value of the fullName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setFullName(String value) { + this.fullName = value; + } + + /** + * Gets the value of the flag property. + * + * @return + * possible object is + * {@link FlagType } + * + */ + public FlagType getFlag() { + return flag; + } + + /** + * Sets the value of the flag property. + * + * @param value + * allowed object is + * {@link FlagType } + * + */ + public void setFlag(FlagType value) { + this.flag = value; + } + + /** + * Gets the value of the city property. + * + * @return + * possible object is + * {@link Object } + * + */ + public Object getCity() { + return city; + } + + /** + * Sets the value of the city property. + * + * @param value + * allowed object is + * {@link Object } + * + */ + public void setCity(Object value) { + this.city = value; + } + +} diff --git a/src/main/resources/payload.xsd b/src/main/resources/payload.xsd new file mode 100644 index 000000000..9ef1e46eb --- /dev/null +++ b/src/main/resources/payload.xsd @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/payload.xml b/src/test/resources/payload.xml new file mode 100644 index 000000000..796e99cb3 --- /dev/null +++ b/src/test/resources/payload.xml @@ -0,0 +1,23 @@ + + + + gmail@gmail.com + Full Name + + + admin@javaops.ru + Admin + + + mail@yandex.ru + Deleted + + + + Санкт-Петербург + Киев + Минск + + \ No newline at end of file From 902c3a618af0bcef0dcc25cc75226524d594ba94 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 20 Jan 2019 08:44:06 +0300 Subject: [PATCH 12/87] 2 07 JAXB --- pom.xml | 19 ++++ .../masterjava/xml/util/JaxbMarshaller.java | 39 ++++++++ .../masterjava/xml/util/JaxbParser.java | 88 +++++++++++++++++++ .../masterjava/xml/util/JaxbUnmarshaller.java | 33 +++++++ .../javaops/masterjava/xml/util/Schemas.java | 48 ++++++++++ .../masterjava/xml/util/JaxbParserTest.java | 40 +++++++++ src/test/resources/city.xml | 4 + 7 files changed, 271 insertions(+) create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/Schemas.java create mode 100644 src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java create mode 100644 src/test/resources/city.xml diff --git a/pom.xml b/pom.xml index 4cadaf85e..884be4839 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,14 @@ ${java.version} + + org.apache.maven.plugins + maven-surefire-plugin + 2.20.1 + + -Dfile.encoding=UTF-8 + + org.apache.maven.plugins maven-shade-plugin @@ -80,6 +88,17 @@ RELEASE provided + + com.google.guava + guava + 21.0 + + + junit + junit + 4.12 + test + diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java b/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java new file mode 100644 index 000000000..d6006800f --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java @@ -0,0 +1,39 @@ +package ru.javaops.masterjava.xml.util; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.PropertyException; +import javax.xml.validation.Schema; +import java.io.StringWriter; +import java.io.Writer; + +public class JaxbMarshaller { + private Marshaller marshaller; + + public JaxbMarshaller(JAXBContext ctx) throws JAXBException { + marshaller = ctx.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + } + + public void setProperty(String prop, Object value) throws PropertyException { + marshaller.setProperty(prop, value); + } + + public synchronized void setSchema(Schema schema) { + marshaller.setSchema(schema); + } + + public String marshal(Object instance) throws JAXBException { + StringWriter sw = new StringWriter(); + marshal(instance, sw); + return sw.toString(); + } + + public synchronized void marshal(Object instance, Writer writer) throws JAXBException { + marshaller.marshal(instance, writer); + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java b/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java new file mode 100644 index 000000000..b3a45f66c --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java @@ -0,0 +1,88 @@ +package ru.javaops.masterjava.xml.util; + +import org.xml.sax.SAXException; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.PropertyException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import java.io.*; + + +/** + * Marshalling/Unmarshalling JAXB helper + * XML Facade + */ +public class JaxbParser { + + protected JaxbMarshaller jaxbMarshaller; + protected JaxbUnmarshaller jaxbUnmarshaller; + protected Schema schema; + + public JaxbParser(Class... classesToBeBound) { + try { + init(JAXBContext.newInstance(classesToBeBound)); + } catch (JAXBException e) { + throw new IllegalArgumentException(e); + } + } + + // http://stackoverflow.com/questions/30643802/what-is-jaxbcontext-newinstancestring-contextpath + public JaxbParser(String context) { + try { + init(JAXBContext.newInstance(context)); + } catch (JAXBException e) { + throw new IllegalArgumentException(e); + } + } + + private void init(JAXBContext ctx) throws JAXBException { + jaxbMarshaller = new JaxbMarshaller(ctx); + jaxbUnmarshaller = new JaxbUnmarshaller(ctx); + } + + // Unmarshaller + public T unmarshal(InputStream is) throws JAXBException { + return (T) jaxbUnmarshaller.unmarshal(is); + } + + public T unmarshal(Reader reader) throws JAXBException { + return (T) jaxbUnmarshaller.unmarshal(reader); + } + + public T unmarshal(String str) throws JAXBException { + return (T) jaxbUnmarshaller.unmarshal(str); + } + + // Marshaller + public void setMarshallerProperty(String prop, Object value) { + try { + jaxbMarshaller.setProperty(prop, value); + } catch (PropertyException e) { + throw new IllegalArgumentException(e); + } + } + + public String marshal(Object instance) throws JAXBException { + return jaxbMarshaller.marshal(instance); + } + + public void marshal(Object instance, Writer writer) throws JAXBException { + jaxbMarshaller.marshal(instance, writer); + } + + public void setSchema(Schema schema) { + this.schema = schema; + jaxbUnmarshaller.setSchema(schema); + jaxbMarshaller.setSchema(schema); + } + + public void validate(String str) throws IOException, SAXException { + validate(new StringReader(str)); + } + + public void validate(Reader reader) throws IOException, SAXException { + schema.newValidator().validate(new StreamSource(reader)); + } +} diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java b/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java new file mode 100644 index 000000000..7a3e13461 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java @@ -0,0 +1,33 @@ +package ru.javaops.masterjava.xml.util; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.validation.Schema; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; + +public class JaxbUnmarshaller { + private Unmarshaller unmarshaller; + + public JaxbUnmarshaller(JAXBContext ctx) throws JAXBException { + unmarshaller = ctx.createUnmarshaller(); + } + + public synchronized void setSchema(Schema schema) { + unmarshaller.setSchema(schema); + } + + public synchronized Object unmarshal(InputStream is) throws JAXBException { + return unmarshaller.unmarshal(is); + } + + public synchronized Object unmarshal(Reader reader) throws JAXBException { + return unmarshaller.unmarshal(reader); + } + + public Object unmarshal(String str) throws JAXBException { + return unmarshal(new StringReader(str)); + } +} diff --git a/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java b/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java new file mode 100644 index 000000000..42f41df80 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java @@ -0,0 +1,48 @@ +package ru.javaops.masterjava.xml.util; + +import com.google.common.io.Resources; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.File; +import java.io.StringReader; +import java.net.URL; + + +public class Schemas { + + // SchemaFactory is not thread-safe + private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + + public static synchronized Schema ofString(String xsd) { + try { + return SCHEMA_FACTORY.newSchema(new StreamSource(new StringReader(xsd))); + } catch (SAXException e) { + throw new IllegalArgumentException(e); + } + } + + public static synchronized Schema ofClasspath(String resource) { + // http://digitalsanctum.com/2012/11/30/how-to-read-file-contents-in-java-the-easy-way-with-guava/ + return ofURL(Resources.getResource(resource)); + } + + public static synchronized Schema ofURL(URL url) { + try { + return SCHEMA_FACTORY.newSchema(url); + } catch (SAXException e) { + throw new IllegalArgumentException(e); + } + } + + public static synchronized Schema ofFile(File file) { + try { + return SCHEMA_FACTORY.newSchema(file); + } catch (SAXException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java b/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java new file mode 100644 index 000000000..623265428 --- /dev/null +++ b/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java @@ -0,0 +1,40 @@ +package ru.javaops.masterjava.xml.util; + +import com.google.common.io.Resources; +import org.junit.Test; +import ru.javaops.masterjava.xml.schema.CityType; +import ru.javaops.masterjava.xml.schema.ObjectFactory; +import ru.javaops.masterjava.xml.schema.Payload; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +public class JaxbParserTest { + private static final JaxbParser JAXB_PARSER = new JaxbParser(ObjectFactory.class); + + static { + JAXB_PARSER.setSchema(Schemas.ofClasspath("payload.xsd")); + } + + @Test + public void testPayload() throws Exception { +// JaxbParserTest.class.getResourceAsStream("/city.xml") + Payload payload = JAXB_PARSER.unmarshal( + Resources.getResource("payload.xml").openStream()); + String strPayload = JAXB_PARSER.marshal(payload); + JAXB_PARSER.validate(strPayload); + System.out.println(strPayload); + } + + @Test + public void testCity() throws Exception { + JAXBElement cityElement = JAXB_PARSER.unmarshal( + Resources.getResource("city.xml").openStream()); + CityType city = cityElement.getValue(); + JAXBElement cityElement2 = + new JAXBElement<>(new QName("http://javaops.ru", "City"), CityType.class, city); + String strCity = JAXB_PARSER.marshal(cityElement2); + JAXB_PARSER.validate(strCity); + System.out.println(strCity); + } +} \ No newline at end of file diff --git a/src/test/resources/city.xml b/src/test/resources/city.xml new file mode 100644 index 000000000..8b0abcf8a --- /dev/null +++ b/src/test/resources/city.xml @@ -0,0 +1,4 @@ +Санкт-Петербург + \ No newline at end of file From 9421c72410ee7d5b67c1aa23f18369c6a62a4260 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Fri, 1 Feb 2019 05:38:35 +0300 Subject: [PATCH 13/87] 2 08 StAX --- .../xml/util/StaxStreamProcessor.java | 56 +++++++++++++++++++ .../xml/util/StaxStreamProcessorTest.java | 36 ++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java create mode 100644 src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java new file mode 100644 index 000000000..921ca6aff --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java @@ -0,0 +1,56 @@ +package ru.javaops.masterjava.xml.util; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.events.XMLEvent; +import java.io.InputStream; + +public class StaxStreamProcessor implements AutoCloseable { + private static final XMLInputFactory FACTORY = XMLInputFactory.newInstance(); + + private final XMLStreamReader reader; + + public StaxStreamProcessor(InputStream is) throws XMLStreamException { + reader = FACTORY.createXMLStreamReader(is); + } + + public XMLStreamReader getReader() { + return reader; + } + + public boolean doUntil(int stopEvent, String value) throws XMLStreamException { + while (reader.hasNext()) { + int event = reader.next(); + if (event == stopEvent) { + if (value.equals(getValue(event))) { + return true; + } + } + } + return false; + } + + public String getValue(int event) throws XMLStreamException { + return (event == XMLEvent.CHARACTERS) ? reader.getText() : reader.getLocalName(); + } + + public String getElementValue(String element) throws XMLStreamException { + return doUntil(XMLEvent.START_ELEMENT, element) ? reader.getElementText() : null; + } + + public String getText() throws XMLStreamException { + return reader.getElementText(); + } + + @Override + public void close() { + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + // empty + } + } + } +} diff --git a/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java b/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java new file mode 100644 index 000000000..fd55963dd --- /dev/null +++ b/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.xml.util; + +import com.google.common.io.Resources; +import org.junit.Test; + +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.events.XMLEvent; + +public class StaxStreamProcessorTest { + @Test + public void readCities() throws Exception { + try (StaxStreamProcessor processor = + new StaxStreamProcessor(Resources.getResource("payload.xml").openStream())) { + XMLStreamReader reader = processor.getReader(); + while (reader.hasNext()) { + int event = reader.next(); + if (event == XMLEvent.START_ELEMENT) { + if ("City".equals(reader.getLocalName())) { + System.out.println(reader.getElementText()); + } + } + } + } + } + + @Test + public void readCities2() throws Exception { + try (StaxStreamProcessor processor = + new StaxStreamProcessor(Resources.getResource("payload.xml").openStream())) { + String city; + while ((city = processor.getElementValue("City")) != null) { + System.out.println(city); + } + } + } +} \ No newline at end of file From 3e44d144569ce235642a9dc718416bfcd288c812 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Fri, 1 Feb 2019 05:39:23 +0300 Subject: [PATCH 14/87] 2 09 XPath --- .../masterjava/xml/util/XPathProcessor.java | 58 +++++++++++++++++++ .../xml/util/XPathProcessorTest.java | 26 +++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java create mode 100644 src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java new file mode 100644 index 000000000..63baae5d1 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java @@ -0,0 +1,58 @@ +package ru.javaops.masterjava.xml.util; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.InputStream; + +public class XPathProcessor { + private static final DocumentBuilderFactory DOCUMENT_FACTORY = DocumentBuilderFactory.newInstance(); + private static final DocumentBuilder DOCUMENT_BUILDER; + + private static final XPathFactory XPATH_FACTORY = XPathFactory.newInstance(); + private static final XPath XPATH = XPATH_FACTORY.newXPath(); + + static { + DOCUMENT_FACTORY.setNamespaceAware(true); + try { + DOCUMENT_BUILDER = DOCUMENT_FACTORY.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException(e); + } + } + + private final Document doc; + + public XPathProcessor(InputStream is) { + try { + doc = DOCUMENT_BUILDER.parse(is); + } catch (SAXException | IOException e) { + throw new IllegalArgumentException(e); + } + } + + public static synchronized XPathExpression getExpression(String exp) { + try { + return XPATH.compile(exp); + } catch (XPathExpressionException e) { + throw new IllegalArgumentException(e); + } + } + + public T evaluate(XPathExpression expression, QName type) { + try { + return (T) expression.evaluate(doc, type); + } catch (XPathExpressionException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java b/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java new file mode 100644 index 000000000..199f676a1 --- /dev/null +++ b/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java @@ -0,0 +1,26 @@ +package ru.javaops.masterjava.xml.util; + +import com.google.common.io.Resources; +import org.junit.Test; +import org.w3c.dom.NodeList; + +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import java.io.InputStream; +import java.util.stream.IntStream; + +public class XPathProcessorTest { + @Test + public void getCities() throws Exception { + try (InputStream is = + Resources.getResource("payload.xml").openStream()) { + XPathProcessor processor = new XPathProcessor(is); + XPathExpression expression = + XPathProcessor.getExpression("/*[name()='Payload']/*[name()='Cities']/*[name()='City']/text()"); + NodeList nodes = processor.evaluate(expression, XPathConstants.NODESET); + IntStream.range(0, nodes.getLength()).forEach( + i -> System.out.println(nodes.item(i).getNodeValue()) + ); + } + } +} \ No newline at end of file From 9c330dd3f111ff89eb947774ad91222364877600 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Fri, 1 Feb 2019 05:40:13 +0300 Subject: [PATCH 15/87] 2 10 Xslt --- .../masterjava/xml/util/XsltProcessor.java | 43 +++++++++++++++++++ src/main/resources/cities.xsl | 9 ++++ .../xml/util/XsltProcessorTest.java | 18 ++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java create mode 100644 src/main/resources/cities.xsl create mode 100644 src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java new file mode 100644 index 000000000..019eeed3d --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java @@ -0,0 +1,43 @@ +package ru.javaops.masterjava.xml.util; + +import javax.xml.transform.*; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.*; +import java.nio.charset.StandardCharsets; + +public class XsltProcessor { + private static TransformerFactory FACTORY = TransformerFactory.newInstance(); + private final Transformer xformer; + + public XsltProcessor(InputStream xslInputStream) { + this(new BufferedReader(new InputStreamReader(xslInputStream, StandardCharsets.UTF_8))); + } + + public XsltProcessor(Reader xslReader) { + try { + Templates template = FACTORY.newTemplates(new StreamSource(xslReader)); + xformer = template.newTransformer(); + } catch (TransformerConfigurationException e) { + throw new IllegalStateException("XSLT transformer creation failed: " + e.toString(), e); + } + } + + public String transform(InputStream xmlInputStream) throws TransformerException { + StringWriter out = new StringWriter(); + transform(xmlInputStream, out); + return out.getBuffer().toString(); + } + + public void transform(InputStream xmlInputStream, Writer result) throws TransformerException { + transform(new BufferedReader(new InputStreamReader(xmlInputStream, StandardCharsets.UTF_8)), result); + } + + public void transform(Reader sourceReader, Writer result) throws TransformerException { + xformer.transform(new StreamSource(sourceReader), new StreamResult(result)); + } + + public static String getXsltHeader(String xslt) { + return "\n"; + } +} diff --git a/src/main/resources/cities.xsl b/src/main/resources/cities.xsl new file mode 100644 index 000000000..1c509124b --- /dev/null +++ b/src/main/resources/cities.xsl @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java b/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java new file mode 100644 index 000000000..d7f42a699 --- /dev/null +++ b/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java @@ -0,0 +1,18 @@ +package ru.javaops.masterjava.xml.util; + +import com.google.common.io.Resources; +import org.junit.Test; + +import java.io.InputStream; + +public class XsltProcessorTest { + @Test + public void transform() throws Exception { + try (InputStream xslInputStream = Resources.getResource("cities.xsl").openStream(); + InputStream xmlInputStream = Resources.getResource("payload.xml").openStream()) { + + XsltProcessor processor = new XsltProcessor(xslInputStream); + System.out.println(processor.transform(xmlInputStream)); + } + } +} From ce286122266e414350b6504728f8c9f414651a07 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 18:49:15 +0300 Subject: [PATCH 16/87] 3 1 HW2 schema --- .../masterjava/xml/schema/GroupType.java | 40 ++++ .../masterjava/xml/schema/ObjectFactory.java | 24 ++ .../masterjava/xml/schema/Payload.java | 111 ++++++++- .../masterjava/xml/schema/Project.java | 216 ++++++++++++++++++ .../javaops/masterjava/xml/schema/User.java | 95 +++++--- src/main/resources/payload.xsd | 57 ++++- src/test/resources/payload.xml | 36 +-- 7 files changed, 516 insertions(+), 63 deletions(-) create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java create mode 100644 src/main/java/ru/javaops/masterjava/xml/schema/Project.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java b/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java new file mode 100644 index 000000000..d5041640b --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java @@ -0,0 +1,40 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for groupType. + * + *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+ * <simpleType name="groupType">
+ *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ *     <enumeration value="REGISTERING"/>
+ *     <enumeration value="CURRENT"/>
+ *     <enumeration value="FINISHED"/>
+ *   </restriction>
+ * </simpleType>
+ * 
+ * + */ +@XmlType(name = "groupType", namespace = "http://javaops.ru") +@XmlEnum +public enum GroupType { + + REGISTERING, + CURRENT, + FINISHED; + + public String value() { + return name(); + } + + public static GroupType fromValue(String v) { + return valueOf(v); + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java b/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java index e8f105e2a..bfb393299 100644 --- a/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java +++ b/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java @@ -33,6 +33,14 @@ public class ObjectFactory { public ObjectFactory() { } + /** + * Create an instance of {@link Project } + * + */ + public Project createProject() { + return new Project(); + } + /** * Create an instance of {@link Payload } * @@ -41,6 +49,14 @@ public Payload createPayload() { return new Payload(); } + /** + * Create an instance of {@link Project.Group } + * + */ + public Project.Group createProjectGroup() { + return new Project.Group(); + } + /** * Create an instance of {@link User } * @@ -49,6 +65,14 @@ public User createUser() { return new User(); } + /** + * Create an instance of {@link Payload.Projects } + * + */ + public Payload.Projects createPayloadProjects() { + return new Payload.Projects(); + } + /** * Create an instance of {@link Payload.Cities } * diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java b/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java index 2a6276490..9d4cc3046 100644 --- a/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java +++ b/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java @@ -1,13 +1,9 @@ package ru.javaops.masterjava.xml.schema; +import javax.xml.bind.annotation.*; import java.util.ArrayList; import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; /** @@ -19,7 +15,18 @@ * <complexType> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> - * <all> + * <sequence> + * <element name="Projects"> + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence maxOccurs="unbounded"> + * <element ref="{http://javaops.ru}Project"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * </element> * <element name="Cities"> * <complexType> * <complexContent> @@ -42,7 +49,7 @@ * </complexContent> * </complexType> * </element> - * </all> + * </sequence> * </restriction> * </complexContent> * </complexType> @@ -52,16 +59,44 @@ */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { - + "projects", + "cities", + "users" }) @XmlRootElement(name = "Payload", namespace = "http://javaops.ru") public class Payload { + @XmlElement(name = "Projects", namespace = "http://javaops.ru", required = true) + protected Payload.Projects projects; @XmlElement(name = "Cities", namespace = "http://javaops.ru", required = true) protected Payload.Cities cities; @XmlElement(name = "Users", namespace = "http://javaops.ru", required = true) protected Payload.Users users; + /** + * Gets the value of the projects property. + * + * @return + * possible object is + * {@link Payload.Projects } + * + */ + public Payload.Projects getProjects() { + return projects; + } + + /** + * Sets the value of the projects property. + * + * @param value + * allowed object is + * {@link Payload.Projects } + * + */ + public void setProjects(Payload.Projects value) { + this.projects = value; + } + /** * Gets the value of the cities property. * @@ -171,6 +206,66 @@ public List getCity() { } + /** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence maxOccurs="unbounded">
+     *         <element ref="{http://javaops.ru}Project"/>
+     *       </sequence>
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * 
+ * + * + */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "", propOrder = { + "project" + }) + public static class Projects { + + @XmlElement(name = "Project", namespace = "http://javaops.ru", required = true) + protected List project; + + /** + * Gets the value of the project property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the project property. + * + *

+ * For example, to add a new item, do as follows: + *

+         *    getProject().add(newItem);
+         * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Project } + * + * + */ + public List getProject() { + if (project == null) { + project = new ArrayList(); + } + return this.project; + } + + } + + /** *

Java class for anonymous complex type. * diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/Project.java b/src/main/java/ru/javaops/masterjava/xml/schema/Project.java new file mode 100644 index 000000000..180a997e9 --- /dev/null +++ b/src/main/java/ru/javaops/masterjava/xml/schema/Project.java @@ -0,0 +1,216 @@ + +package ru.javaops.masterjava.xml.schema; + +import javax.xml.bind.annotation.*; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.ArrayList; +import java.util.List; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="description" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         <sequence maxOccurs="unbounded">
+ *           <element name="Group">
+ *             <complexType>
+ *               <complexContent>
+ *                 <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}ID" />
+ *                   <attribute name="type" use="required" type="{http://javaops.ru}groupType" />
+ *                 </restriction>
+ *               </complexContent>
+ *             </complexType>
+ *           </element>
+ *         </sequence>
+ *       </sequence>
+ *       <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "description", + "group" +}) +@XmlRootElement(name = "Project", namespace = "http://javaops.ru") +public class Project { + + @XmlElement(namespace = "http://javaops.ru", required = true) + protected String description; + @XmlElement(name = "Group", namespace = "http://javaops.ru", required = true) + protected List group; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the description property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDescription() { + return description; + } + + /** + * Sets the value of the description property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDescription(String value) { + this.description = value; + } + + /** + * Gets the value of the group property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the group property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getGroup().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Project.Group } + * + * + */ + public List getGroup() { + if (group == null) { + group = new ArrayList(); + } + return this.group; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + + /** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}ID" />
+     *       <attribute name="type" use="required" type="{http://javaops.ru}groupType" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * 
+ * + * + */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "") + public static class Group { + + @XmlAttribute(name = "name", required = true) + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + @XmlID + @XmlSchemaType(name = "ID") + protected String name; + @XmlAttribute(name = "type", required = true) + protected GroupType type; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link GroupType } + * + */ + public GroupType getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link GroupType } + * + */ + public void setType(GroupType value) { + this.type = value; + } + + } + +} diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/User.java b/src/main/java/ru/javaops/masterjava/xml/schema/User.java index b3430ce71..b2cc1c449 100644 --- a/src/main/java/ru/javaops/masterjava/xml/schema/User.java +++ b/src/main/java/ru/javaops/masterjava/xml/schema/User.java @@ -1,14 +1,9 @@ package ru.javaops.masterjava.xml.schema; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlIDREF; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; /** @@ -18,16 +13,14 @@ * *
  * <complexType>
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="email" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         <element name="fullName" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *       </sequence>
+ *   <simpleContent>
+ *     <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ *       <attribute name="email" type="{http://javaops.ru}emailAddressType" />
  *       <attribute name="flag" use="required" type="{http://javaops.ru}flagType" />
  *       <attribute name="city" use="required" type="{http://www.w3.org/2001/XMLSchema}IDREF" />
- *     </restriction>
- *   </complexContent>
+ *       <attribute name="groupRefs" type="{http://www.w3.org/2001/XMLSchema}IDREFS" />
+ *     </extension>
+ *   </simpleContent>
  * </complexType>
  * 
* @@ -35,69 +28,72 @@ */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { - "email", - "fullName" + "value" }) @XmlRootElement(name = "User", namespace = "http://javaops.ru") public class User { - @XmlElement(namespace = "http://javaops.ru", required = true) + @XmlValue + protected String value; + @XmlAttribute(name = "email") protected String email; - @XmlElement(namespace = "http://javaops.ru", required = true) - protected String fullName; @XmlAttribute(name = "flag", required = true) protected FlagType flag; @XmlAttribute(name = "city", required = true) @XmlIDREF @XmlSchemaType(name = "IDREF") protected Object city; + @XmlAttribute(name = "groupRefs") + @XmlIDREF + @XmlSchemaType(name = "IDREFS") + protected List groupRefs; /** - * Gets the value of the email property. + * Gets the value of the value property. * * @return * possible object is * {@link String } * */ - public String getEmail() { - return email; + public String getValue() { + return value; } /** - * Sets the value of the email property. + * Sets the value of the value property. * * @param value * allowed object is * {@link String } * */ - public void setEmail(String value) { - this.email = value; + public void setValue(String value) { + this.value = value; } /** - * Gets the value of the fullName property. + * Gets the value of the email property. * * @return * possible object is * {@link String } * */ - public String getFullName() { - return fullName; + public String getEmail() { + return email; } /** - * Sets the value of the fullName property. + * Sets the value of the email property. * * @param value * allowed object is * {@link String } * */ - public void setFullName(String value) { - this.fullName = value; + public void setEmail(String value) { + this.email = value; } /** @@ -148,4 +144,37 @@ public void setCity(Object value) { this.city = value; } + /** + * Gets the value of the groupRefs property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the groupRefs property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getGroupRefs().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Object } + * + * + */ + public List getGroupRefs() { + if (groupRefs == null) { + groupRefs = new ArrayList(); + } + return this.groupRefs; + } + + @Override + public String toString() { + return value + '(' + email + ')'; + } } diff --git a/src/main/resources/payload.xsd b/src/main/resources/payload.xsd index 9ef1e46eb..3d545ec54 100644 --- a/src/main/resources/payload.xsd +++ b/src/main/resources/payload.xsd @@ -6,7 +6,14 @@ - + + + + + + + + @@ -21,18 +28,39 @@ - + - + - - + + + + + + + + + - - + + + + + + + + + + + + + + + + @@ -44,7 +72,13 @@ - + + + + + + + @@ -53,4 +87,11 @@ + + + + + + + \ No newline at end of file diff --git a/src/test/resources/payload.xml b/src/test/resources/payload.xml index 796e99cb3..fadf8c64a 100644 --- a/src/test/resources/payload.xml +++ b/src/test/resources/payload.xml @@ -1,23 +1,31 @@ - - - gmail@gmail.com - Full Name - - - admin@javaops.ru - Admin - - - mail@yandex.ru - Deleted - - + + + + Topjava + + + + + + Masterjava + + + Санкт-Петербург + Москва Киев Минск + + Full Name + Admin + Deleted + User1 + User2 + User3 + \ No newline at end of file From 93ae52c61b5060c3551e99a32c431c6a95a78e94 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 19:05:59 +0300 Subject: [PATCH 17/87] 3 2 HW2 JAXB HTML --- pom.xml | 13 ++++ src/main/java/ru/javaops/masterjava/Main.java | 14 ---- .../java/ru/javaops/masterjava/MainXml.java | 78 +++++++++++++++++++ 3 files changed, 91 insertions(+), 14 deletions(-) delete mode 100644 src/main/java/ru/javaops/masterjava/Main.java create mode 100644 src/test/java/ru/javaops/masterjava/MainXml.java diff --git a/pom.xml b/pom.xml index 884be4839..880943475 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,19 @@ guava 21.0 + + one.util + streamex + RELEASE + + + + com.j2html + j2html + RELEASE + + + junit junit diff --git a/src/main/java/ru/javaops/masterjava/Main.java b/src/main/java/ru/javaops/masterjava/Main.java deleted file mode 100644 index a849258c4..000000000 --- a/src/main/java/ru/javaops/masterjava/Main.java +++ /dev/null @@ -1,14 +0,0 @@ -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/test/java/ru/javaops/masterjava/MainXml.java b/src/test/java/ru/javaops/masterjava/MainXml.java new file mode 100644 index 000000000..e60f2a4f2 --- /dev/null +++ b/src/test/java/ru/javaops/masterjava/MainXml.java @@ -0,0 +1,78 @@ +package ru.javaops.masterjava; + +import com.google.common.io.Resources; +import j2html.tags.ContainerTag; +import one.util.streamex.StreamEx; +import ru.javaops.masterjava.xml.schema.ObjectFactory; +import ru.javaops.masterjava.xml.schema.Payload; +import ru.javaops.masterjava.xml.schema.Project; +import ru.javaops.masterjava.xml.schema.User; +import ru.javaops.masterjava.xml.util.JaxbParser; +import ru.javaops.masterjava.xml.util.Schemas; + +import java.io.InputStream; +import java.io.Writer; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +import static j2html.TagCreator.*; + +public class MainXml { + + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.out.println("Format: projectName"); + System.exit(1); + } + String projectName = args[0]; + URL payloadUrl = Resources.getResource("payload.xml"); + + Set users = parseByJaxb(projectName, payloadUrl); + users.forEach(System.out::println); + + String html = toHtml(users, projectName); + System.out.println(html); + try (Writer writer = Files.newBufferedWriter(Paths.get("out/users.html"))) { + writer.write(html); + } + } + + private static Set parseByJaxb(String projectName, URL payloadUrl) throws Exception { + JaxbParser parser = new JaxbParser(ObjectFactory.class); + parser.setSchema(Schemas.ofClasspath("payload.xsd")); + Payload payload; + try (InputStream is = payloadUrl.openStream()) { + payload = parser.unmarshal(is); + } + + Project project = StreamEx.of(payload.getProjects().getProject()) + .filter(p -> p.getName().equals(projectName)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Invalid project name '" + projectName + '\'')); + + final Set groups = new HashSet<>(project.getGroup()); // identity compare + return StreamEx.of(payload.getUsers().getUser()) + .filter(u -> !Collections.disjoint(groups, u.getGroupRefs())) + .collect( + Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getValue).thenComparing(User::getEmail))) + ); + } + + private static String toHtml(Set users, String projectName) { + final ContainerTag table = table().with( + tr().with(th("FullName"), th("email"))) + .attr("border", "1") + .attr("cellpadding", "8") + .attr("cellspacing", "0"); + + users.forEach(u -> table.with(tr().with(td(u.getValue()), td(u.getEmail())))); + + return html().with( + head().with(title(projectName + " users")), + body().with(h1(projectName + " users"), table) + ).render(); + } +} From d25d412feda0b00e0ba9b60f8a9a116266004529 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 19:31:59 +0300 Subject: [PATCH 18/87] 3 3 HW2 StAX --- .../xml/util/StaxStreamProcessor.java | 17 +++++- .../java/ru/javaops/masterjava/MainXml.java | 55 ++++++++++++++++++- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java index 921ca6aff..8ad82c582 100644 --- a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java +++ b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java @@ -20,15 +20,26 @@ public XMLStreamReader getReader() { } public boolean doUntil(int stopEvent, String value) throws XMLStreamException { + return doUntilAny(stopEvent, value) != null; + } + + public String getAttribute(String name) throws XMLStreamException { + return reader.getAttributeValue(null, name); + } + + public String doUntilAny(int stopEvent, String... values) throws XMLStreamException { while (reader.hasNext()) { int event = reader.next(); if (event == stopEvent) { - if (value.equals(getValue(event))) { - return true; + String xmlValue = getValue(event); + for (String value : values) { + if (value.equals(xmlValue)) { + return xmlValue; + } } } } - return false; + return null; } public String getValue(int event) throws XMLStreamException { diff --git a/src/test/java/ru/javaops/masterjava/MainXml.java b/src/test/java/ru/javaops/masterjava/MainXml.java index e60f2a4f2..5f5f55b99 100644 --- a/src/test/java/ru/javaops/masterjava/MainXml.java +++ b/src/test/java/ru/javaops/masterjava/MainXml.java @@ -1,5 +1,6 @@ package ru.javaops.masterjava; +import com.google.common.base.Splitter; import com.google.common.io.Resources; import j2html.tags.ContainerTag; import one.util.streamex.StreamEx; @@ -9,7 +10,9 @@ import ru.javaops.masterjava.xml.schema.User; import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.Schemas; +import ru.javaops.masterjava.xml.util.StaxStreamProcessor; +import javax.xml.stream.events.XMLEvent; import java.io.InputStream; import java.io.Writer; import java.net.URL; @@ -18,13 +21,16 @@ import java.util.*; import java.util.stream.Collectors; +import static com.google.common.base.Strings.nullToEmpty; import static j2html.TagCreator.*; public class MainXml { + private static final Comparator USER_COMPARATOR = Comparator.comparing(User::getValue).thenComparing(User::getEmail); + public static void main(String[] args) throws Exception { if (args.length != 1) { - System.out.println("Format: projectName"); + System.out.println("Required argument: projectName"); System.exit(1); } String projectName = args[0]; @@ -33,11 +39,16 @@ public static void main(String[] args) throws Exception { Set users = parseByJaxb(projectName, payloadUrl); users.forEach(System.out::println); + System.out.println(); String html = toHtml(users, projectName); System.out.println(html); try (Writer writer = Files.newBufferedWriter(Paths.get("out/users.html"))) { writer.write(html); } + + System.out.println(); + users = processByStax(projectName, payloadUrl); + users.forEach(System.out::println); } private static Set parseByJaxb(String projectName, URL payloadUrl) throws Exception { @@ -57,10 +68,50 @@ private static Set parseByJaxb(String projectName, URL payloadUrl) throws return StreamEx.of(payload.getUsers().getUser()) .filter(u -> !Collections.disjoint(groups, u.getGroupRefs())) .collect( - Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getValue).thenComparing(User::getEmail))) + Collectors.toCollection(() -> new TreeSet<>(USER_COMPARATOR)) ); } + private static Set processByStax(String projectName, URL payloadUrl) throws Exception { + + try (InputStream is = payloadUrl.openStream()) { + StaxStreamProcessor processor = new StaxStreamProcessor(is); + final Set groupNames = new HashSet<>(); + + // Projects loop + projects: + while (processor.doUntil(XMLEvent.START_ELEMENT, "Project")) { + if (projectName.equals(processor.getAttribute("name"))) { + // Groups loop + String element; + while ((element = processor.doUntilAny(XMLEvent.START_ELEMENT, "Project", "Group", "Users")) != null) { + if (!element.equals("Group")) { + break projects; + } + groupNames.add(processor.getAttribute("name")); + } + } + } + if (groupNames.isEmpty()) { + throw new IllegalArgumentException("Invalid " + projectName + " or no groups"); + } + + // Users loop + Set users = new TreeSet<>(USER_COMPARATOR); + + while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { + String groupRefs = processor.getAttribute("groupRefs"); + if (!Collections.disjoint(groupNames, Splitter.on(' ').splitToList(nullToEmpty(groupRefs)))) { + User user = new User(); + user.setEmail(processor.getAttribute("email")); + user.setValue(processor.getText()); + users.add(user); + } + } + return users; + } + } + private static String toHtml(Set users, String projectName) { final ContainerTag table = table().with( tr().with(th("FullName"), th("email"))) From 49d2e2310f8a14047ee62f19969b2c0076c8407c Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 19:33:22 +0300 Subject: [PATCH 19/87] 3 4 StAX refactoring --- .../masterjava/xml/util/JaxbParser.java | 5 +++ .../masterjava/xml/util/JaxbUnmarshaller.java | 7 +++- .../xml/util/StaxStreamProcessor.java | 38 ++++++++++++------- .../java/ru/javaops/masterjava/MainXml.java | 15 +++----- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java b/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java index b3a45f66c..563d53ba0 100644 --- a/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java +++ b/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java @@ -5,6 +5,7 @@ import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.PropertyException; +import javax.xml.stream.XMLStreamReader; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import java.io.*; @@ -55,6 +56,10 @@ public T unmarshal(String str) throws JAXBException { return (T) jaxbUnmarshaller.unmarshal(str); } + public T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { + return jaxbUnmarshaller.unmarshal(reader, elementClass); + } + // Marshaller public void setMarshallerProperty(String prop, Object value) { try { diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java b/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java index 7a3e13461..1de89d823 100644 --- a/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java +++ b/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java @@ -3,6 +3,7 @@ import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLStreamReader; import javax.xml.validation.Schema; import java.io.InputStream; import java.io.Reader; @@ -30,4 +31,8 @@ public synchronized Object unmarshal(Reader reader) throws JAXBException { public Object unmarshal(String str) throws JAXBException { return unmarshal(new StringReader(str)); } -} + + public synchronized T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { + return unmarshaller.unmarshal(reader, elementClass).getValue(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java index 8ad82c582..5878118c0 100644 --- a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java +++ b/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java @@ -15,31 +15,43 @@ public StaxStreamProcessor(InputStream is) throws XMLStreamException { reader = FACTORY.createXMLStreamReader(is); } - public XMLStreamReader getReader() { - return reader; + public boolean startElement(String element, String parent) throws XMLStreamException { + while (reader.hasNext()) { + int event = reader.next(); + if (parent != null && isElementEnd(event, parent)) { + return false; + } + if (isElementStart(event, element)) { + return true; + } + } + return false; } - public boolean doUntil(int stopEvent, String value) throws XMLStreamException { - return doUntilAny(stopEvent, value) != null; + private boolean isElementStart(int event, String el) { + return event == XMLEvent.START_ELEMENT && el.equals(reader.getLocalName()); + } + + private boolean isElementEnd(int event, String el) { + return event == XMLEvent.END_ELEMENT && el.equals(reader.getLocalName()); + } + + public XMLStreamReader getReader() { + return reader; } public String getAttribute(String name) throws XMLStreamException { return reader.getAttributeValue(null, name); } - public String doUntilAny(int stopEvent, String... values) throws XMLStreamException { + public boolean doUntil(int stopEvent, String value) throws XMLStreamException { while (reader.hasNext()) { int event = reader.next(); - if (event == stopEvent) { - String xmlValue = getValue(event); - for (String value : values) { - if (value.equals(xmlValue)) { - return xmlValue; - } - } + if (event == stopEvent && value.equals(getValue(event))) { + return true; } } - return null; + return false; } public String getValue(int event) throws XMLStreamException { diff --git a/src/test/java/ru/javaops/masterjava/MainXml.java b/src/test/java/ru/javaops/masterjava/MainXml.java index 5f5f55b99..bcd589460 100644 --- a/src/test/java/ru/javaops/masterjava/MainXml.java +++ b/src/test/java/ru/javaops/masterjava/MainXml.java @@ -80,16 +80,12 @@ private static Set processByStax(String projectName, URL payloadUrl) throw // Projects loop projects: - while (processor.doUntil(XMLEvent.START_ELEMENT, "Project")) { + while (processor.startElement("Project", "Projects")) { if (projectName.equals(processor.getAttribute("name"))) { - // Groups loop - String element; - while ((element = processor.doUntilAny(XMLEvent.START_ELEMENT, "Project", "Group", "Users")) != null) { - if (!element.equals("Group")) { - break projects; - } + while (processor.startElement("Group", "Project")) { groupNames.add(processor.getAttribute("name")); } + break; } } if (groupNames.isEmpty()) { @@ -99,12 +95,11 @@ private static Set processByStax(String projectName, URL payloadUrl) throw // Users loop Set users = new TreeSet<>(USER_COMPARATOR); + JaxbParser parser = new JaxbParser(User.class); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { String groupRefs = processor.getAttribute("groupRefs"); if (!Collections.disjoint(groupNames, Splitter.on(' ').splitToList(nullToEmpty(groupRefs)))) { - User user = new User(); - user.setEmail(processor.getAttribute("email")); - user.setValue(processor.getText()); + User user = parser.unmarshal(processor.getReader(), User.class); users.add(user); } } From 2b880bcad0544496bd4268065bc1a0b801e90125 Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 19:42:37 +0300 Subject: [PATCH 20/87] 3 5 HW2 xslt --- .../masterjava/xml/util/XsltProcessor.java | 4 ++ src/main/resources/groups.xsl | 37 +++++++++++++++++++ .../java/ru/javaops/masterjava/MainXml.java | 16 ++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/main/resources/groups.xsl diff --git a/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java b/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java index 019eeed3d..083febb00 100644 --- a/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java +++ b/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java @@ -40,4 +40,8 @@ public void transform(Reader sourceReader, Writer result) throws TransformerExce public static String getXsltHeader(String xslt) { return "\n"; } + + public void setParameter(String name, String value) { + xformer.setParameter(name, value); + } } diff --git a/src/main/resources/groups.xsl b/src/main/resources/groups.xsl new file mode 100644 index 000000000..c5ade36b8 --- /dev/null +++ b/src/main/resources/groups.xsl @@ -0,0 +1,37 @@ + + + + + + + + + + <xsl:value-of select="$projectName"/> groups + + + +

+ groups +

+ + + + + + + + + + + +
GroupType
+ + + +
+ + +
+
\ No newline at end of file diff --git a/src/test/java/ru/javaops/masterjava/MainXml.java b/src/test/java/ru/javaops/masterjava/MainXml.java index bcd589460..8f57825c7 100644 --- a/src/test/java/ru/javaops/masterjava/MainXml.java +++ b/src/test/java/ru/javaops/masterjava/MainXml.java @@ -11,6 +11,7 @@ import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.Schemas; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; +import ru.javaops.masterjava.xml.util.XsltProcessor; import javax.xml.stream.events.XMLEvent; import java.io.InputStream; @@ -49,6 +50,12 @@ public static void main(String[] args) throws Exception { System.out.println(); users = processByStax(projectName, payloadUrl); users.forEach(System.out::println); + + System.out.println(); + html = transform(projectName, payloadUrl); + try (Writer writer = Files.newBufferedWriter(Paths.get("out/groups.html"))) { + writer.write(html); + } } private static Set parseByJaxb(String projectName, URL payloadUrl) throws Exception { @@ -121,4 +128,13 @@ private static String toHtml(Set users, String projectName) { body().with(h1(projectName + " users"), table) ).render(); } + + private static String transform(String projectName, URL payloadUrl) throws Exception { + URL xsl = Resources.getResource("groups.xsl"); + try (InputStream xmlStream = payloadUrl.openStream(); InputStream xslStream = xsl.openStream()) { + XsltProcessor processor = new XsltProcessor(xslStream); + processor.setParameter("projectName", projectName); + return processor.transform(xmlStream); + } + } } From e7065a4b92f46bb0369ce6a9d202b74baf7d2aae Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Sun, 3 Feb 2019 19:55:39 +0300 Subject: [PATCH 21/87] 3 6 multimodule --- common/pom.xml | 21 +++++++++ parent/pom.xml | 69 ++++++++++++++++++++++++++++ pom.xml | 120 +++++-------------------------------------------- upload/pom.xml | 39 ++++++++++++++++ 4 files changed, 140 insertions(+), 109 deletions(-) create mode 100644 common/pom.xml create mode 100644 parent/pom.xml create mode 100644 upload/pom.xml diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 000000000..33d3da8ed --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + + ru.javaops + parent + ../parent/pom.xml + 1.0-SNAPSHOT + + + common + 1.0-SNAPSHOT + jar + Common + + + + \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml new file mode 100644 index 000000000..d7b056d1d --- /dev/null +++ b/parent/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + ru.javaops + parent + pom + 1.0-SNAPSHOT + Parent + + + 1.8 + UTF-8 + UTF-8 + + + + install + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20.1 + + -Dfile.encoding=UTF-8 + + + + + + + + com.google.guava + guava + 23.3-jre + + + one.util + streamex + RELEASE + + + + + junit + junit + 4.12 + test + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 880943475..0085819cf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,119 +4,21 @@ ru.javaops masterjava - jar + pom 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.7.0 - - ${java.version} - ${java.version} - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20.1 - - -Dfile.encoding=UTF-8 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.1.0 - - - package - - shade - - - benchmarks - - - org.openjdk.jmh.Main - - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - - - - org.openjdk.jmh - jmh-core - RELEASE - - - org.openjdk.jmh - jmh-generator-annprocess - RELEASE - provided - - - com.google.guava - guava - 21.0 - - - one.util - streamex - RELEASE - - - - com.j2html - j2html - RELEASE - - - - - junit - junit - 4.12 - test - - - - - - - - + + upload + common + + diff --git a/upload/pom.xml b/upload/pom.xml new file mode 100644 index 000000000..7cac01c05 --- /dev/null +++ b/upload/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + ru.javaops + parent + ../parent/pom.xml + 1.0-SNAPSHOT + + + upload + 1.0-SNAPSHOT + war + Upload + + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.0 + + false + + + + + + + + ru.javaops + common + ${project.version} + + + \ No newline at end of file From 731616d7edcb67b3b6e0dae66b3da1349f6aaedd Mon Sep 17 00:00:00 2001 From: Mikhalev Pavel Date: Mon, 4 Feb 2019 09:18:56 +0300 Subject: [PATCH 22/87] 3 6 multimodule --- patches/lesson03.md | 139 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 patches/lesson03.md diff --git a/patches/lesson03.md b/patches/lesson03.md new file mode 100644 index 000000000..6fc8f39bf --- /dev/null +++ b/patches/lesson03.md @@ -0,0 +1,139 @@ +# Онлайн проекта Masterjava. + +## [Материалы занятия](https://drive.google.com/drive/u/0/folders/0B9Ye2auQ_NsFSGFQZ2I0V2pmbXM) (скачать все патчи можно через Download папки patch) + +## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Разбор домашнего задания HW2 +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 1. Scheme, j2html, JAXB +#### Apply 3_1_HW2_schema.patch +- xs:ID restriction + +#### Apply 3_2_HW2_JAXB.patch +> - убрал второй параметр xmlName (всегда `payload.xml`) +> - в `parseByJaxb` сделал закрытие `InputStream` сразу после обработки +> - сделал методы статическими +> - вместо вложенного стрима для групп юзера сделал пересечение коллекций `Collections.disjoint` +> - результат JAXB также вывожу в HTML (в ДЗ только в Optional) +> - в `j2html` вместо `setAttribute` сделал `attr` + +- Тагир Валеев. StreamEx + странности Stream API +- StreamEx примеры +- Java HTML5 builder + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 2. Optional: StAX +#### Apply 3_3_HW2_StAX.patch +> - зарефакторил в `StaxStreamProcessor` `doUntil()` и `getAttribute()` +> - константы вставил в код +> - вместо вложенного цикла для групп юзера сделал пересечение коллекций `Collections.disjoint` и для маскирования пустых групп `Strings.nullToEmpty` + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 1. StAX refactoring: startElement + JAXB +#### Apply 3_4_StAX_refactoring.patch +- [Java XML API: выбираем правильно. StAX: работаем с удовольствием](https://habrahabr.ru/post/339716/) +- [StAX + JAXB](http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html) + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 3. Optional: XSLT +#### Apply 3_5_HW2_xslt.patch +> В `groups.xsl` добавил явный namespace. Теперь при создании xPath IDEA делает автодополнения + +- XSL Transformation in Java with parameters +- Test Cases for XSLT support in browsers + +## Затяние 3 +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 4. [Обзор Guava](https://drive.google.com/open?id=0B9Ye2auQ_NsFeFB5a29JQ2tRNHM) +- [The Top 20 Most Popular Java Libraries](https://dzone.com/articles/the-top-100-java-libraries-in-2016-after-analyzing) +- [Guava Wiki](https://github.com/google/guava/wiki) + - [Apache Commons](https://commons.apache.org/) + - [Spring Boot cache providers](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html#_supported_cache_providers) +- [Release 21.0: Java 8!](https://github.com/google/guava/wiki/Release21) +- [118 слайдов от Егора Чернышева](https://www.slideshare.net/echernyshev/guava-41982734) + + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 5. Монады. flatMap +- Функторы, аппликативные функторы и монады в картинках +- Монады в Java 8 + - Куср Сoursera по функциональному программированию +- Three Monad laws. +- What's Wrong in Java 8 Monads + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 6. SOA и Микросервисы + - Сервис-ориентированная архитектура как повышение абстракции производства ПО + - What is service-oriented architecture? + - Современное представление (SOA): Микросервисы + - Микросервисы (Microservices) + - Доклад Кирилла Толкачёва и Александра Тарасова про микросервисы + - Минск: Микросервисы - Огонь, Вода и Медные трубы + - Слайды + - CQRS + - Event Sourcing, + - CQRS + Event Sourcing, + - Event-Sourcing+CQRS example application + - Интеграция через несколько ДБ. + - Гибридные решения для эффективного хранения данных. + - PgBouncer + - Microservices with Spring, Микросервисная архитектура, Spring Cloud и Docker + - Microservices demo + - Spring Cloud Netflix + - Netflix + - Service Discovery: Eureka + - Circuit Breaker: Hystrix + - Load Balancer: Ribbon + - Declarative REST Client: Feign + - External Configuration: Archaius + - Gateway, Router and Filter: Zuul + - Metrics: Spectator, Servo, and Atlas + - RxJava with Spring MVC + - ReactiveX + - RxJava: Reactive Extensions for the JVM + - Справичник + - microservice in spring.io + - Building microservices with Spring Boot – part 1 + - Building microservices with Spring Boot – part 2 + - Deploying Spring Boot-based microservices with Docker – part 3 + - Developing Microservices With Spring Boot + - Webinar: Building "Bootiful" Microservices with Spring Boot + - Managing your Microservices on Heroku with Netflix's Eureka + - Платформы + - A comparison of Microservices Frameworks + - Lagom, Play Framework + - Микросервисе в обзоре Zeroturnaround + - Spring 5: Functional Web Framework + - Apache Camel for Micro­service Architectures + +### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 7. Многомодульный Maven проект +#### Apply 3_5_multimodule.patch +- Maven на topjava. Snapshot. The Reactor. +- Maven module inheritance vs aggregation +- Расширение кругозора: + - Антон Архипов, Евгений Борисов, Барух Садогурский — Maven vs Gradle vs SBT + +## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Домашнее задание HW3 +- Сделать структуру проекта согласно схеме. В модулях c `packaging=pom` кода нет, корневое `src` перенести в другие модули. + - `Matrix + Benchmark` перенести в модуль `test`. + - Схемы XML/XSD в модуль `upload` + +**Внимание: название модуля `export` в видео и картинке идеологически неверно. В коде и патчах переименовал в `upload`.** + +WAR модули: +- **`mail-service`** - модуль отсылки почты. Транспорт: JAX-WS, JAX-RS, JMX, AKKA +- **`upload`** - модуль загрузки payload.xml. Заргузка и многопоточночная вставка в DB +- **`webapp`** - веб-интерфейс для отсылки почты пользователям из DB. Клиент модуля `mail-service` + +![image](https://cloud.githubusercontent.com/assets/13649199/23876457/ab01ff0a-084e-11e7-964f-49c90579fac9.png) + +- Учитывая, что web модулей в проекте предполагается много, измените структуру, чтобы не дублировать `maven-war-plugin` (и другие общие зависимости war модулей). +- Проверьте, что проект собирается! + +#### Optional +- Реализовать простую форму заргузки файла `payload.xml` в модуле `upload` (через StAX) и отобразить загруженных пользователей (имя/email/flag) + - для отображения взять любой шаблон (JSP, [Thymeleaf](http://www.concretepage.com/thymeleaf/java-thymeleaf-example-getting-started-with-thymeleaf), ...) + - загрузку сделать через любую реализацию (Servlet 3.х предпочтительнее): + - Commons FileUpload + - Tomcat fileupload copied and package renamed + - Java EE 6 Tutorial: fileupload example + - Servlet 3.0's FileUpload Sample +- Сделать загрузку через StAX+JAXB многопоточно. Сейчас `JaxbParser` реализован с `synchronize`, что означает что он потокобезопасен (может работать с несколькими запросами), но последовательно, в одном потоке. Нужен рефакторинг. + - [Thread safe of JAXBContext, Marshaller, Unmarshalleris](https://stackoverflow.com/a/37926057/548473) + +## Замечания: +- 1: имя артифакта `artifactId` как и пакеты называть только в lowercase. Обычно используют "-" как разделитель слов. Имя каталога модуля лучше делать тоже самое: [Project Directories Should Match the Artifact ID](http://blog.sonatype.com/2011/01/maven-tip-project-directories-and-artifact-ids/) +- 2: следите, чтобы пути в шаблонах были не от рута "/". Каждое приложение деплоится в свой Application Context. +- 3: чтобы не дублировать `maven-war-plugin` сделать `paren-web`. Он наследуется от `parent`, а от него наследуются все war модули. From aeb2b6696df04520e18b1ab10405ef166c4b4061 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:51:38 +0300 Subject: [PATCH 23/87] Preparing to multimodule system --- patches/lesson03.md | 139 -------------------------------------------- 1 file changed, 139 deletions(-) delete mode 100644 patches/lesson03.md diff --git a/patches/lesson03.md b/patches/lesson03.md deleted file mode 100644 index 6fc8f39bf..000000000 --- a/patches/lesson03.md +++ /dev/null @@ -1,139 +0,0 @@ -# Онлайн проекта Masterjava. - -## [Материалы занятия](https://drive.google.com/drive/u/0/folders/0B9Ye2auQ_NsFSGFQZ2I0V2pmbXM) (скачать все патчи можно через Download папки patch) - -## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Разбор домашнего задания HW2 -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 1. Scheme, j2html, JAXB -#### Apply 3_1_HW2_schema.patch -- xs:ID restriction - -#### Apply 3_2_HW2_JAXB.patch -> - убрал второй параметр xmlName (всегда `payload.xml`) -> - в `parseByJaxb` сделал закрытие `InputStream` сразу после обработки -> - сделал методы статическими -> - вместо вложенного стрима для групп юзера сделал пересечение коллекций `Collections.disjoint` -> - результат JAXB также вывожу в HTML (в ДЗ только в Optional) -> - в `j2html` вместо `setAttribute` сделал `attr` - -- Тагир Валеев. StreamEx + странности Stream API -- StreamEx примеры -- Java HTML5 builder - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 2. Optional: StAX -#### Apply 3_3_HW2_StAX.patch -> - зарефакторил в `StaxStreamProcessor` `doUntil()` и `getAttribute()` -> - константы вставил в код -> - вместо вложенного цикла для групп юзера сделал пересечение коллекций `Collections.disjoint` и для маскирования пустых групп `Strings.nullToEmpty` - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 1. StAX refactoring: startElement + JAXB -#### Apply 3_4_StAX_refactoring.patch -- [Java XML API: выбираем правильно. StAX: работаем с удовольствием](https://habrahabr.ru/post/339716/) -- [StAX + JAXB](http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html) - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 3. Optional: XSLT -#### Apply 3_5_HW2_xslt.patch -> В `groups.xsl` добавил явный namespace. Теперь при создании xPath IDEA делает автодополнения - -- XSL Transformation in Java with parameters -- Test Cases for XSLT support in browsers - -## Затяние 3 -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 4. [Обзор Guava](https://drive.google.com/open?id=0B9Ye2auQ_NsFeFB5a29JQ2tRNHM) -- [The Top 20 Most Popular Java Libraries](https://dzone.com/articles/the-top-100-java-libraries-in-2016-after-analyzing) -- [Guava Wiki](https://github.com/google/guava/wiki) - - [Apache Commons](https://commons.apache.org/) - - [Spring Boot cache providers](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html#_supported_cache_providers) -- [Release 21.0: Java 8!](https://github.com/google/guava/wiki/Release21) -- [118 слайдов от Егора Чернышева](https://www.slideshare.net/echernyshev/guava-41982734) - - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 5. Монады. flatMap -- Функторы, аппликативные функторы и монады в картинках -- Монады в Java 8 - - Куср Сoursera по функциональному программированию -- Three Monad laws. -- What's Wrong in Java 8 Monads - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 6. SOA и Микросервисы - - Сервис-ориентированная архитектура как повышение абстракции производства ПО - - What is service-oriented architecture? - - Современное представление (SOA): Микросервисы - - Микросервисы (Microservices) - - Доклад Кирилла Толкачёва и Александра Тарасова про микросервисы - - Минск: Микросервисы - Огонь, Вода и Медные трубы - - Слайды - - CQRS - - Event Sourcing, - - CQRS + Event Sourcing, - - Event-Sourcing+CQRS example application - - Интеграция через несколько ДБ. - - Гибридные решения для эффективного хранения данных. - - PgBouncer - - Microservices with Spring, Микросервисная архитектура, Spring Cloud и Docker - - Microservices demo - - Spring Cloud Netflix - - Netflix - - Service Discovery: Eureka - - Circuit Breaker: Hystrix - - Load Balancer: Ribbon - - Declarative REST Client: Feign - - External Configuration: Archaius - - Gateway, Router and Filter: Zuul - - Metrics: Spectator, Servo, and Atlas - - RxJava with Spring MVC - - ReactiveX - - RxJava: Reactive Extensions for the JVM - - Справичник - - microservice in spring.io - - Building microservices with Spring Boot – part 1 - - Building microservices with Spring Boot – part 2 - - Deploying Spring Boot-based microservices with Docker – part 3 - - Developing Microservices With Spring Boot - - Webinar: Building "Bootiful" Microservices with Spring Boot - - Managing your Microservices on Heroku with Netflix's Eureka - - Платформы - - A comparison of Microservices Frameworks - - Lagom, Play Framework - - Микросервисе в обзоре Zeroturnaround - - Spring 5: Functional Web Framework - - Apache Camel for Micro­service Architectures - -### ![video](https://cloud.githubusercontent.com/assets/13649199/13672715/06dbc6ce-e6e7-11e5-81a9-04fbddb9e488.png) 7. Многомодульный Maven проект -#### Apply 3_5_multimodule.patch -- Maven на topjava. Snapshot. The Reactor. -- Maven module inheritance vs aggregation -- Расширение кругозора: - - Антон Архипов, Евгений Борисов, Барух Садогурский — Maven vs Gradle vs SBT - -## ![hw](https://cloud.githubusercontent.com/assets/13649199/13672719/09593080-e6e7-11e5-81d1-5cb629c438ca.png) Домашнее задание HW3 -- Сделать структуру проекта согласно схеме. В модулях c `packaging=pom` кода нет, корневое `src` перенести в другие модули. - - `Matrix + Benchmark` перенести в модуль `test`. - - Схемы XML/XSD в модуль `upload` - -**Внимание: название модуля `export` в видео и картинке идеологически неверно. В коде и патчах переименовал в `upload`.** - -WAR модули: -- **`mail-service`** - модуль отсылки почты. Транспорт: JAX-WS, JAX-RS, JMX, AKKA -- **`upload`** - модуль загрузки payload.xml. Заргузка и многопоточночная вставка в DB -- **`webapp`** - веб-интерфейс для отсылки почты пользователям из DB. Клиент модуля `mail-service` - -![image](https://cloud.githubusercontent.com/assets/13649199/23876457/ab01ff0a-084e-11e7-964f-49c90579fac9.png) - -- Учитывая, что web модулей в проекте предполагается много, измените структуру, чтобы не дублировать `maven-war-plugin` (и другие общие зависимости war модулей). -- Проверьте, что проект собирается! - -#### Optional -- Реализовать простую форму заргузки файла `payload.xml` в модуле `upload` (через StAX) и отобразить загруженных пользователей (имя/email/flag) - - для отображения взять любой шаблон (JSP, [Thymeleaf](http://www.concretepage.com/thymeleaf/java-thymeleaf-example-getting-started-with-thymeleaf), ...) - - загрузку сделать через любую реализацию (Servlet 3.х предпочтительнее): - - Commons FileUpload - - Tomcat fileupload copied and package renamed - - Java EE 6 Tutorial: fileupload example - - Servlet 3.0's FileUpload Sample -- Сделать загрузку через StAX+JAXB многопоточно. Сейчас `JaxbParser` реализован с `synchronize`, что означает что он потокобезопасен (может работать с несколькими запросами), но последовательно, в одном потоке. Нужен рефакторинг. - - [Thread safe of JAXBContext, Marshaller, Unmarshalleris](https://stackoverflow.com/a/37926057/548473) - -## Замечания: -- 1: имя артифакта `artifactId` как и пакеты называть только в lowercase. Обычно используют "-" как разделитель слов. Имя каталога модуля лучше делать тоже самое: [Project Directories Should Match the Artifact ID](http://blog.sonatype.com/2011/01/maven-tip-project-directories-and-artifact-ids/) -- 2: следите, чтобы пути в шаблонах были не от рута "/". Каждое приложение деплоится в свой Application Context. -- 3: чтобы не дублировать `maven-war-plugin` сделать `paren-web`. Он наследуется от `parent`, а от него наследуются все war модули. From aa0f7f464b616c6d8dd362d6bb94153f44378e4e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:52:09 +0300 Subject: [PATCH 24/87] 4 1 HW3 pom structure --- {upload => parent-web}/pom.xml | 14 +++- pom.xml | 11 +-- services/mail-api/pom.xml | 26 +++++++ services/mail-service/pom.xml | 26 +++++++ .../service/mail/MailServiceExecutor.java | 6 +- services/pom.xml | 15 ++++ test/pom.xml | 76 +++++++++++++++++++ .../javaops/masterjava/matrix/MainMatrix.java | 0 .../masterjava/matrix/MatrixBenchmark.java | 0 .../javaops/masterjava/matrix/MatrixUtil.java | 0 web/pom.xml | 16 ++++ web/upload/pom.xml | 31 ++++++++ .../masterjava/xml/schema/CityType.java | 33 ++++---- .../masterjava/xml/schema/FlagType.java | 0 .../masterjava/xml/schema/GroupType.java | 0 .../masterjava/xml/schema/ObjectFactory.java | 0 .../masterjava/xml/schema/Payload.java | 0 .../masterjava/xml/schema/Project.java | 0 .../javaops/masterjava/xml/schema/User.java | 0 .../masterjava/xml/util/JaxbMarshaller.java | 0 .../masterjava/xml/util/JaxbParser.java | 0 .../masterjava/xml/util/JaxbUnmarshaller.java | 0 .../javaops/masterjava/xml/util/Schemas.java | 0 .../xml/util/StaxStreamProcessor.java | 0 .../masterjava/xml/util/XPathProcessor.java | 0 .../masterjava/xml/util/XsltProcessor.java | 0 .../upload/src}/main/resources/cities.xsl | 0 .../upload/src}/main/resources/groups.xsl | 0 .../upload/src}/main/resources/payload.xsd | 0 .../java/ru/javaops/masterjava/MainXml.java | 5 +- .../masterjava/xml/util/JaxbParserTest.java | 0 .../xml/util/StaxStreamProcessorTest.java | 0 .../xml/util/XPathProcessorTest.java | 0 .../xml/util/XsltProcessorTest.java | 0 .../upload/src}/test/resources/city.xml | 0 .../upload/src}/test/resources/payload.xml | 0 web/webapp/pom.xml | 30 ++++++++ 37 files changed, 252 insertions(+), 37 deletions(-) rename {upload => parent-web}/pom.xml (85%) create mode 100644 services/mail-api/pom.xml create mode 100644 services/mail-service/pom.xml rename src/main/java/ru/javaops/masterjava/service/MailService.java => services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java (96%) create mode 100644 services/pom.xml create mode 100644 test/pom.xml rename {src => test/src}/main/java/ru/javaops/masterjava/matrix/MainMatrix.java (100%) rename {src => test/src}/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java (100%) rename {src => test/src}/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java (100%) create mode 100644 web/pom.xml create mode 100644 web/upload/pom.xml rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/CityType.java (81%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/FlagType.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/GroupType.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/Payload.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/Project.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/schema/User.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/Schemas.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java (100%) rename {src => web/upload/src}/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java (100%) rename {src => web/upload/src}/main/resources/cities.xsl (100%) rename {src => web/upload/src}/main/resources/groups.xsl (100%) rename {src => web/upload/src}/main/resources/payload.xsd (100%) rename {src => web/upload/src}/test/java/ru/javaops/masterjava/MainXml.java (96%) rename {src => web/upload/src}/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java (100%) rename {src => web/upload/src}/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java (100%) rename {src => web/upload/src}/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java (100%) rename {src => web/upload/src}/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java (100%) rename {src => web/upload/src}/test/resources/city.xml (100%) rename {src => web/upload/src}/test/resources/payload.xml (100%) create mode 100644 web/webapp/pom.xml diff --git a/upload/pom.xml b/parent-web/pom.xml similarity index 85% rename from upload/pom.xml rename to parent-web/pom.xml index 7cac01c05..f1fa775c5 100644 --- a/upload/pom.xml +++ b/parent-web/pom.xml @@ -11,10 +11,10 @@ 1.0-SNAPSHOT - upload + parent-web + pom 1.0-SNAPSHOT - war - Upload + Parent Web @@ -36,4 +36,10 @@ ${project.version}
- \ No newline at end of file + + + + + + + diff --git a/pom.xml b/pom.xml index 0085819cf..f0c5cbbb2 100644 --- a/pom.xml +++ b/pom.xml @@ -8,17 +8,14 @@ 1.0-SNAPSHOT - Master Java + Masterjava Root https://github.com/JavaOPs/masterjava - upload common - + + web + services diff --git a/services/mail-api/pom.xml b/services/mail-api/pom.xml new file mode 100644 index 000000000..9bc8f887e --- /dev/null +++ b/services/mail-api/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + ru.javaops + parent + ../../parent/pom.xml + 1.0-SNAPSHOT + + + mail-api + 1.0-SNAPSHOT + Mail API + + + + ${project.groupId} + common + ${project.version} + + + + diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml new file mode 100644 index 000000000..bddffc16c --- /dev/null +++ b/services/mail-service/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + ru.javaops + parent-web + ../../parent-web/pom.xml + 1.0-SNAPSHOT + + + mail-service + 1.0-SNAPSHOT + war + Mail Service + + + + ${project.groupId} + mail-api + ${project.version} + + + diff --git a/src/main/java/ru/javaops/masterjava/service/MailService.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java similarity index 96% rename from src/main/java/ru/javaops/masterjava/service/MailService.java rename to services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java index 2b6aeb294..545bded32 100644 --- a/src/main/java/ru/javaops/masterjava/service/MailService.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java @@ -1,10 +1,10 @@ -package ru.javaops.masterjava.service; +package ru.javaops.masterjava.service.mail; import java.util.Collections; import java.util.List; import java.util.Set; -public class MailService { +public class MailServiceExecutor { private static final String OK = "OK"; private static final String INTERRUPTED_BY_FAULTS_NUMBER = "+++ Interrupted by faults number"; @@ -72,4 +72,4 @@ public String toString() { (failedCause == null ? "" : "Failed cause" + failedCause); } } -} \ No newline at end of file +} diff --git a/services/pom.xml b/services/pom.xml new file mode 100644 index 000000000..62ffc5e38 --- /dev/null +++ b/services/pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + + ru.javaops + services + pom + 1.0-SNAPSHOT + + Services + + mail-api + mail-service + + diff --git a/test/pom.xml b/test/pom.xml new file mode 100644 index 000000000..0b944bf22 --- /dev/null +++ b/test/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + + ru.javaops + parent + ../parent/pom.xml + 1.0-SNAPSHOT + + + test + 1.0-SNAPSHOT + Test + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.2 + + + package + + shade + + + benchmarks + + + org.openjdk.jmh.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + + ${project.groupId} + common + ${project.version} + + + org.openjdk.jmh + jmh-core + RELEASE + + + org.openjdk.jmh + jmh-generator-annprocess + RELEASE + provided + + + diff --git a/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java b/test/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java rename to test/src/main/java/ru/javaops/masterjava/matrix/MainMatrix.java diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java b/test/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java rename to test/src/main/java/ru/javaops/masterjava/matrix/MatrixBenchmark.java diff --git a/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java b/test/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java rename to test/src/main/java/ru/javaops/masterjava/matrix/MatrixUtil.java diff --git a/web/pom.xml b/web/pom.xml new file mode 100644 index 000000000..ddb4a428b --- /dev/null +++ b/web/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + ru.javaops + web + pom + 1.0-SNAPSHOT + Web + + + common-web + upload + webapp + + diff --git a/web/upload/pom.xml b/web/upload/pom.xml new file mode 100644 index 000000000..24ef5f3df --- /dev/null +++ b/web/upload/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + ru.javaops + parent-web + ../../parent-web/pom.xml + 1.0-SNAPSHOT + + + upload + 1.0-SNAPSHOT + war + Upload + + + upload + + + + + com.j2html + j2html + 1.2.0 + + + + diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java similarity index 81% rename from src/main/java/ru/javaops/masterjava/xml/schema/CityType.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java index 029e352cb..0d8152566 100644 --- a/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/CityType.java @@ -1,22 +1,17 @@ + package ru.javaops.masterjava.xml.schema; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlID; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** *

Java class for cityType complex type. - * + * *

The following schema fragment specifies the expected content contained within this class. - * + * *

  * <complexType name="cityType">
  *   <simpleContent>
@@ -26,8 +21,8 @@
  *   </simpleContent>
  * </complexType>
  * 
- * - * + * + * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "cityType", namespace = "http://javaops.ru", propOrder = { @@ -45,11 +40,11 @@ public class CityType { /** * Gets the value of the value property. - * + * * @return * possible object is * {@link String } - * + * */ public String getValue() { return value; @@ -57,11 +52,11 @@ public String getValue() { /** * Sets the value of the value property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setValue(String value) { this.value = value; @@ -69,11 +64,11 @@ public void setValue(String value) { /** * Gets the value of the id property. - * + * * @return * possible object is * {@link String } - * + * */ public String getId() { return id; @@ -81,11 +76,11 @@ public String getId() { /** * Sets the value of the id property. - * + * * @param value * allowed object is * {@link String } - * + * */ public void setId(String value) { this.id = value; diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/FlagType.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/GroupType.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/ObjectFactory.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/Payload.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/Payload.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/Project.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/Project.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/Project.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/Project.java diff --git a/src/main/java/ru/javaops/masterjava/xml/schema/User.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/schema/User.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/schema/User.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/schema/User.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/Schemas.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/Schemas.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/StaxStreamProcessor.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/XPathProcessor.java diff --git a/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java similarity index 100% rename from src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java rename to web/upload/src/main/java/ru/javaops/masterjava/xml/util/XsltProcessor.java diff --git a/src/main/resources/cities.xsl b/web/upload/src/main/resources/cities.xsl similarity index 100% rename from src/main/resources/cities.xsl rename to web/upload/src/main/resources/cities.xsl diff --git a/src/main/resources/groups.xsl b/web/upload/src/main/resources/groups.xsl similarity index 100% rename from src/main/resources/groups.xsl rename to web/upload/src/main/resources/groups.xsl diff --git a/src/main/resources/payload.xsd b/web/upload/src/main/resources/payload.xsd similarity index 100% rename from src/main/resources/payload.xsd rename to web/upload/src/main/resources/payload.xsd diff --git a/src/test/java/ru/javaops/masterjava/MainXml.java b/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java similarity index 96% rename from src/test/java/ru/javaops/masterjava/MainXml.java rename to web/upload/src/test/java/ru/javaops/masterjava/MainXml.java index 8f57825c7..08db01e0e 100644 --- a/src/test/java/ru/javaops/masterjava/MainXml.java +++ b/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java @@ -20,7 +20,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; -import java.util.stream.Collectors; import static com.google.common.base.Strings.nullToEmpty; import static j2html.TagCreator.*; @@ -74,9 +73,7 @@ private static Set parseByJaxb(String projectName, URL payloadUrl) throws final Set groups = new HashSet<>(project.getGroup()); // identity compare return StreamEx.of(payload.getUsers().getUser()) .filter(u -> !Collections.disjoint(groups, u.getGroupRefs())) - .collect( - Collectors.toCollection(() -> new TreeSet<>(USER_COMPARATOR)) - ); + .toCollection(() -> new TreeSet<>(USER_COMPARATOR)); } private static Set processByStax(String projectName, URL payloadUrl) throws Exception { diff --git a/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java similarity index 100% rename from src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java rename to web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java diff --git a/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java similarity index 100% rename from src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java rename to web/upload/src/test/java/ru/javaops/masterjava/xml/util/StaxStreamProcessorTest.java diff --git a/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java similarity index 100% rename from src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java rename to web/upload/src/test/java/ru/javaops/masterjava/xml/util/XPathProcessorTest.java diff --git a/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java similarity index 100% rename from src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java rename to web/upload/src/test/java/ru/javaops/masterjava/xml/util/XsltProcessorTest.java diff --git a/src/test/resources/city.xml b/web/upload/src/test/resources/city.xml similarity index 100% rename from src/test/resources/city.xml rename to web/upload/src/test/resources/city.xml diff --git a/src/test/resources/payload.xml b/web/upload/src/test/resources/payload.xml similarity index 100% rename from src/test/resources/payload.xml rename to web/upload/src/test/resources/payload.xml diff --git a/web/webapp/pom.xml b/web/webapp/pom.xml new file mode 100644 index 000000000..cf0983cc6 --- /dev/null +++ b/web/webapp/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + ru.javaops + parent-web + ../../parent-web/pom.xml + 1.0-SNAPSHOT + + + webapp + 1.0-SNAPSHOT + war + WebApp + + + webapp + + + + + ${project.groupId} + common + ${project.version} + + + From d0b22c995818f3a829a8476f16919e75cb1db4ad Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:52:20 +0300 Subject: [PATCH 25/87] 4 2 HW3 thymeleaf upload --- parent-web/pom.xml | 10 ++- web/common-web/pom.xml | 37 +++++++++++ .../common/web/ThymeleafListener.java | 20 ++++++ .../masterjava/common/web/ThymeleafUtil.java | 24 +++++++ .../ru/javaops/masterjava/model/User.java | 63 +++++++++++++++++++ .../ru/javaops/masterjava/model/UserFlag.java | 11 ++++ .../masterjava/upload/UploadServlet.java | 51 +++++++++++++++ .../masterjava/upload/UserProcessor.java | 28 +++++++++ .../webapp/WEB-INF/templates/exception.html | 20 ++++++ .../main/webapp/WEB-INF/templates/result.html | 27 ++++++++ .../main/webapp/WEB-INF/templates/upload.html | 16 +++++ 11 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 web/common-web/pom.xml create mode 100644 web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafListener.java create mode 100644 web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafUtil.java create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/model/User.java create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java create mode 100644 web/upload/src/main/webapp/WEB-INF/templates/exception.html create mode 100644 web/upload/src/main/webapp/WEB-INF/templates/result.html create mode 100644 web/upload/src/main/webapp/WEB-INF/templates/upload.html diff --git a/parent-web/pom.xml b/parent-web/pom.xml index f1fa775c5..d47d5a51b 100644 --- a/parent-web/pom.xml +++ b/parent-web/pom.xml @@ -31,10 +31,16 @@ - ru.javaops - common + ${project.groupId} + common-web ${project.version} + + javax.servlet + javax.servlet-api + 3.1.0 + provided + diff --git a/web/common-web/pom.xml b/web/common-web/pom.xml new file mode 100644 index 000000000..150c89250 --- /dev/null +++ b/web/common-web/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + ru.javaops + parent + ../../parent/pom.xml + 1.0-SNAPSHOT + + + common-web + 1.0-SNAPSHOT + Common Web + + + + ${project.groupId} + common + ${project.version} + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + org.thymeleaf + thymeleaf + 3.0.8.RELEASE + + + diff --git a/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafListener.java b/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafListener.java new file mode 100644 index 000000000..16948aa44 --- /dev/null +++ b/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafListener.java @@ -0,0 +1,20 @@ +package ru.javaops.masterjava.common.web; + +import org.thymeleaf.TemplateEngine; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class ThymeleafListener implements ServletContextListener { + + public static TemplateEngine engine; + + public void contextInitialized(ServletContextEvent sce) { + engine = ThymeleafUtil.getTemplateEngine(sce.getServletContext()); + } + + public void contextDestroyed(ServletContextEvent sce) { + } +} diff --git a/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafUtil.java b/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafUtil.java new file mode 100644 index 000000000..bf87ed3eb --- /dev/null +++ b/web/common-web/src/main/java/ru/javaops/masterjava/common/web/ThymeleafUtil.java @@ -0,0 +1,24 @@ +package ru.javaops.masterjava.common.web; + +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.templatemode.TemplateMode; +import org.thymeleaf.templateresolver.ServletContextTemplateResolver; + +import javax.servlet.ServletContext; + +public class ThymeleafUtil { + + private ThymeleafUtil() { + } + + public static TemplateEngine getTemplateEngine(ServletContext context) { + final ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(context); + templateResolver.setTemplateMode(TemplateMode.HTML); + templateResolver.setPrefix("/WEB-INF/templates/"); + templateResolver.setSuffix(".html"); + templateResolver.setCacheTTLMs(1000L); + final TemplateEngine engine = new TemplateEngine(); + engine.setTemplateResolver(templateResolver); + return engine; + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/model/User.java b/web/upload/src/main/java/ru/javaops/masterjava/model/User.java new file mode 100644 index 000000000..d7c3f8cd3 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/model/User.java @@ -0,0 +1,63 @@ +package ru.javaops.masterjava.model; + +import java.util.Objects; + +public class User { + private final Integer id; + private final String fullName; + private final String email; + private final UserFlag flag; + + public User(String fullName, String email, UserFlag flag) { + this(null, fullName, email, flag); + } + + public User(Integer id, String fullName, String email, UserFlag flag) { + this.id = id; + this.fullName = fullName; + this.email = email; + this.flag = flag; + } + + public Integer getId() { + return id; + } + + public String getFullName() { + return fullName; + } + + public String getEmail() { + return email; + } + + public UserFlag getFlag() { + return flag; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && + Objects.equals(fullName, user.fullName) && + Objects.equals(email, user.email) && + flag == user.flag; + } + + @Override + public int hashCode() { + return Objects.hash(id, fullName, email, flag); + } + + @Override + public String toString() { + return "User (" + + "id=" + id + + ", fullName='" + fullName + '\'' + + ", email='" + email + '\'' + + ", flag=" + flag + + ')'; + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java b/web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java new file mode 100644 index 000000000..603626ae1 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java @@ -0,0 +1,11 @@ +package ru.javaops.masterjava.model; + +/** + * gkislin + * 13.10.2016 + */ +public enum UserFlag { + active, + deleted, + superuser; +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java new file mode 100644 index 000000000..3831eda27 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -0,0 +1,51 @@ +package ru.javaops.masterjava.upload; + +import org.thymeleaf.context.WebContext; +import ru.javaops.masterjava.model.User; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static ru.javaops.masterjava.common.web.ThymeleafListener.engine; + +@WebServlet(urlPatterns = "/", loadOnStartup = 1) +@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit +public class UploadServlet extends HttpServlet { + + private final UserProcessor userProcessor = new UserProcessor(); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); + engine.process("upload", webContext, resp.getWriter()); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); + + try { +// http://docs.oracle.com/javaee/6/tutorial/doc/glraq.html + Part filePart = req.getPart("fileToUpload"); + if (filePart.getSize() == 0) { + throw new IllegalStateException("Upload file have not been selected"); + } + try (InputStream is = filePart.getInputStream()) { + List users = userProcessor.process(is); + webContext.setVariable("users", users); + engine.process("result", webContext, resp.getWriter()); + } + } catch (Exception e) { + webContext.setVariable("exception", e); + engine.process("exception", webContext, resp.getWriter()); + } + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java new file mode 100644 index 000000000..4c81d3483 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -0,0 +1,28 @@ +package ru.javaops.masterjava.upload; + +import ru.javaops.masterjava.model.User; +import ru.javaops.masterjava.model.UserFlag; +import ru.javaops.masterjava.xml.util.StaxStreamProcessor; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class UserProcessor { + + public List process(final InputStream is) throws XMLStreamException { + final StaxStreamProcessor processor = new StaxStreamProcessor(is); + List users = new ArrayList<>(); + + while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { + final String email = processor.getAttribute("email"); + final UserFlag flag = UserFlag.valueOf(processor.getAttribute("flag")); + final String fullName = processor.getReader().getElementText(); + final User user = new User(fullName, email, flag); + users.add(user); + } + return users; + } +} diff --git a/web/upload/src/main/webapp/WEB-INF/templates/exception.html b/web/upload/src/main/webapp/WEB-INF/templates/exception.html new file mode 100644 index 000000000..2297da20d --- /dev/null +++ b/web/upload/src/main/webapp/WEB-INF/templates/exception.html @@ -0,0 +1,20 @@ + + + + Application error + + + +
+
+
+

Application error:

+ +

exception.message

+
    +
  • +
+
+
+ + diff --git a/web/upload/src/main/webapp/WEB-INF/templates/result.html b/web/upload/src/main/webapp/WEB-INF/templates/result.html new file mode 100644 index 000000000..b8a274c0f --- /dev/null +++ b/web/upload/src/main/webapp/WEB-INF/templates/result.html @@ -0,0 +1,27 @@ + + + + Uploaded users + + +

Upload XML

+

Uploaded users

+ + + + + + + + + + + + + + + + +
Full NameEmailFlag
+ + diff --git a/web/upload/src/main/webapp/WEB-INF/templates/upload.html b/web/upload/src/main/webapp/WEB-INF/templates/upload.html new file mode 100644 index 000000000..bd2abb33b --- /dev/null +++ b/web/upload/src/main/webapp/WEB-INF/templates/upload.html @@ -0,0 +1,16 @@ + + + Upload XML + + +
+

Select xml file to upload

+

+
+

+

+ +

+
+ + From f5dcb95515e1555327cb843ff3ce07263334f8fd Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:55:56 +0300 Subject: [PATCH 26/87] 4 4 HW3 jaxb stax --- .../masterjava/upload/UserProcessor.java | 14 ++-- .../masterjava/xml/util/JaxbMarshaller.java | 13 ++-- .../masterjava/xml/util/JaxbParser.java | 71 ++++++++----------- .../masterjava/xml/util/JaxbUnmarshaller.java | 18 ++--- .../java/ru/javaops/masterjava/MainXml.java | 13 ++-- .../masterjava/xml/util/JaxbParserTest.java | 24 ++++--- 6 files changed, 76 insertions(+), 77 deletions(-) diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 4c81d3483..f8c574bf8 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -2,8 +2,12 @@ import ru.javaops.masterjava.model.User; import ru.javaops.masterjava.model.UserFlag; +import ru.javaops.masterjava.xml.schema.ObjectFactory; +import ru.javaops.masterjava.xml.util.JaxbParser; +import ru.javaops.masterjava.xml.util.JaxbUnmarshaller; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; +import javax.xml.bind.JAXBException; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; import java.io.InputStream; @@ -11,16 +15,16 @@ import java.util.List; public class UserProcessor { + private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); - public List process(final InputStream is) throws XMLStreamException { + public List process(final InputStream is) throws XMLStreamException, JAXBException { final StaxStreamProcessor processor = new StaxStreamProcessor(is); List users = new ArrayList<>(); + JaxbUnmarshaller unmarshaller = jaxbParser.createUnmarshaller(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { - final String email = processor.getAttribute("email"); - final UserFlag flag = UserFlag.valueOf(processor.getAttribute("flag")); - final String fullName = processor.getReader().getElementText(); - final User user = new User(fullName, email, flag); + ru.javaops.masterjava.xml.schema.User xmlUser = unmarshaller.unmarshal(processor.getReader(), ru.javaops.masterjava.xml.schema.User.class); + final User user = new User(xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); users.add(user); } return users; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java index d6006800f..7d87f24fe 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbMarshaller.java @@ -18,11 +18,15 @@ public JaxbMarshaller(JAXBContext ctx) throws JAXBException { marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); } - public void setProperty(String prop, Object value) throws PropertyException { - marshaller.setProperty(prop, value); + public void setProperty(String prop, Object value) { + try { + marshaller.setProperty(prop, value); + } catch (PropertyException e) { + throw new IllegalArgumentException(e); + } } - public synchronized void setSchema(Schema schema) { + public void setSchema(Schema schema) { marshaller.setSchema(schema); } @@ -32,8 +36,7 @@ public String marshal(Object instance) throws JAXBException { return sw.toString(); } - public synchronized void marshal(Object instance, Writer writer) throws JAXBException { + public void marshal(Object instance, Writer writer) throws JAXBException { marshaller.marshal(instance, writer); } - } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java index 563d53ba0..8aff55510 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbParser.java @@ -4,28 +4,26 @@ import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; -import javax.xml.bind.PropertyException; -import javax.xml.stream.XMLStreamReader; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; -import java.io.*; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; /** - * Marshalling/Unmarshalling JAXB helper - * XML Facade + * Marshalling/Unmarshalling JAXB facade */ public class JaxbParser { - protected JaxbMarshaller jaxbMarshaller; - protected JaxbUnmarshaller jaxbUnmarshaller; + private JAXBContext ctx; protected Schema schema; public JaxbParser(Class... classesToBeBound) { try { init(JAXBContext.newInstance(classesToBeBound)); } catch (JAXBException e) { - throw new IllegalArgumentException(e); + throw new IllegalStateException(e); } } @@ -34,53 +32,42 @@ public JaxbParser(String context) { try { init(JAXBContext.newInstance(context)); } catch (JAXBException e) { - throw new IllegalArgumentException(e); + throw new IllegalStateException(e); } } - private void init(JAXBContext ctx) throws JAXBException { - jaxbMarshaller = new JaxbMarshaller(ctx); - jaxbUnmarshaller = new JaxbUnmarshaller(ctx); + private void init(JAXBContext ctx) { + this.ctx = ctx; } - // Unmarshaller - public T unmarshal(InputStream is) throws JAXBException { - return (T) jaxbUnmarshaller.unmarshal(is); - } - - public T unmarshal(Reader reader) throws JAXBException { - return (T) jaxbUnmarshaller.unmarshal(reader); - } - - public T unmarshal(String str) throws JAXBException { - return (T) jaxbUnmarshaller.unmarshal(str); - } - - public T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { - return jaxbUnmarshaller.unmarshal(reader, elementClass); - } - - // Marshaller - public void setMarshallerProperty(String prop, Object value) { + // https://stackoverflow.com/a/7400735/548473 + public JaxbMarshaller createMarshaller() { try { - jaxbMarshaller.setProperty(prop, value); - } catch (PropertyException e) { - throw new IllegalArgumentException(e); + JaxbMarshaller marshaller = new JaxbMarshaller(ctx); + if (schema != null) { + marshaller.setSchema(schema); + } + return marshaller; + } catch (JAXBException e) { + throw new IllegalStateException(e); } } - public String marshal(Object instance) throws JAXBException { - return jaxbMarshaller.marshal(instance); - } - - public void marshal(Object instance, Writer writer) throws JAXBException { - jaxbMarshaller.marshal(instance, writer); + // https://stackoverflow.com/a/7400735/548473 + public JaxbUnmarshaller createUnmarshaller() { + try { + JaxbUnmarshaller unmarshaller = new JaxbUnmarshaller(ctx); + if (schema != null) { + unmarshaller.setSchema(schema); + } + return unmarshaller; + } catch (JAXBException e) { + throw new IllegalStateException(e); + } } public void setSchema(Schema schema) { this.schema = schema; - jaxbUnmarshaller.setSchema(schema); - jaxbMarshaller.setSchema(schema); } public void validate(String str) throws IOException, SAXException { diff --git a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java index 1de89d823..bada879ac 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/xml/util/JaxbUnmarshaller.java @@ -16,23 +16,23 @@ public JaxbUnmarshaller(JAXBContext ctx) throws JAXBException { unmarshaller = ctx.createUnmarshaller(); } - public synchronized void setSchema(Schema schema) { + public void setSchema(Schema schema) { unmarshaller.setSchema(schema); } - public synchronized Object unmarshal(InputStream is) throws JAXBException { - return unmarshaller.unmarshal(is); + public T unmarshal(InputStream is) throws JAXBException { + return (T) unmarshaller.unmarshal(is); } - public synchronized Object unmarshal(Reader reader) throws JAXBException { - return unmarshaller.unmarshal(reader); + public T unmarshal(Reader reader) throws JAXBException { + return (T) unmarshaller.unmarshal(reader); } - public Object unmarshal(String str) throws JAXBException { - return unmarshal(new StringReader(str)); + public T unmarshal(String str) throws JAXBException { + return (T) unmarshal(new StringReader(str)); } - public synchronized T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { + public T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { return unmarshaller.unmarshal(reader, elementClass).getValue(); } -} \ No newline at end of file +} diff --git a/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java b/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java index 08db01e0e..ce2e3a902 100644 --- a/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java +++ b/web/upload/src/test/java/ru/javaops/masterjava/MainXml.java @@ -8,10 +8,7 @@ import ru.javaops.masterjava.xml.schema.Payload; import ru.javaops.masterjava.xml.schema.Project; import ru.javaops.masterjava.xml.schema.User; -import ru.javaops.masterjava.xml.util.JaxbParser; -import ru.javaops.masterjava.xml.util.Schemas; -import ru.javaops.masterjava.xml.util.StaxStreamProcessor; -import ru.javaops.masterjava.xml.util.XsltProcessor; +import ru.javaops.masterjava.xml.util.*; import javax.xml.stream.events.XMLEvent; import java.io.InputStream; @@ -59,10 +56,11 @@ public static void main(String[] args) throws Exception { private static Set parseByJaxb(String projectName, URL payloadUrl) throws Exception { JaxbParser parser = new JaxbParser(ObjectFactory.class); + JaxbUnmarshaller unmarshaller = parser.createUnmarshaller(); parser.setSchema(Schemas.ofClasspath("payload.xsd")); Payload payload; try (InputStream is = payloadUrl.openStream()) { - payload = parser.unmarshal(is); + payload = unmarshaller.unmarshal(is); } Project project = StreamEx.of(payload.getProjects().getProject()) @@ -99,11 +97,12 @@ private static Set processByStax(String projectName, URL payloadUrl) throw // Users loop Set users = new TreeSet<>(USER_COMPARATOR); - JaxbParser parser = new JaxbParser(User.class); + JaxbParser parser = new JaxbParser(ObjectFactory.class); + JaxbUnmarshaller unmarshaller = parser.createUnmarshaller(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { String groupRefs = processor.getAttribute("groupRefs"); if (!Collections.disjoint(groupNames, Splitter.on(' ').splitToList(nullToEmpty(groupRefs)))) { - User user = parser.unmarshal(processor.getReader(), User.class); + User user = unmarshaller.unmarshal(processor.getReader(), User.class); users.add(user); } } diff --git a/web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java index 623265428..57d0d8984 100644 --- a/web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java +++ b/web/upload/src/test/java/ru/javaops/masterjava/xml/util/JaxbParserTest.java @@ -10,31 +10,37 @@ import javax.xml.namespace.QName; public class JaxbParserTest { - private static final JaxbParser JAXB_PARSER = new JaxbParser(ObjectFactory.class); + // https://google.github.io/styleguide/javaguide.html#s5.2.4-constant-names + private static final JaxbParser jaxbParser; + private static final JaxbMarshaller marshaller; + private static final JaxbUnmarshaller unmarshaller; static { - JAXB_PARSER.setSchema(Schemas.ofClasspath("payload.xsd")); + jaxbParser = new JaxbParser(ObjectFactory.class); + jaxbParser.setSchema(Schemas.ofClasspath("payload.xsd")); + marshaller = jaxbParser.createMarshaller(); + unmarshaller = jaxbParser.createUnmarshaller(); } @Test public void testPayload() throws Exception { // JaxbParserTest.class.getResourceAsStream("/city.xml") - Payload payload = JAXB_PARSER.unmarshal( + Payload payload = unmarshaller.unmarshal( Resources.getResource("payload.xml").openStream()); - String strPayload = JAXB_PARSER.marshal(payload); - JAXB_PARSER.validate(strPayload); + String strPayload = marshaller.marshal(payload); + jaxbParser.validate(strPayload); System.out.println(strPayload); } @Test public void testCity() throws Exception { - JAXBElement cityElement = JAXB_PARSER.unmarshal( + JAXBElement cityElement = unmarshaller.unmarshal( Resources.getResource("city.xml").openStream()); CityType city = cityElement.getValue(); JAXBElement cityElement2 = new JAXBElement<>(new QName("http://javaops.ru", "City"), CityType.class, city); - String strCity = JAXB_PARSER.marshal(cityElement2); - JAXB_PARSER.validate(strCity); + String strCity = marshaller.marshal(cityElement2); + jaxbParser.validate(strCity); System.out.println(strCity); } -} \ No newline at end of file +} From 3db2f848264f3e492db37fc555e13ba77ca0e296 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:56:23 +0300 Subject: [PATCH 27/87] 4 5 dependencies --- parent/pom.xml | 4 +++- pom.xml | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index d7b056d1d..f0b2702be 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -14,6 +14,8 @@ 1.8 UTF-8 UTF-8 + + false @@ -66,4 +68,4 @@ - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index f0c5cbbb2..43674e646 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,9 @@ https://github.com/JavaOPs/masterjava + parent + parent-web + common test From 2bd9bdc9eee4f99965aa504bfb541901117aacba Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:57:01 +0300 Subject: [PATCH 28/87] 4 6 fix convergence --- parent/pom.xml | 2 +- test/pom.xml | 4 ++-- web/upload/pom.xml | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index f0b2702be..1508835cc 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -50,7 +50,7 @@ one.util streamex - RELEASE + 0.6.6 diff --git a/test/pom.xml b/test/pom.xml index 0b944bf22..1891ca791 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -64,12 +64,12 @@ org.openjdk.jmh jmh-core - RELEASE + 1.19 org.openjdk.jmh jmh-generator-annprocess - RELEASE + 1.19 provided diff --git a/web/upload/pom.xml b/web/upload/pom.xml index 24ef5f3df..3d1d3f5c9 100644 --- a/web/upload/pom.xml +++ b/web/upload/pom.xml @@ -25,6 +25,12 @@ com.j2html j2html 1.2.0 + + + com.google.guava + guava + + From efaf79b19ddd46c9cf2887062030f1d1d28006e7 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:57:13 +0300 Subject: [PATCH 29/87] 4 7 enforcer --- parent/pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/parent/pom.xml b/parent/pom.xml index 1508835cc..2b170e91e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -38,6 +38,24 @@ -Dfile.encoding=UTF-8 + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + enforce + + + + + + + enforce + + + + From 43d88f0de74b72db39e38b22ad34c0f4ecb84aa4 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 10:57:35 +0300 Subject: [PATCH 30/87] 4 8 logging --- config_templates/logback-test.xml | 20 ++++++++++++++++ config_templates/logback.xml | 40 +++++++++++++++++++++++++++++++ parent-web/pom.xml | 13 ++++++++++ parent/pom.xml | 30 +++++++++++++++++++++++ web/common-web/pom.xml | 6 +++++ 5 files changed, 109 insertions(+) create mode 100644 config_templates/logback-test.xml create mode 100644 config_templates/logback.xml diff --git a/config_templates/logback-test.xml b/config_templates/logback-test.xml new file mode 100644 index 000000000..49652c7b7 --- /dev/null +++ b/config_templates/logback-test.xml @@ -0,0 +1,20 @@ + + + + true + + + + + UTF-8 + %-5level %logger{0} [%file:%line] %msg%n + + + + + + + + + + diff --git a/config_templates/logback.xml b/config_templates/logback.xml new file mode 100644 index 000000000..352ee14a0 --- /dev/null +++ b/config_templates/logback.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + ${LOG_DIR}/${project.build.finalName}.log + + UTF-8 + %d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%file:%line] - %msg%n + + + + ${LOG_DIR}/archived/${project.build.finalName}.%d{yyyy-MM-dd}.%i.log + + + 5MB + + + + + + + UTF-8 + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} [%file:%line] - %msg%n + + + + + + + + + + diff --git a/parent-web/pom.xml b/parent-web/pom.xml index d47d5a51b..6c750c423 100644 --- a/parent-web/pom.xml +++ b/parent-web/pom.xml @@ -27,6 +27,19 @@ + + + + ${masterjava.config} + true + + logback.xml + + + + src/main/resources + + diff --git a/parent/pom.xml b/parent/pom.xml index 2b170e91e..b748daa8b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -14,7 +14,11 @@ 1.8 UTF-8 UTF-8 + + 1.2.3 + 1.7.25 + /apps/masterjava/config/ false @@ -57,6 +61,18 @@ + + + + ${masterjava.config} + + logback-test.xml + + + + src/test/resources + + @@ -71,6 +87,20 @@ 0.6.6 + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + + ch.qos.logback + logback-classic + ${logback.version} + + junit diff --git a/web/common-web/pom.xml b/web/common-web/pom.xml index 150c89250..fd0f5eb49 100644 --- a/web/common-web/pom.xml +++ b/web/common-web/pom.xml @@ -32,6 +32,12 @@ org.thymeleaf thymeleaf 3.0.8.RELEASE + + + org.slf4j + slf4j-api + + From adb085d4c6091894747f3c1c593ce1533c2ec7b6 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 11:03:33 +0300 Subject: [PATCH 31/87] 4 9 context --- config_templates/context.xml | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 config_templates/context.xml diff --git a/config_templates/context.xml b/config_templates/context.xml new file mode 100644 index 000000000..40bb8aa0c --- /dev/null +++ b/config_templates/context.xml @@ -0,0 +1,49 @@ + + + + + + + + WEB-INF/web.xml + ${catalina.base}/conf/web.xml + + + + + + + + + From 283d6f7b931002c0459f9b3b0f376bb3f299b3ba Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 11:05:50 +0300 Subject: [PATCH 32/87] 4 10 persist --- config_templates/logback-test.xml | 1 + config_templates/sql/initDB.sql | 15 ++++++ persist/pom.xml | 46 ++++++++++++++++ .../masterjava/persist/DBIProvider.java | 52 +++++++++++++++++++ .../masterjava/persist/dao/AbstractDao.java | 5 ++ .../masterjava/persist/dao/UserDao.java | 37 +++++++++++++ .../masterjava/persist/model/BaseEntity.java | 37 +++++++++++++ .../masterjava/persist}/model/User.java | 41 +++++++++++---- .../masterjava/persist}/model/UserFlag.java | 2 +- .../masterjava/persist/DBITestProvider.java | 20 +++++++ .../masterjava/persist/UserTestData.java | 37 +++++++++++++ .../persist/dao/AbstractDaoTest.java | 16 ++++++ .../masterjava/persist/dao/UserDaoTest.java | 35 +++++++++++++ pom.xml | 1 + web/upload/pom.xml | 5 ++ .../masterjava/upload/UploadServlet.java | 2 +- .../masterjava/upload/UserProcessor.java | 4 +- 17 files changed, 341 insertions(+), 15 deletions(-) create mode 100644 config_templates/sql/initDB.sql create mode 100644 persist/pom.xml create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/AbstractDao.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java rename {web/upload/src/main/java/ru/javaops/masterjava => persist/src/main/java/ru/javaops/masterjava/persist}/model/User.java (69%) rename {web/upload/src/main/java/ru/javaops/masterjava => persist/src/main/java/ru/javaops/masterjava/persist}/model/UserFlag.java (68%) create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java diff --git a/config_templates/logback-test.xml b/config_templates/logback-test.xml index 49652c7b7..fa0c7f8b0 100644 --- a/config_templates/logback-test.xml +++ b/config_templates/logback-test.xml @@ -12,6 +12,7 @@ + diff --git a/config_templates/sql/initDB.sql b/config_templates/sql/initDB.sql new file mode 100644 index 000000000..888ba8141 --- /dev/null +++ b/config_templates/sql/initDB.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS users; +DROP SEQUENCE IF EXISTS user_seq; +DROP TYPE IF EXISTS user_flag; + +CREATE TYPE user_flag AS ENUM ('active', 'deleted', 'superuser'); + +CREATE SEQUENCE user_seq START 100000; + +CREATE TABLE users ( + id INTEGER PRIMARY KEY DEFAULT nextval('user_seq'), + full_name TEXT NOT NULL, + email TEXT NOT NULL, + flag user_flag NOT NULL +); + diff --git a/persist/pom.xml b/persist/pom.xml new file mode 100644 index 000000000..4953ba4d0 --- /dev/null +++ b/persist/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + ru.javaops + parent + ../parent/pom.xml + 1.0-SNAPSHOT + + + persist + 1.0-SNAPSHOT + Persist + + + + ${project.groupId} + common + ${project.version} + + + org.jdbi + jdbi + 2.78 + + + com.bertoncelj.jdbi.entitymapper + jdbi-entity-mapper + 1.0.0 + + + org.jdbi + jdbi + + + + + org.postgresql + postgresql + 42.1.4 + + + diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java b/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java new file mode 100644 index 000000000..7cfaab77f --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java @@ -0,0 +1,52 @@ +package ru.javaops.masterjava.persist; + +import org.skife.jdbi.v2.DBI; +import org.skife.jdbi.v2.logging.SLF4JLog; +import org.skife.jdbi.v2.tweak.ConnectionFactory; +import org.slf4j.Logger; +import ru.javaops.masterjava.persist.dao.AbstractDao; + +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import static org.slf4j.LoggerFactory.getLogger; + +public class DBIProvider { + private static final Logger log = getLogger(DBIProvider.class); + + private volatile static ConnectionFactory connectionFactory = null; + + private static class DBIHolder { + static final DBI jDBI; + + static { + final DBI dbi; + if (connectionFactory != null) { + log.info("Init jDBI with connectionFactory"); + dbi = new DBI(connectionFactory); + } else { + try { + log.info("Init jDBI with JNDI"); + InitialContext ctx = new InitialContext(); + dbi = new DBI((DataSource) ctx.lookup("java:/comp/env/jdbc/masterjava")); + } catch (Exception ex) { + throw new IllegalStateException("PostgreSQL initialization failed", ex); + } + } + jDBI = dbi; + jDBI.setSQLLog(new SLF4JLog()); + } + } + + public static void init(ConnectionFactory connectionFactory) { + DBIProvider.connectionFactory = connectionFactory; + } + + public static DBI getDBI() { + return DBIHolder.jDBI; + } + + public static T getDao(Class daoClass) { + return DBIHolder.jDBI.onDemand(daoClass); + } +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/AbstractDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/AbstractDao.java new file mode 100644 index 000000000..8b3ee1099 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/AbstractDao.java @@ -0,0 +1,5 @@ +package ru.javaops.masterjava.persist.dao; + +public interface AbstractDao { + void clean(); +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java new file mode 100644 index 000000000..e0230cccf --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist.dao; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import org.skife.jdbi.v2.sqlobject.*; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.model.User; + +import java.util.List; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class UserDao implements AbstractDao { + + public User insert(User user) { + if (user.isNew()) { + int id = insertGeneratedId(user); + user.setId(id); + } else { + insertWitId(user); + } + return user; + } + + @SqlUpdate("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS user_flag)) ") + @GetGeneratedKeys + abstract int insertGeneratedId(@BindBean User user); + + @SqlUpdate("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS user_flag)) ") + abstract void insertWitId(@BindBean User user); + + @SqlQuery("SELECT * FROM users ORDER BY full_name, email LIMIT :it") + public abstract List getWithLimit(@Bind int limit); + + // http://stackoverflow.com/questions/13223820/postgresql-delete-all-content + @SqlUpdate("TRUNCATE users") + @Override + public abstract void clean(); +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java new file mode 100644 index 000000000..415d3d037 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist.model; + +abstract public class BaseEntity { + protected BaseEntity() { + } + + protected BaseEntity(Integer id) { + this.id = id; + } + + protected Integer id; + + public Integer getId() { + return id; + } + + protected void setId(Integer id) { + this.id = id; + } + + public boolean isNew() { + return id == null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BaseEntity baseEntity = (BaseEntity) o; + return id != null && id.equals(baseEntity.id); + } + + @Override + public int hashCode() { + return id == null ? 0 : id; + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/model/User.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java similarity index 69% rename from web/upload/src/main/java/ru/javaops/masterjava/model/User.java rename to persist/src/main/java/ru/javaops/masterjava/persist/model/User.java index d7c3f8cd3..02ef1c864 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/model/User.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java @@ -1,28 +1,31 @@ -package ru.javaops.masterjava.model; +package ru.javaops.masterjava.persist.model; + +import com.bertoncelj.jdbi.entitymapper.Column; import java.util.Objects; -public class User { - private final Integer id; - private final String fullName; - private final String email; - private final UserFlag flag; +public class User extends BaseEntity { + @Column("full_name") + private String fullName; + + private String email; + + private UserFlag flag; + + public User() { + } public User(String fullName, String email, UserFlag flag) { this(null, fullName, email, flag); } public User(Integer id, String fullName, String email, UserFlag flag) { - this.id = id; + super(id); this.fullName = fullName; this.email = email; this.flag = flag; } - public Integer getId() { - return id; - } - public String getFullName() { return fullName; } @@ -35,6 +38,22 @@ public UserFlag getFlag() { return flag; } + public void setId(Integer id) { + this.id = id; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setFlag(UserFlag flag) { + this.flag = flag; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java similarity index 68% rename from web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java rename to persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java index 603626ae1..bc2f69183 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/model/UserFlag.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java @@ -1,4 +1,4 @@ -package ru.javaops.masterjava.model; +package ru.javaops.masterjava.persist.model; /** * gkislin diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java b/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java new file mode 100644 index 000000000..78e4e6d8f --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java @@ -0,0 +1,20 @@ +package ru.javaops.masterjava.persist; + +import java.sql.DriverManager; + +public class DBITestProvider { + public static void initDBI() { + initDBI("jdbc:postgresql://localhost:5432/masterjava", "user", "password"); + } + + public static void initDBI(String dbUrl, String dbUser, String dbPassword) { + DBIProvider.init(() -> { + try { + Class.forName("org.postgresql.Driver"); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("PostgreSQL driver not found", e); + } + return DriverManager.getConnection(dbUrl, dbUser, dbPassword); + }); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java new file mode 100644 index 000000000..7c13c0499 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist; + +import com.google.common.collect.ImmutableList; +import ru.javaops.masterjava.persist.dao.UserDao; +import ru.javaops.masterjava.persist.model.User; +import ru.javaops.masterjava.persist.model.UserFlag; + +import java.util.List; + +public class UserTestData { + public static User ADMIN; + public static User DELETED; + public static User FULL_NAME; + public static User USER1; + public static User USER2; + public static User USER3; + public static List FIST5_USERS; + + public static void init() { + ADMIN = new User("Admin", "admin@javaops.ru", UserFlag.superuser); + DELETED = new User("Deleted", "deleted@yandex.ru", UserFlag.deleted); + FULL_NAME = new User("Full Name", "gmail@gmail.com", UserFlag.active); + USER1 = new User("User1", "user1@gmail.com", UserFlag.active); + USER2 = new User("User2", "user2@yandex.ru", UserFlag.active); + USER3 = new User("User3", "user3@yandex.ru", UserFlag.active); + FIST5_USERS = ImmutableList.of(ADMIN, DELETED, FULL_NAME, USER1, USER2); + } + + public static void setUp() { + UserDao dao = DBIProvider.getDao(UserDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + FIST5_USERS.forEach(dao::insert); + dao.insert(USER3); + }); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java new file mode 100644 index 000000000..a65302fdd --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java @@ -0,0 +1,16 @@ +package ru.javaops.masterjava.persist.dao; + +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.persist.DBITestProvider; + +public abstract class AbstractDaoTest { + static { + DBITestProvider.initDBI(); + } + + protected DAO dao; + + protected AbstractDaoTest(Class daoClass) { + this.dao = DBIProvider.getDao(daoClass); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java new file mode 100644 index 000000000..39caffd82 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java @@ -0,0 +1,35 @@ +package ru.javaops.masterjava.persist.dao; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import ru.javaops.masterjava.persist.UserTestData; +import ru.javaops.masterjava.persist.model.User; + +import java.util.List; + +import static ru.javaops.masterjava.persist.UserTestData.FIST5_USERS; + +public class UserDaoTest extends AbstractDaoTest { + + public UserDaoTest() { + super(UserDao.class); + } + + @BeforeClass + public static void init() throws Exception { + UserTestData.init(); + } + + @Before + public void setUp() throws Exception { + UserTestData.setUp(); + } + + @Test + public void getWithLimit() { + List users = dao.getWithLimit(5); + Assert.assertEquals(FIST5_USERS, users); + } +} diff --git a/pom.xml b/pom.xml index 43674e646..c7e9c65d2 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ parent-web common + persist test web diff --git a/web/upload/pom.xml b/web/upload/pom.xml index 3d1d3f5c9..a73113dae 100644 --- a/web/upload/pom.xml +++ b/web/upload/pom.xml @@ -21,6 +21,11 @@ + + ${project.groupId} + persist + ${project.version} + com.j2html j2html diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java index 3831eda27..a2b583aa3 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -1,7 +1,7 @@ package ru.javaops.masterjava.upload; import org.thymeleaf.context.WebContext; -import ru.javaops.masterjava.model.User; +import ru.javaops.masterjava.persist.model.User; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index f8c574bf8..5cb3deb56 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -1,7 +1,7 @@ package ru.javaops.masterjava.upload; -import ru.javaops.masterjava.model.User; -import ru.javaops.masterjava.model.UserFlag; +import ru.javaops.masterjava.persist.model.User; +import ru.javaops.masterjava.persist.model.UserFlag; import ru.javaops.masterjava.xml.schema.ObjectFactory; import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.JaxbUnmarshaller; From 2169997750069cba4ffd6330943e5162cb1aa9a2 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 25 Feb 2019 11:06:58 +0300 Subject: [PATCH 33/87] Preparing to multimodule system --- web/upload/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web/upload/pom.xml b/web/upload/pom.xml index a73113dae..ddfd39c1a 100644 --- a/web/upload/pom.xml +++ b/web/upload/pom.xml @@ -37,6 +37,12 @@ + + org.thymeleaf + thymeleaf + 3.0.11.RELEASE + compile + From 2af68d7f30e1bc5bfc32c3307998175bbe7b6cf1 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:41:15 +0300 Subject: [PATCH 34/87] 5 1 HW4 upload chunk --- parent-web/pom.xml | 41 +++++++++++ .../masterjava/persist/dao/UserDao.java | 30 +++++++- .../masterjava/persist/dao/UserDaoTest.java | 14 ++++ .../masterjava/upload/UploadServlet.java | 44 +++++++---- .../masterjava/upload/UserProcessor.java | 73 +++++++++++++++++-- .../main/webapp/WEB-INF/templates/upload.html | 8 +- 6 files changed, 186 insertions(+), 24 deletions(-) diff --git a/parent-web/pom.xml b/parent-web/pom.xml index 6c750c423..6eb7245ae 100644 --- a/parent-web/pom.xml +++ b/parent-web/pom.xml @@ -26,6 +26,47 @@ false + + + + org.codehaus.cargo + cargo-maven2-plugin + 1.6.5 + + + tomcat8x + + UTF-8 + + + + org.postgresql + postgresql + + + + + + + ${masterjava.config}/context.xml + conf/Catalina/localhost/ + context.xml.default + + + + + + ${project.groupId} + ${project.artifactId} + war + + ${project.build.finalName} + + + + + + diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java index e0230cccf..9f75c73a7 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java @@ -2,6 +2,7 @@ import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; import org.skife.jdbi.v2.sqlobject.*; +import org.skife.jdbi.v2.sqlobject.customizers.BatchChunkSize; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; import ru.javaops.masterjava.persist.model.User; @@ -20,11 +21,21 @@ public User insert(User user) { return user; } - @SqlUpdate("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS user_flag)) ") + @SqlQuery("SELECT nextval('user_seq')") + abstract int getNextVal(); + + @Transaction + public int getSeqAndSkip(int step) { + int id = getNextVal(); + DBIProvider.getDBI().useHandle(h -> h.execute("ALTER SEQUENCE user_seq RESTART WITH " + (id + step))); + return id; + } + + @SqlUpdate("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG)) ") @GetGeneratedKeys abstract int insertGeneratedId(@BindBean User user); - @SqlUpdate("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS user_flag)) ") + @SqlUpdate("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG)) ") abstract void insertWitId(@BindBean User user); @SqlQuery("SELECT * FROM users ORDER BY full_name, email LIMIT :it") @@ -34,4 +45,19 @@ public User insert(User user) { @SqlUpdate("TRUNCATE users") @Override public abstract void clean(); + + // https://habrahabr.ru/post/264281/ + @SqlBatch("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG))" + + "ON CONFLICT DO NOTHING") +// "ON CONFLICT (email) DO UPDATE SET full_name=:fullName, flag=CAST(:flag AS USER_FLAG)") + public abstract int[] insertBatch(@BindBean List users, @BatchChunkSize int chunkSize); + + + public List insertAndGetConflictEmails(List users) { + int[] result = insertBatch(users, users.size()); + return IntStreamEx.range(0, users.size()) + .filter(i -> result[i] == 0) + .mapToObj(index -> users.get(index).getEmail()) + .toList(); + } } diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java index 39caffd82..ad5212808 100644 --- a/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserDaoTest.java @@ -32,4 +32,18 @@ public void getWithLimit() { List users = dao.getWithLimit(5); Assert.assertEquals(FIST5_USERS, users); } + + @Test + public void insertBatch() throws Exception { + dao.clean(); + dao.insertBatch(FIST5_USERS, 3); + Assert.assertEquals(5, dao.getWithLimit(100).size()); + } + + @Test + public void getSeqAndSkip() throws Exception { + int seq1 = dao.getSeqAndSkip(5); + int seq2 = dao.getSeqAndSkip(1); + Assert.assertEquals(5, seq2 - seq1); + } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java index a2b583aa3..37e7e612a 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -1,5 +1,7 @@ package ru.javaops.masterjava.upload; +import com.google.common.collect.ImmutableMap; +import lombok.extern.slf4j.Slf4j; import org.thymeleaf.context.WebContext; import ru.javaops.masterjava.persist.model.User; @@ -19,33 +21,47 @@ @WebServlet(urlPatterns = "/", loadOnStartup = 1) @MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit public class UploadServlet extends HttpServlet { + private static final int CHUNK_SIZE = 2000; private final UserProcessor userProcessor = new UserProcessor(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); - engine.process("upload", webContext, resp.getWriter()); + out(req, resp, "", CHUNK_SIZE); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); - + String message; + int chunkSize = CHUNK_SIZE; try { // http://docs.oracle.com/javaee/6/tutorial/doc/glraq.html - Part filePart = req.getPart("fileToUpload"); - if (filePart.getSize() == 0) { - throw new IllegalStateException("Upload file have not been selected"); - } - try (InputStream is = filePart.getInputStream()) { - List users = userProcessor.process(is); - webContext.setVariable("users", users); - engine.process("result", webContext, resp.getWriter()); + chunkSize = Integer.parseInt(req.getParameter("chunkSize")); + if (chunkSize < 1) { + message = "Chunk Size must be > 1"; + } else { + Part filePart = req.getPart("fileToUpload"); + try (InputStream is = filePart.getInputStream()) { + List failed = userProcessor.process(is, chunkSize); + log.info("Failed users: " + failed); + final WebContext webContext = + new WebContext(req, resp, req.getServletContext(), req.getLocale(), + ImmutableMap.of("users", failed)); + engine.process("result", webContext, resp.getWriter()); + return; + } } } catch (Exception e) { - webContext.setVariable("exception", e); - engine.process("exception", webContext, resp.getWriter()); + log.info(e.getMessage(), e); + message = e.toString(); } + out(req, resp, message, chunkSize); + } + + private void out(HttpServletRequest req, HttpServletResponse resp, String message, int chunkSize) throws IOException { + resp.setCharacterEncoding("utf-8"); + final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale(), + ImmutableMap.of("message", message, "chunkSize", chunkSize)); + engine.process("upload", webContext, resp.getWriter()); } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 5cb3deb56..d0b12b3fd 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -1,5 +1,10 @@ package ru.javaops.masterjava.upload; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; import ru.javaops.masterjava.persist.model.UserFlag; import ru.javaops.masterjava.xml.schema.ObjectFactory; @@ -16,17 +21,71 @@ public class UserProcessor { private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); + private static UserDao userDao = DBIProvider.getDao(UserDao.class); - public List process(final InputStream is) throws XMLStreamException, JAXBException { - final StaxStreamProcessor processor = new StaxStreamProcessor(is); - List users = new ArrayList<>(); + private ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_THREADS); + + @AllArgsConstructor + public static class FailedEmails { + public String emailsOrRange; + public String reason; + + @Override + public String toString() { + return emailsOrRange + " : " + reason; + } + } + + /* + * return failed users chunks + */ + public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { + log.info("Start processing with chunkSize=" + chunkSize); + + Map>> chunkFutures = new LinkedHashMap<>(); // ordered map (emailRange -> chunk future) + + int id = userDao.getSeqAndSkip(chunkSize); + List chunk = new ArrayList<>(chunkSize); + val processor = new StaxStreamProcessor(is); + val unmarshaller = jaxbParser.createUnmarshaller(); - JaxbUnmarshaller unmarshaller = jaxbParser.createUnmarshaller(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { ru.javaops.masterjava.xml.schema.User xmlUser = unmarshaller.unmarshal(processor.getReader(), ru.javaops.masterjava.xml.schema.User.class); - final User user = new User(xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); - users.add(user); + final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); + chunk.add(user); + if (chunk.size() == chunkSize) { + addChunkFutures(chunkFutures, chunk); + chunk = new ArrayList<>(chunkSize); + id = userDao.getSeqAndSkip(chunkSize); + } } - return users; + + if (!chunk.isEmpty()) { + addChunkFutures(chunkFutures, chunk); + } + + List failed = new ArrayList<>(); + List allAlreadyPresents = new ArrayList<>(); + chunkFutures.forEach((emailRange, future) -> { + try { + List alreadyPresentsInChunk = future.get(); + log.info("{} successfully executed with already presents: {}", emailRange, alreadyPresentsInChunk); + allAlreadyPresents.addAll(alreadyPresentsInChunk); + } catch (InterruptedException | ExecutionException e) { + log.error(emailRange + " failed", e); + failed.add(new FailedEmails(emailRange, e.toString())); + } + }); + if (!allAlreadyPresents.isEmpty()) { + failed.add(new FailedEmails(allAlreadyPresents.toString(), "already presents")); + } + return failed; + } + + private void addChunkFutures(Map>> chunkFutures, List chunk) { + String emailRange = String.format("[%s-%s]", chunk.get(0).getEmail(), chunk.get(chunk.size() - 1).getEmail()); + Future> future = executorService.submit(() -> userDao.insertAndGetConflictEmails(chunk)); + chunkFutures.put(emailRange, future); + log.info("Submit chunk: " + emailRange); } } diff --git a/web/upload/src/main/webapp/WEB-INF/templates/upload.html b/web/upload/src/main/webapp/WEB-INF/templates/upload.html index bd2abb33b..c4c2641ee 100644 --- a/web/upload/src/main/webapp/WEB-INF/templates/upload.html +++ b/web/upload/src/main/webapp/WEB-INF/templates/upload.html @@ -1,12 +1,18 @@ + Upload XML
+ +

Message

Select xml file to upload

-
+ + +

+

From a2a1064da86d5422a53bf954a21a19b69d76254e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:42:58 +0300 Subject: [PATCH 35/87] 5 2 HW4 webapp users --- web/webapp/pom.xml | 2 +- .../masterjava/webapp/UsersServlet.java | 27 +++++++++++++++++++ .../main/webapp/WEB-INF/templates/users.html | 27 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 web/webapp/src/main/java/ru/javaops/masterjava/webapp/UsersServlet.java create mode 100644 web/webapp/src/main/webapp/WEB-INF/templates/users.html diff --git a/web/webapp/pom.xml b/web/webapp/pom.xml index cf0983cc6..b543f44ec 100644 --- a/web/webapp/pom.xml +++ b/web/webapp/pom.xml @@ -23,7 +23,7 @@ ${project.groupId} - common + persist ${project.version} diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/UsersServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/UsersServlet.java new file mode 100644 index 000000000..5b587128f --- /dev/null +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/UsersServlet.java @@ -0,0 +1,27 @@ +package ru.javaops.masterjava.webapp; + +import com.google.common.collect.ImmutableMap; +import org.thymeleaf.context.WebContext; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.persist.dao.UserDao; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static ru.javaops.masterjava.common.web.ThymeleafListener.engine; + +@WebServlet("") +public class UsersServlet extends HttpServlet { + private UserDao userDao = DBIProvider.getDao(UserDao.class); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale(), + ImmutableMap.of("users", userDao.getWithLimit(20))); + engine.process("users", webContext, resp.getWriter()); + } +} diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html new file mode 100644 index 000000000..c03ddba96 --- /dev/null +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -0,0 +1,27 @@ + + + + Users + + + + + + + + + + + + + + + + + + + + +
#Full NameEmailFlag
+ + From cac1a8c213fa0aff899405c71610f1e99c977a8e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:43:10 +0300 Subject: [PATCH 36/87] 5 3 HW4 already present --- config_templates/sql/initDB.sql | 1 + .../main/webapp/WEB-INF/templates/result.html | 25 +++++-------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/config_templates/sql/initDB.sql b/config_templates/sql/initDB.sql index 888ba8141..287db70c0 100644 --- a/config_templates/sql/initDB.sql +++ b/config_templates/sql/initDB.sql @@ -13,3 +13,4 @@ CREATE TABLE users ( flag user_flag NOT NULL ); +CREATE UNIQUE INDEX email_idx ON users (email); diff --git a/web/upload/src/main/webapp/WEB-INF/templates/result.html b/web/upload/src/main/webapp/WEB-INF/templates/result.html index b8a274c0f..7fe855cf3 100644 --- a/web/upload/src/main/webapp/WEB-INF/templates/result.html +++ b/web/upload/src/main/webapp/WEB-INF/templates/result.html @@ -1,27 +1,14 @@ - Uploaded users + Failed users

Upload XML

-

Uploaded users

- - - - - - - - - - - - - - - - -
Full NameEmailFlag
+

Failed users

+
    + +
  • +
From 990a5c1c1746f4a72bb7985e5d0f5ca579a84eef Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:43:22 +0300 Subject: [PATCH 37/87] 5 4 HW4 parallel --- .../java/ru/javaops/masterjava/persist/dao/UserDao.java | 2 ++ .../java/ru/javaops/masterjava/upload/UploadServlet.java | 1 - .../java/ru/javaops/masterjava/upload/UserProcessor.java | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java index 9f75c73a7..8932950e8 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java @@ -1,9 +1,11 @@ package ru.javaops.masterjava.persist.dao; import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import one.util.streamex.IntStreamEx; import org.skife.jdbi.v2.sqlobject.*; import org.skife.jdbi.v2.sqlobject.customizers.BatchChunkSize; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.model.User; import java.util.List; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java index 37e7e612a..a852ae9ce 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -3,7 +3,6 @@ import com.google.common.collect.ImmutableMap; import lombok.extern.slf4j.Slf4j; import org.thymeleaf.context.WebContext; -import ru.javaops.masterjava.persist.model.User; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index d0b12b3fd..3f428561a 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -18,8 +18,15 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; public class UserProcessor { + private static final int NUMBER_THREADS = 4; + private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); private static UserDao userDao = DBIProvider.getDao(UserDao.class); From fac599fc4f255dd55b84816bfd9c0e64be9b8ea6 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:43:34 +0300 Subject: [PATCH 38/87] 5 5 HW4 parallel2 --- .../main/java/ru/javaops/masterjava/upload/UserProcessor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 3f428561a..c8e72370c 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -17,6 +17,7 @@ import javax.xml.stream.events.XMLEvent; import java.io.InputStream; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; From 242cc571bdf4307107692a287d925bcf50406bd0 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:43:46 +0300 Subject: [PATCH 39/87] 5 6 typesafe config --- common/pom.xml | 32 ++++++++++++++++++- .../ru/javaops/masterjava/config/Configs.java | 19 +++++++++++ parent/pom.xml | 26 +++------------ persist/src/main/resources/persist.conf | 7 ++++ .../masterjava/persist/DBITestProvider.java | 6 +++- 5 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 common/src/main/java/ru/javaops/masterjava/config/Configs.java create mode 100644 persist/src/main/resources/persist.conf diff --git a/common/pom.xml b/common/pom.xml index 33d3da8ed..15b746386 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -17,5 +17,35 @@ Common + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + com.google.guava + guava + 23.3-jre + + + one.util + streamex + 0.6.6 + + + + com.typesafe + config + 1.3.2 + - \ No newline at end of file + diff --git a/common/src/main/java/ru/javaops/masterjava/config/Configs.java b/common/src/main/java/ru/javaops/masterjava/config/Configs.java new file mode 100644 index 000000000..d11483da2 --- /dev/null +++ b/common/src/main/java/ru/javaops/masterjava/config/Configs.java @@ -0,0 +1,19 @@ +package ru.javaops.masterjava.config; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; + +/** + * gkislin + * 01.11.2016 + */ +public class Configs { + + public static Config getConfig(String resource) { + return ConfigFactory.parseResources(resource).resolve(); + } + + public static Config getConfig(String resource, String domain) { + return getConfig(resource).getConfig(domain); + } +} diff --git a/parent/pom.xml b/parent/pom.xml index b748daa8b..09839f9da 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -77,28 +77,10 @@ - com.google.guava - guava - 23.3-jre - - - one.util - streamex - 0.6.6 - - - - - org.slf4j - jcl-over-slf4j - ${slf4j.version} - runtime - - - - ch.qos.logback - logback-classic - ${logback.version} + org.projectlombok + lombok + 1.16.18 + provided diff --git a/persist/src/main/resources/persist.conf b/persist/src/main/resources/persist.conf new file mode 100644 index 000000000..fe150292e --- /dev/null +++ b/persist/src/main/resources/persist.conf @@ -0,0 +1,7 @@ +db { + url = "jdbc:postgresql://localhost:5432/masterjava" + user = user + password = password +} + +include required(file("/apps/masterjava/config/persist.conf")) diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java b/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java index 78e4e6d8f..eb9605066 100644 --- a/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java +++ b/persist/src/test/java/ru/javaops/masterjava/persist/DBITestProvider.java @@ -1,10 +1,14 @@ package ru.javaops.masterjava.persist; +import com.typesafe.config.Config; +import ru.javaops.masterjava.config.Configs; + import java.sql.DriverManager; public class DBITestProvider { public static void initDBI() { - initDBI("jdbc:postgresql://localhost:5432/masterjava", "user", "password"); + Config db = Configs.getConfig("persist.conf","db"); + initDBI(db.getString("url"), db.getString("user"), db.getString("password")); } public static void initDBI(String dbUrl, String dbUser, String dbPassword) { From a57154dfb13e14206cf432ea84d0bc7566bac394 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Mon, 1 Jul 2019 07:43:58 +0300 Subject: [PATCH 40/87] 5 7 lombok --- .../masterjava/persist/DBIProvider.java | 6 +- .../masterjava/persist/model/BaseEntity.java | 21 ++--- .../masterjava/persist/model/User.java | 82 +++---------------- .../masterjava/upload/UploadServlet.java | 1 + .../masterjava/upload/UserProcessor.java | 2 +- 5 files changed, 21 insertions(+), 91 deletions(-) diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java b/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java index 7cfaab77f..f3d115dcf 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/DBIProvider.java @@ -1,18 +1,16 @@ package ru.javaops.masterjava.persist; +import lombok.extern.slf4j.Slf4j; import org.skife.jdbi.v2.DBI; import org.skife.jdbi.v2.logging.SLF4JLog; import org.skife.jdbi.v2.tweak.ConnectionFactory; -import org.slf4j.Logger; import ru.javaops.masterjava.persist.dao.AbstractDao; import javax.naming.InitialContext; import javax.sql.DataSource; -import static org.slf4j.LoggerFactory.getLogger; - +@Slf4j public class DBIProvider { - private static final Logger log = getLogger(DBIProvider.class); private volatile static ConnectionFactory connectionFactory = null; diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java index 415d3d037..ee44b5f71 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java @@ -1,23 +1,16 @@ package ru.javaops.masterjava.persist.model; -abstract public class BaseEntity { - protected BaseEntity() { - } +import lombok.*; - protected BaseEntity(Integer id) { - this.id = id; - } +@NoArgsConstructor +@AllArgsConstructor +@ToString +abstract public class BaseEntity { + @Getter + @Setter protected Integer id; - public Integer getId() { - return id; - } - - protected void setId(Integer id) { - this.id = id; - } - public boolean isNew() { return id == null; } diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java index 02ef1c864..030a40b4d 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java @@ -1,82 +1,20 @@ package ru.javaops.masterjava.persist.model; import com.bertoncelj.jdbi.entitymapper.Column; +import lombok.*; -import java.util.Objects; - +@Data +@RequiredArgsConstructor +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor public class User extends BaseEntity { @Column("full_name") - private String fullName; - - private String email; - - private UserFlag flag; - - public User() { - } - - public User(String fullName, String email, UserFlag flag) { - this(null, fullName, email, flag); - } + private @NonNull String fullName; + private @NonNull String email; + private @NonNull UserFlag flag; public User(Integer id, String fullName, String email, UserFlag flag) { - super(id); - this.fullName = fullName; - this.email = email; - this.flag = flag; - } - - public String getFullName() { - return fullName; - } - - public String getEmail() { - return email; - } - - public UserFlag getFlag() { - return flag; - } - - public void setId(Integer id) { - this.id = id; - } - - public void setFullName(String fullName) { - this.fullName = fullName; - } - - public void setEmail(String email) { - this.email = email; - } - - public void setFlag(UserFlag flag) { - this.flag = flag; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - User user = (User) o; - return Objects.equals(id, user.id) && - Objects.equals(fullName, user.fullName) && - Objects.equals(email, user.email) && - flag == user.flag; - } - - @Override - public int hashCode() { - return Objects.hash(id, fullName, email, flag); - } - - @Override - public String toString() { - return "User (" + - "id=" + id + - ", fullName='" + fullName + '\'' + - ", email='" + email + '\'' + - ", flag=" + flag + - ')'; + this(fullName, email, flag); + this.id=id; } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java index a852ae9ce..732c25cb1 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -19,6 +19,7 @@ @WebServlet(urlPatterns = "/", loadOnStartup = 1) @MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit +@Slf4j public class UploadServlet extends HttpServlet { private static final int CHUNK_SIZE = 2000; diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index c8e72370c..d7b52ef30 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -9,7 +9,6 @@ import ru.javaops.masterjava.persist.model.UserFlag; import ru.javaops.masterjava.xml.schema.ObjectFactory; import ru.javaops.masterjava.xml.util.JaxbParser; -import ru.javaops.masterjava.xml.util.JaxbUnmarshaller; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; import javax.xml.bind.JAXBException; @@ -25,6 +24,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +@Slf4j public class UserProcessor { private static final int NUMBER_THREADS = 4; From 4878cd639df6aef21d11a677e490db47e3db3477 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 13 Jul 2019 14:08:07 +0300 Subject: [PATCH 41/87] 6 1 HW5 model sql --- .../masterjava/persist/dao/UserDao.java | 8 ++--- .../masterjava/persist/model/BaseEntity.java | 21 +++--------- .../masterjava/persist/model/City.java | 16 +++++++++ .../masterjava/persist/model/Group.java | 17 ++++++++++ .../masterjava/persist/model/Project.java | 14 ++++++++ .../masterjava/persist/model/RefEntity.java | 12 +++++++ .../masterjava/persist/model/User.java | 12 ++++--- .../masterjava/persist/model/UserGroup.java | 15 ++++++++ .../persist/model/type/GroupType.java | 7 ++++ .../persist/model/{ => type}/UserFlag.java | 2 +- .../masterjava/persist/UserTestData.java | 14 ++++---- sql/databaseChangeLog.sql | 34 +++++++++++++++++++ {config_templates/sql => sql}/initDB.sql | 0 sql/lb_apply.bat | 8 +++++ .../masterjava/upload/UserProcessor.java | 4 +-- 15 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/City.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/Group.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/Project.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/RefEntity.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/UserGroup.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/model/type/GroupType.java rename persist/src/main/java/ru/javaops/masterjava/persist/model/{ => type}/UserFlag.java (66%) create mode 100644 sql/databaseChangeLog.sql rename {config_templates/sql => sql}/initDB.sql (100%) create mode 100644 sql/lb_apply.bat diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java index 8932950e8..c57c4f2e2 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java @@ -33,23 +33,23 @@ public int getSeqAndSkip(int step) { return id; } - @SqlUpdate("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG)) ") + @SqlUpdate("INSERT INTO users (full_name, email, flag, city_ref) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG), :cityRef) ") @GetGeneratedKeys abstract int insertGeneratedId(@BindBean User user); - @SqlUpdate("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG)) ") + @SqlUpdate("INSERT INTO users (id, full_name, email, flag, city_ref) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG), :cityRef) ") abstract void insertWitId(@BindBean User user); @SqlQuery("SELECT * FROM users ORDER BY full_name, email LIMIT :it") public abstract List getWithLimit(@Bind int limit); // http://stackoverflow.com/questions/13223820/postgresql-delete-all-content - @SqlUpdate("TRUNCATE users") + @SqlUpdate("TRUNCATE users CASCADE") @Override public abstract void clean(); // https://habrahabr.ru/post/264281/ - @SqlBatch("INSERT INTO users (id, full_name, email, flag) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG))" + + @SqlBatch("INSERT INTO users (id, full_name, email, flag, city_ref) VALUES (:id, :fullName, :email, CAST(:flag AS USER_FLAG), :cityRef)" + "ON CONFLICT DO NOTHING") // "ON CONFLICT (email) DO UPDATE SET full_name=:fullName, flag=CAST(:flag AS USER_FLAG)") public abstract int[] insertBatch(@BindBean List users, @BatchChunkSize int chunkSize); diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java index ee44b5f71..83ed0a9f2 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/BaseEntity.java @@ -1,30 +1,17 @@ package ru.javaops.masterjava.persist.model; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data @NoArgsConstructor @AllArgsConstructor -@ToString abstract public class BaseEntity { - @Getter - @Setter protected Integer id; public boolean isNew() { return id == null; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BaseEntity baseEntity = (BaseEntity) o; - return id != null && id.equals(baseEntity.id); - } - - @Override - public int hashCode() { - return id == null ? 0 : id; - } } diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/City.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/City.java new file mode 100644 index 000000000..61b197c83 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/City.java @@ -0,0 +1,16 @@ +package ru.javaops.masterjava.persist.model; + +import lombok.*; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class City extends RefEntity { + public City(String ref, String name) { + super(ref); + this.name = name; + } + + @NonNull private String name; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/Group.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/Group.java new file mode 100644 index 000000000..71a648731 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/Group.java @@ -0,0 +1,17 @@ +package ru.javaops.masterjava.persist.model; + +import com.bertoncelj.jdbi.entitymapper.Column; +import lombok.*; +import ru.javaops.masterjava.persist.model.type.GroupType; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class Group extends BaseEntity { + + @NonNull private String name; + @NonNull private GroupType type; + @NonNull @Column("project_id") private int projectId; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/Project.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/Project.java new file mode 100644 index 000000000..c040121e4 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/Project.java @@ -0,0 +1,14 @@ +package ru.javaops.masterjava.persist.model; + +import lombok.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class Project extends BaseEntity { + + @NonNull private String name; + @NonNull private String description; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/RefEntity.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/RefEntity.java new file mode 100644 index 000000000..249189604 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/RefEntity.java @@ -0,0 +1,12 @@ +package ru.javaops.masterjava.persist.model; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode +abstract public class RefEntity { + @Getter + @NonNull private String ref; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java index 030a40b4d..53bb87273 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/User.java @@ -2,19 +2,23 @@ import com.bertoncelj.jdbi.entitymapper.Column; import lombok.*; +import ru.javaops.masterjava.persist.model.type.UserFlag; @Data -@RequiredArgsConstructor -@EqualsAndHashCode(callSuper = true) @NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public class User extends BaseEntity { @Column("full_name") private @NonNull String fullName; private @NonNull String email; private @NonNull UserFlag flag; + @Column("city_ref") + private @NonNull String cityRef; - public User(Integer id, String fullName, String email, UserFlag flag) { - this(fullName, email, flag); + public User(Integer id, String fullName, String email, UserFlag flag, String cityRef) { + this(fullName, email, flag, cityRef); this.id=id; } } diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/UserGroup.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/UserGroup.java new file mode 100644 index 000000000..fc98f8b99 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/UserGroup.java @@ -0,0 +1,15 @@ +package ru.javaops.masterjava.persist.model; + +import com.bertoncelj.jdbi.entitymapper.Column; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserGroup { + @NonNull @Column("user_id") private Integer userId; + @NonNull @Column("group_id") private Integer groupId; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/type/GroupType.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/type/GroupType.java new file mode 100644 index 000000000..8750d8613 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/type/GroupType.java @@ -0,0 +1,7 @@ +package ru.javaops.masterjava.persist.model.type; + +public enum GroupType { + REGISTERING, + CURRENT, + FINISHED; +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java b/persist/src/main/java/ru/javaops/masterjava/persist/model/type/UserFlag.java similarity index 66% rename from persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java rename to persist/src/main/java/ru/javaops/masterjava/persist/model/type/UserFlag.java index bc2f69183..0aff2a46d 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/model/UserFlag.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/model/type/UserFlag.java @@ -1,4 +1,4 @@ -package ru.javaops.masterjava.persist.model; +package ru.javaops.masterjava.persist.model.type; /** * gkislin diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java index 7c13c0499..b5d7fad1e 100644 --- a/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java +++ b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java @@ -3,7 +3,7 @@ import com.google.common.collect.ImmutableList; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; -import ru.javaops.masterjava.persist.model.UserFlag; +import ru.javaops.masterjava.persist.model.type.UserFlag; import java.util.List; @@ -17,12 +17,12 @@ public class UserTestData { public static List FIST5_USERS; public static void init() { - ADMIN = new User("Admin", "admin@javaops.ru", UserFlag.superuser); - DELETED = new User("Deleted", "deleted@yandex.ru", UserFlag.deleted); - FULL_NAME = new User("Full Name", "gmail@gmail.com", UserFlag.active); - USER1 = new User("User1", "user1@gmail.com", UserFlag.active); - USER2 = new User("User2", "user2@yandex.ru", UserFlag.active); - USER3 = new User("User3", "user3@yandex.ru", UserFlag.active); + ADMIN = new User("Admin", "admin@javaops.ru", UserFlag.superuser, null); + DELETED = new User("Deleted", "deleted@yandex.ru", UserFlag.deleted, null); + FULL_NAME = new User("Full Name", "gmail@gmail.com", UserFlag.active, null); + USER1 = new User("User1", "user1@gmail.com", UserFlag.active, null); + USER2 = new User("User2", "user2@yandex.ru", UserFlag.active, null); + USER3 = new User("User3", "user3@yandex.ru", UserFlag.active, null); FIST5_USERS = ImmutableList.of(ADMIN, DELETED, FULL_NAME, USER1, USER2); } diff --git a/sql/databaseChangeLog.sql b/sql/databaseChangeLog.sql new file mode 100644 index 000000000..60ec863d9 --- /dev/null +++ b/sql/databaseChangeLog.sql @@ -0,0 +1,34 @@ +--liquibase formatted sql + +--changeset gkislin:1 +CREATE SEQUENCE common_seq START 100000; + +CREATE TABLE city ( + ref TEXT PRIMARY KEY, + name TEXT NOT NULL +); + +ALTER TABLE users + ADD COLUMN city_ref TEXT REFERENCES city (ref) ON UPDATE CASCADE; + +--changeset gkislin:2 +CREATE TABLE project ( + id INTEGER PRIMARY KEY DEFAULT nextval('common_seq'), + name TEXT UNIQUE NOT NULL, + description TEXT +); + +CREATE TYPE GROUP_TYPE AS ENUM ('REGISTERING', 'CURRENT', 'FINISHED'); + +CREATE TABLE groups ( + id INTEGER PRIMARY KEY DEFAULT nextval('common_seq'), + name TEXT UNIQUE NOT NULL, + type GROUP_TYPE NOT NULL, + project_id INTEGER NOT NULL REFERENCES project (id) +); + +CREATE TABLE user_group ( + user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, + group_id INTEGER NOT NULL REFERENCES groups (id), + CONSTRAINT users_group_idx UNIQUE (user_id, group_id) +); diff --git a/config_templates/sql/initDB.sql b/sql/initDB.sql similarity index 100% rename from config_templates/sql/initDB.sql rename to sql/initDB.sql diff --git a/sql/lb_apply.bat b/sql/lb_apply.bat new file mode 100644 index 000000000..342d9c334 --- /dev/null +++ b/sql/lb_apply.bat @@ -0,0 +1,8 @@ +set LB_HOME=c:\java\liquibase-3.5.3 +call %LB_HOME%\liquibase.bat --driver=org.postgresql.Driver ^ +--classpath=%LB_HOME%\lib ^ +--changeLogFile=databaseChangeLog.sql ^ +--url="jdbc:postgresql://localhost:5432/masterjava" ^ +--username=user ^ +--password=password ^ +migrate diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index d7b52ef30..945208617 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -6,7 +6,7 @@ import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; -import ru.javaops.masterjava.persist.model.UserFlag; +import ru.javaops.masterjava.persist.model.type.UserFlag; import ru.javaops.masterjava.xml.schema.ObjectFactory; import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; @@ -59,7 +59,7 @@ public List process(final InputStream is, int chunkSize) throws XM while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { ru.javaops.masterjava.xml.schema.User xmlUser = unmarshaller.unmarshal(processor.getReader(), ru.javaops.masterjava.xml.schema.User.class); - final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); + final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value()), null); chunk.add(user); if (chunk.size() == chunkSize) { addChunkFutures(chunkFutures, chunk); From 78829eb5e4ce04dca6fefde8c5c1faddf6c03a07 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 13 Jul 2019 14:08:38 +0300 Subject: [PATCH 42/87] 6 2 HW5 dao test --- .../masterjava/persist/dao/CityDao.java | 37 +++++++++++++ .../masterjava/persist/dao/GroupDao.java | 37 +++++++++++++ .../masterjava/persist/dao/ProjectDao.java | 37 +++++++++++++ .../masterjava/persist/dao/UserGroupDao.java | 32 +++++++++++ .../masterjava/persist/CityTestData.java | 36 +++++++++++++ .../masterjava/persist/GroupTestData.java | 53 +++++++++++++++++++ .../masterjava/persist/ProjectTestData.java | 34 ++++++++++++ .../masterjava/persist/UserGroupTestData.java | 41 ++++++++++++++ .../masterjava/persist/UserTestData.java | 17 +++--- .../persist/dao/AbstractDaoTest.java | 19 +++++++ .../masterjava/persist/dao/CityDaoTest.java | 36 +++++++++++++ .../masterjava/persist/dao/GroupDaoTest.java | 36 +++++++++++++ .../persist/dao/ProjectDaoTest.java | 36 +++++++++++++ .../persist/dao/UserGroupDaoTest.java | 39 ++++++++++++++ 14 files changed, 484 insertions(+), 6 deletions(-) create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/CityDao.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/ProjectDao.java create mode 100644 persist/src/main/java/ru/javaops/masterjava/persist/dao/UserGroupDao.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/CityTestData.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/GroupTestData.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/ProjectTestData.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/UserGroupTestData.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/CityDaoTest.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/GroupDaoTest.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/ProjectDaoTest.java create mode 100644 persist/src/test/java/ru/javaops/masterjava/persist/dao/UserGroupDaoTest.java diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/CityDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/CityDao.java new file mode 100644 index 000000000..5ebdb1afa --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/CityDao.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist.dao; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import one.util.streamex.StreamEx; +import org.skife.jdbi.v2.sqlobject.BindBean; +import org.skife.jdbi.v2.sqlobject.SqlBatch; +import org.skife.jdbi.v2.sqlobject.SqlQuery; +import org.skife.jdbi.v2.sqlobject.SqlUpdate; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.model.City; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static java.util.function.Function.identity; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class CityDao implements AbstractDao { + + @SqlUpdate("TRUNCATE city CASCADE ") + @Override + public abstract void clean(); + + @SqlQuery("SELECT * FROM city") + public abstract List getAll(); + + public Map getAsMap() { + return StreamEx.of(getAll()).toMap(City::getRef, identity()); + } + + @SqlUpdate("INSERT INTO city (ref, name) VALUES (:ref, :name)") + public abstract void insert(@BindBean City city); + + @SqlBatch("INSERT INTO city (ref, name) VALUES (:ref, :name)") + public abstract void insertBatch(@BindBean Collection cities); +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java new file mode 100644 index 000000000..7eb23fd59 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist.dao; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import one.util.streamex.StreamEx; +import org.skife.jdbi.v2.sqlobject.BindBean; +import org.skife.jdbi.v2.sqlobject.GetGeneratedKeys; +import org.skife.jdbi.v2.sqlobject.SqlQuery; +import org.skife.jdbi.v2.sqlobject.SqlUpdate; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.model.Group; + +import java.util.List; +import java.util.Map; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class GroupDao implements AbstractDao { + + @SqlUpdate("TRUNCATE groups CASCADE ") + @Override + public abstract void clean(); + + @SqlQuery("SELECT * FROM groups ORDER BY name") + public abstract List getAll(); + + public Map getAsMap() { + return StreamEx.of(getAll()).toMap(Group::getName, g -> g); + } + + @SqlUpdate("INSERT INTO groups (name, type, project_id) VALUES (:name, CAST(:type AS group_type), :projectId)") + @GetGeneratedKeys + public abstract int insertGeneratedId(@BindBean Group groups); + + public void insert(Group groups) { + int id = insertGeneratedId(groups); + groups.setId(id); + } +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/ProjectDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/ProjectDao.java new file mode 100644 index 000000000..e5b8a9b1f --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/ProjectDao.java @@ -0,0 +1,37 @@ +package ru.javaops.masterjava.persist.dao; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import one.util.streamex.StreamEx; +import org.skife.jdbi.v2.sqlobject.BindBean; +import org.skife.jdbi.v2.sqlobject.GetGeneratedKeys; +import org.skife.jdbi.v2.sqlobject.SqlQuery; +import org.skife.jdbi.v2.sqlobject.SqlUpdate; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.model.Project; + +import java.util.List; +import java.util.Map; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class ProjectDao implements AbstractDao { + + @SqlUpdate("TRUNCATE project CASCADE ") + @Override + public abstract void clean(); + + @SqlQuery("SELECT * FROM project ORDER BY name") + public abstract List getAll(); + + public Map getAsMap() { + return StreamEx.of(getAll()).toMap(Project::getName, g -> g); + } + + @SqlUpdate("INSERT INTO project (name, description) VALUES (:name, :description)") + @GetGeneratedKeys + public abstract int insertGeneratedId(@BindBean Project project); + + public void insert(Project project) { + int id = insertGeneratedId(project); + project.setId(id); + } +} diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserGroupDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserGroupDao.java new file mode 100644 index 000000000..2b5c89354 --- /dev/null +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserGroupDao.java @@ -0,0 +1,32 @@ +package ru.javaops.masterjava.persist.dao; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import one.util.streamex.StreamEx; +import org.skife.jdbi.v2.sqlobject.*; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.model.UserGroup; + +import java.util.List; +import java.util.Set; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class UserGroupDao implements AbstractDao { + + @SqlUpdate("TRUNCATE user_group CASCADE") + @Override + public abstract void clean(); + + @SqlBatch("INSERT INTO user_group (user_id, group_id) VALUES (:userId, :groupId)") + public abstract void insertBatch(@BindBean List userGroups); + + @SqlQuery("SELECT user_id FROM user_group WHERE group_id=:it") + public abstract Set getUserIds(@Bind int groupId); + + public static List toUserGroups(int userId, Integer... groupIds) { + return StreamEx.of(groupIds).map(groupId -> new UserGroup(userId, groupId)).toList(); + } + + public static Set getByGroupId(int groupId, List userGroups) { + return StreamEx.of(userGroups).filter(ug -> ug.getGroupId() == groupId).map(UserGroup::getUserId).toSet(); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/CityTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/CityTestData.java new file mode 100644 index 000000000..1e3fef700 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/CityTestData.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.persist; + +import com.google.common.collect.ImmutableMap; +import ru.javaops.masterjava.persist.dao.CityDao; +import ru.javaops.masterjava.persist.model.City; + +import java.util.Map; + +public class CityTestData { + public static City KIEV; + public static City MINSK; + public static City MOSCOW; + public static City SPB; + + public static Map CITIES; + + public static void init() { + KIEV = new City("kiv", "Киев"); + MINSK = new City("mnsk", "Минск"); + MOSCOW = new City("mow", "Москва"); + SPB = new City("spb", "Санкт-Петербург"); + CITIES = ImmutableMap.of( + KIEV.getRef(), KIEV, + MINSK.getRef(), MINSK, + MOSCOW.getRef(), MOSCOW, + SPB.getRef(), SPB); + } + + public static void setUp() { + CityDao dao = DBIProvider.getDao(CityDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + CITIES.values().forEach(dao::insert); + }); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/GroupTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/GroupTestData.java new file mode 100644 index 000000000..2bbd47d7d --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/GroupTestData.java @@ -0,0 +1,53 @@ +package ru.javaops.masterjava.persist; + +import com.google.common.collect.ImmutableMap; +import ru.javaops.masterjava.persist.dao.GroupDao; +import ru.javaops.masterjava.persist.model.Group; + +import java.util.Map; + +import static ru.javaops.masterjava.persist.ProjectTestData.MASTERJAVA_ID; +import static ru.javaops.masterjava.persist.ProjectTestData.TOPJAVA_ID; +import static ru.javaops.masterjava.persist.model.type.GroupType.CURRENT; +import static ru.javaops.masterjava.persist.model.type.GroupType.FINISHED; + +public class GroupTestData { + public static Group TOPJAVA_06; + public static Group TOPJAVA_07; + public static Group TOPJAVA_08; + public static Group MASTERJAVA_01; + public static Map GROUPS; + + public static int TOPJAVA_06_ID; + public static int TOPJAVA_07_ID; + public static int TOPJAVA_08_ID; + public static int MASTERJAVA_01_ID; + + + public static void init() { + ProjectTestData.init(); + ProjectTestData.setUp(); + + TOPJAVA_06 = new Group("topjava06", FINISHED, TOPJAVA_ID); + TOPJAVA_07 = new Group("topjava07", FINISHED, TOPJAVA_ID); + TOPJAVA_08 = new Group("topjava08", CURRENT, TOPJAVA_ID); + MASTERJAVA_01 = new Group("masterjava01", CURRENT, MASTERJAVA_ID); + GROUPS = ImmutableMap.of( + TOPJAVA_06.getName(), TOPJAVA_06, + TOPJAVA_07.getName(), TOPJAVA_07, + TOPJAVA_08.getName(), TOPJAVA_08, + MASTERJAVA_01.getName(), MASTERJAVA_01); + } + + public static void setUp() { + GroupDao dao = DBIProvider.getDao(GroupDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + GROUPS.values().forEach(dao::insert); + }); + TOPJAVA_06_ID = TOPJAVA_06.getId(); + TOPJAVA_07_ID = TOPJAVA_07.getId(); + TOPJAVA_08_ID = TOPJAVA_08.getId(); + MASTERJAVA_01_ID = MASTERJAVA_01.getId(); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/ProjectTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/ProjectTestData.java new file mode 100644 index 000000000..cd45980d0 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/ProjectTestData.java @@ -0,0 +1,34 @@ +package ru.javaops.masterjava.persist; + +import com.google.common.collect.ImmutableMap; +import ru.javaops.masterjava.persist.dao.ProjectDao; +import ru.javaops.masterjava.persist.model.Project; + +import java.util.Map; + +public class ProjectTestData { + public static Project TOPJAVA; + public static Project MASTERJAVA; + public static Map PROJECTS; + + public static int TOPJAVA_ID; + public static int MASTERJAVA_ID; + + public static void init() { + TOPJAVA = new Project("topjava", "Topjava"); + MASTERJAVA = new Project("masterjava", "Masterjava"); + PROJECTS = ImmutableMap.of( + TOPJAVA.getName(), TOPJAVA, + MASTERJAVA.getName(), MASTERJAVA); + } + + public static void setUp() { + ProjectDao dao = DBIProvider.getDao(ProjectDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + PROJECTS.values().forEach(dao::insert); + }); + TOPJAVA_ID = TOPJAVA.getId(); + MASTERJAVA_ID = MASTERJAVA.getId(); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/UserGroupTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/UserGroupTestData.java new file mode 100644 index 000000000..06c2d4be9 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/UserGroupTestData.java @@ -0,0 +1,41 @@ +package ru.javaops.masterjava.persist; + +import ru.javaops.masterjava.persist.dao.UserGroupDao; +import ru.javaops.masterjava.persist.model.UserGroup; + +import java.util.List; +import java.util.Set; + +import static ru.javaops.masterjava.persist.GroupTestData.*; +import static ru.javaops.masterjava.persist.dao.UserGroupDao.toUserGroups; + +public class UserGroupTestData { + + public static List USER_GROUPS; + + public static void init() { + UserTestData.init(); + UserTestData.setUp(); + + GroupTestData.init(); + GroupTestData.setUp(); + + USER_GROUPS = toUserGroups(UserTestData.ADMIN.getId(), TOPJAVA_07_ID, TOPJAVA_08_ID, MASTERJAVA_01_ID); + USER_GROUPS.addAll(toUserGroups(UserTestData.FULL_NAME.getId(), TOPJAVA_07_ID, MASTERJAVA_01_ID)); + USER_GROUPS.addAll(toUserGroups(UserTestData.USER1.getId(), TOPJAVA_06_ID, MASTERJAVA_01_ID)); + USER_GROUPS.add(new UserGroup(UserTestData.USER2.getId(), MASTERJAVA_01_ID)); + USER_GROUPS.add(new UserGroup(UserTestData.USER3.getId(), MASTERJAVA_01_ID)); + } + + public static void setUp() { + UserGroupDao dao = DBIProvider.getDao(UserGroupDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + dao.insertBatch(USER_GROUPS); + }); + } + + public static Set getByGroupId(int groupId) { + return UserGroupDao.getByGroupId(groupId, USER_GROUPS); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java index b5d7fad1e..ff1779c5e 100644 --- a/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java +++ b/persist/src/test/java/ru/javaops/masterjava/persist/UserTestData.java @@ -7,6 +7,8 @@ import java.util.List; +import static ru.javaops.masterjava.persist.CityTestData.*; + public class UserTestData { public static User ADMIN; public static User DELETED; @@ -17,12 +19,15 @@ public class UserTestData { public static List FIST5_USERS; public static void init() { - ADMIN = new User("Admin", "admin@javaops.ru", UserFlag.superuser, null); - DELETED = new User("Deleted", "deleted@yandex.ru", UserFlag.deleted, null); - FULL_NAME = new User("Full Name", "gmail@gmail.com", UserFlag.active, null); - USER1 = new User("User1", "user1@gmail.com", UserFlag.active, null); - USER2 = new User("User2", "user2@yandex.ru", UserFlag.active, null); - USER3 = new User("User3", "user3@yandex.ru", UserFlag.active, null); + CityTestData.init(); + CityTestData.setUp(); + + ADMIN = new User("Admin", "admin@javaops.ru", UserFlag.superuser, SPB.getRef()); + DELETED = new User("Deleted", "deleted@yandex.ru", UserFlag.deleted, SPB.getRef()); + FULL_NAME = new User("Full Name", "gmail@gmail.com", UserFlag.active, KIEV.getRef()); + USER1 = new User("User1", "user1@gmail.com", UserFlag.active, MOSCOW.getRef()); + USER2 = new User("User2", "user2@yandex.ru", UserFlag.active, KIEV.getRef()); + USER3 = new User("User3", "user3@yandex.ru", UserFlag.active, MINSK.getRef()); FIST5_USERS = ImmutableList.of(ADMIN, DELETED, FULL_NAME, USER1, USER2); } diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java index a65302fdd..3e8a891a5 100644 --- a/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/AbstractDaoTest.java @@ -1,13 +1,32 @@ package ru.javaops.masterjava.persist.dao; +import lombok.extern.slf4j.Slf4j; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.DBITestProvider; +@Slf4j public abstract class AbstractDaoTest { static { DBITestProvider.initDBI(); } + @Rule + public TestRule testWatcher = new TestWatcher() { + @Override + protected void starting(Description description) { + log.info("\n\n+++ Start " + description.getDisplayName()); + } + + @Override + protected void finished(Description description) { + log.info("\n+++ Finish " + description.getDisplayName() + '\n'); + } + }; + protected DAO dao; protected AbstractDaoTest(Class daoClass) { diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/CityDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/CityDaoTest.java new file mode 100644 index 000000000..29cd042e0 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/CityDaoTest.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.persist.dao; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import ru.javaops.masterjava.persist.CityTestData; +import ru.javaops.masterjava.persist.model.City; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static ru.javaops.masterjava.persist.CityTestData.CITIES; + +public class CityDaoTest extends AbstractDaoTest { + + public CityDaoTest() { + super(CityDao.class); + } + + @BeforeClass + public static void init() throws Exception { + CityTestData.init(); + } + + @Before + public void setUp() throws Exception { + CityTestData.setUp(); + } + + @Test + public void getAll() throws Exception { + final Map cities = dao.getAsMap(); + assertEquals(CITIES, cities); + System.out.println(cities.values()); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/GroupDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/GroupDaoTest.java new file mode 100644 index 000000000..f3c7e5970 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/GroupDaoTest.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.persist.dao; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import ru.javaops.masterjava.persist.GroupTestData; +import ru.javaops.masterjava.persist.model.Group; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static ru.javaops.masterjava.persist.GroupTestData.GROUPS; + +public class GroupDaoTest extends AbstractDaoTest { + + public GroupDaoTest() { + super(GroupDao.class); + } + + @BeforeClass + public static void init() throws Exception { + GroupTestData.init(); + } + + @Before + public void setUp() throws Exception { + GroupTestData.setUp(); + } + + @Test + public void getAll() throws Exception { + final Map projects = dao.getAsMap(); + assertEquals(GROUPS, projects); + System.out.println(projects.values()); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/ProjectDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/ProjectDaoTest.java new file mode 100644 index 000000000..4c86078d5 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/ProjectDaoTest.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.persist.dao; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import ru.javaops.masterjava.persist.ProjectTestData; +import ru.javaops.masterjava.persist.model.Project; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static ru.javaops.masterjava.persist.ProjectTestData.PROJECTS; + +public class ProjectDaoTest extends AbstractDaoTest { + + public ProjectDaoTest() { + super(ProjectDao.class); + } + + @BeforeClass + public static void init() throws Exception { + ProjectTestData.init(); + } + + @Before + public void setUp() throws Exception { + ProjectTestData.setUp(); + } + + @Test + public void getAll() throws Exception { + final Map projects = dao.getAsMap(); + assertEquals(PROJECTS, projects); + System.out.println(projects.values()); + } +} diff --git a/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserGroupDaoTest.java b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserGroupDaoTest.java new file mode 100644 index 000000000..6d27a8642 --- /dev/null +++ b/persist/src/test/java/ru/javaops/masterjava/persist/dao/UserGroupDaoTest.java @@ -0,0 +1,39 @@ +package ru.javaops.masterjava.persist.dao; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import ru.javaops.masterjava.persist.UserGroupTestData; + +import java.util.Set; + +import static ru.javaops.masterjava.persist.GroupTestData.MASTERJAVA_01_ID; +import static ru.javaops.masterjava.persist.GroupTestData.TOPJAVA_07_ID; +import static ru.javaops.masterjava.persist.UserGroupTestData.getByGroupId; + +public class UserGroupDaoTest extends AbstractDaoTest { + + public UserGroupDaoTest() { + super(UserGroupDao.class); + } + + @BeforeClass + public static void init() throws Exception { + UserGroupTestData.init(); + } + + @Before + public void setUp() throws Exception { + UserGroupTestData.setUp(); + } + + @Test + public void getAll() throws Exception { + Set userIds = dao.getUserIds(MASTERJAVA_01_ID); + Assert.assertEquals(getByGroupId(MASTERJAVA_01_ID), userIds); + + userIds = dao.getUserIds(TOPJAVA_07_ID); + Assert.assertEquals(getByGroupId(TOPJAVA_07_ID), userIds); + } +} From 5c89cdf065547ed7a794a12a0b7d2c738ad604aa Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 13 Jul 2019 14:11:20 +0300 Subject: [PATCH 43/87] 6 3 HW5 add PayloadProcessor --- .../masterjava/upload/PayloadProcessor.java | 30 +++++++++++++++++++ .../masterjava/upload/UploadServlet.java | 4 +-- .../masterjava/upload/UserProcessor.java | 17 ++--------- 3 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java new file mode 100644 index 000000000..5b7354e84 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java @@ -0,0 +1,30 @@ +package ru.javaops.masterjava.upload; + +import lombok.AllArgsConstructor; +import ru.javaops.masterjava.xml.util.StaxStreamProcessor; + +import javax.xml.bind.JAXBException; +import javax.xml.stream.XMLStreamException; +import java.io.InputStream; +import java.util.List; + +public class PayloadProcessor { + private final UserProcessor userProcessor = new UserProcessor(); + + @AllArgsConstructor + public static class FailedEmails { + public String emailsOrRange; + public String reason; + + @Override + public String toString() { + return emailsOrRange + " : " + reason; + } + } + + + public List process(InputStream is, int chunkSize) throws XMLStreamException, JAXBException { + final StaxStreamProcessor processor = new StaxStreamProcessor(is); + return userProcessor.process(processor, chunkSize); + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java index 732c25cb1..fafe55db0 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -23,7 +23,7 @@ public class UploadServlet extends HttpServlet { private static final int CHUNK_SIZE = 2000; - private final UserProcessor userProcessor = new UserProcessor(); + private final PayloadProcessor payloadProcessor = new PayloadProcessor(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -42,7 +42,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } else { Part filePart = req.getPart("fileToUpload"); try (InputStream is = filePart.getInputStream()) { - List failed = userProcessor.process(is, chunkSize); + List failed = payloadProcessor.process(is, chunkSize); log.info("Failed users: " + failed); final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale(), diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 945208617..1071b398f 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -1,12 +1,12 @@ package ru.javaops.masterjava.upload; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import lombok.val; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; import ru.javaops.masterjava.persist.model.type.UserFlag; +import ru.javaops.masterjava.upload.PayloadProcessor.FailedEmails; import ru.javaops.masterjava.xml.schema.ObjectFactory; import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; @@ -14,7 +14,6 @@ import javax.xml.bind.JAXBException; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; -import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -33,28 +32,16 @@ public class UserProcessor { private ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_THREADS); - @AllArgsConstructor - public static class FailedEmails { - public String emailsOrRange; - public String reason; - - @Override - public String toString() { - return emailsOrRange + " : " + reason; - } - } - /* * return failed users chunks */ - public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { + public List process(final StaxStreamProcessor processor, int chunkSize) throws XMLStreamException, JAXBException { log.info("Start processing with chunkSize=" + chunkSize); Map>> chunkFutures = new LinkedHashMap<>(); // ordered map (emailRange -> chunk future) int id = userDao.getSeqAndSkip(chunkSize); List chunk = new ArrayList<>(chunkSize); - val processor = new StaxStreamProcessor(is); val unmarshaller = jaxbParser.createUnmarshaller(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { From 560c898dcb6c24823ea756179f86362adb41145f Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 13 Jul 2019 14:15:07 +0300 Subject: [PATCH 44/87] 6 4 HW5 add CityProcessor --- .../masterjava/upload/CityProcessor.java | 32 +++++++++++++++++++ .../masterjava/upload/PayloadProcessor.java | 5 ++- .../masterjava/upload/UserProcessor.java | 22 ++++++++----- web/upload/src/test/resources/payload_bad.xml | 31 ++++++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/upload/CityProcessor.java create mode 100644 web/upload/src/test/resources/payload_bad.xml diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/CityProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/CityProcessor.java new file mode 100644 index 000000000..a288e08a7 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/CityProcessor.java @@ -0,0 +1,32 @@ +package ru.javaops.masterjava.upload; + +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.persist.dao.CityDao; +import ru.javaops.masterjava.persist.model.City; +import ru.javaops.masterjava.xml.util.StaxStreamProcessor; + +import javax.xml.stream.XMLStreamException; +import java.util.ArrayList; +import java.util.Map; + +@Slf4j +public class CityProcessor { + private final CityDao cityDao = DBIProvider.getDao(CityDao.class); + + public Map process(StaxStreamProcessor processor) throws XMLStreamException { + val map = cityDao.getAsMap(); + val newCities = new ArrayList(); + + while (processor.startElement("City", "Cities")) { + val ref = processor.getAttribute("id"); + if (!map.containsKey(ref)) { + newCities.add(new City(ref, processor.getText())); + } + } + log.info("Insert batch " + newCities); + cityDao.insertBatch(newCities); + return cityDao.getAsMap(); + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java index 5b7354e84..3edd101ef 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java @@ -1,6 +1,7 @@ package ru.javaops.masterjava.upload; import lombok.AllArgsConstructor; +import lombok.val; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; import javax.xml.bind.JAXBException; @@ -9,6 +10,7 @@ import java.util.List; public class PayloadProcessor { + private final CityProcessor cityProcessor = new CityProcessor(); private final UserProcessor userProcessor = new UserProcessor(); @AllArgsConstructor @@ -25,6 +27,7 @@ public String toString() { public List process(InputStream is, int chunkSize) throws XMLStreamException, JAXBException { final StaxStreamProcessor processor = new StaxStreamProcessor(is); - return userProcessor.process(processor, chunkSize); + val cities = cityProcessor.process(processor); + return userProcessor.process(processor, cities, chunkSize); } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 1071b398f..170521ac9 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -4,6 +4,7 @@ import lombok.val; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; +import ru.javaops.masterjava.persist.model.City; import ru.javaops.masterjava.persist.model.User; import ru.javaops.masterjava.persist.model.type.UserFlag; import ru.javaops.masterjava.upload.PayloadProcessor.FailedEmails; @@ -35,7 +36,7 @@ public class UserProcessor { /* * return failed users chunks */ - public List process(final StaxStreamProcessor processor, int chunkSize) throws XMLStreamException, JAXBException { + public List process(final StaxStreamProcessor processor, Map cities, int chunkSize) throws XMLStreamException, JAXBException { log.info("Start processing with chunkSize=" + chunkSize); Map>> chunkFutures = new LinkedHashMap<>(); // ordered map (emailRange -> chunk future) @@ -43,15 +44,21 @@ public List process(final StaxStreamProcessor processor, int chunk int id = userDao.getSeqAndSkip(chunkSize); List chunk = new ArrayList<>(chunkSize); val unmarshaller = jaxbParser.createUnmarshaller(); + List failed = new ArrayList<>(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { + String cityRef = processor.getAttribute("city"); // unmarshal doesn't get city ref ru.javaops.masterjava.xml.schema.User xmlUser = unmarshaller.unmarshal(processor.getReader(), ru.javaops.masterjava.xml.schema.User.class); - final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value()), null); - chunk.add(user); - if (chunk.size() == chunkSize) { - addChunkFutures(chunkFutures, chunk); - chunk = new ArrayList<>(chunkSize); - id = userDao.getSeqAndSkip(chunkSize); + if (cities.get(cityRef) == null) { + failed.add(new FailedEmails(xmlUser.getEmail(), "City '" + cityRef + "' is not present in DB")); + } else { + final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value()), cityRef); + chunk.add(user); + if (chunk.size() == chunkSize) { + addChunkFutures(chunkFutures, chunk); + chunk = new ArrayList<>(chunkSize); + id = userDao.getSeqAndSkip(chunkSize); + } } } @@ -59,7 +66,6 @@ public List process(final StaxStreamProcessor processor, int chunk addChunkFutures(chunkFutures, chunk); } - List failed = new ArrayList<>(); List allAlreadyPresents = new ArrayList<>(); chunkFutures.forEach((emailRange, future) -> { try { diff --git a/web/upload/src/test/resources/payload_bad.xml b/web/upload/src/test/resources/payload_bad.xml new file mode 100644 index 000000000..a6f67cdd2 --- /dev/null +++ b/web/upload/src/test/resources/payload_bad.xml @@ -0,0 +1,31 @@ + + + + + Topjava + + + + + + Masterjava + + + + + Санкт-Петербург + Москва + Киев + Минск + + + Full Name + Admin + Deleted + User1 + User2 + User3 + + From 5803af426514d3bd910eaa819fd5b5a37528ee34 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 13 Jul 2019 14:50:31 +0300 Subject: [PATCH 45/87] 6 5 web services --- .../masterjava/service/mail/Addressee.java | 17 +++++++++++++++ .../masterjava/service/mail/MailService.java | 21 +++++++++++++++++++ .../masterjava/service/mail/MailSender.java | 12 +++++++++++ .../service/mail/MailServiceImpl.java | 11 ++++++++++ .../service/mail/MailServiceClient.java | 20 ++++++++++++++++++ .../service/mail/MailServicePublisher.java | 14 +++++++++++++ 6 files changed, 95 insertions(+) create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java create mode 100644 services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java create mode 100644 services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java new file mode 100644 index 000000000..f30f68fc4 --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java @@ -0,0 +1,17 @@ +package ru.javaops.masterjava.service.mail; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * gkislin + * 15.11.2016 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Addressee { + private String email; + private String name; +} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java new file mode 100644 index 000000000..ab4d91311 --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -0,0 +1,21 @@ +package ru.javaops.masterjava.service.mail; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; +import java.util.List; + +/** + * gkislin + * 15.11.2016 + */ +@WebService +public interface MailService { + + @WebMethod + void sendMail( + @WebParam(name = "to") List to, + @WebParam(name = "cc") List cc, + @WebParam(name = "subject") String subject, + @WebParam(name = "body") String body); +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java new file mode 100644 index 000000000..ecfff61c6 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -0,0 +1,12 @@ +package ru.javaops.masterjava.service.mail; + +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +public class MailSender { + static void sendMail(List to, List cc, String subject, String body) { + log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java new file mode 100644 index 000000000..46bff4609 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -0,0 +1,11 @@ +package ru.javaops.masterjava.service.mail; + +import javax.jws.WebService; +import java.util.List; + +@WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService") +public class MailServiceImpl implements MailService { + public void sendMail(List to, List cc, String subject, String body) { + MailSender.sendMail(to, cc, subject, body); + } +} diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java new file mode 100644 index 000000000..f2bfc79ad --- /dev/null +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -0,0 +1,20 @@ +package ru.javaops.masterjava.service.mail; + +import com.google.common.collect.ImmutableList; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; +import java.net.MalformedURLException; +import java.net.URL; + +public class MailServiceClient { + + public static void main(String[] args) throws MalformedURLException { + Service service = Service.create( + new URL("http://localhost:8080/mail/mailService?wsdl"), + new QName("http://mail.service.masterjava.javaops.ru/", "MailServiceImplService")); + + MailService mailService = service.getPort(MailService.class); + mailService.sendMail(ImmutableList.of(new Addressee("masterjava@javaops.ru", null)), null, "Subject", "Body"); + } +} diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java new file mode 100644 index 000000000..88de00ae3 --- /dev/null +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java @@ -0,0 +1,14 @@ +package ru.javaops.masterjava.service.mail; + +import javax.xml.ws.Endpoint; + +/** + * User: gkislin + * Date: 28.05.2014 + */ +public class MailServicePublisher { + + public static void main(String[] args) { + Endpoint.publish("http://localhost:8080/mail/mailService", new MailServiceImpl()); + } +} From 57466daedfdf54b10187a9ffc61750c84907876a Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 13:58:03 +0300 Subject: [PATCH 46/87] 7 1 HW6 MailSender --- .../masterjava/service/mail/Addressee.java | 11 +-- services/mail-service/pom.xml | 5 ++ .../masterjava/service/mail/MailConfig.java | 67 +++++++++++++++++++ .../masterjava/service/mail/MailSender.java | 21 ++++++ .../mail-service/src/main/resources/mail.conf | 12 ++++ 5 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailConfig.java create mode 100644 services/mail-service/src/main/resources/mail.conf diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java index f30f68fc4..a6d578b6a 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java @@ -3,15 +3,16 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.NonNull; -/** - * gkislin - * 15.11.2016 - */ @Data @AllArgsConstructor @NoArgsConstructor public class Addressee { - private String email; + private @NonNull String email; private String name; + + public Addressee(String email) { + this(email, null); + } } diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index bddffc16c..5235cb429 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -22,5 +22,10 @@ mail-api ${project.version} + + org.apache.commons + commons-email + 1.5 + diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailConfig.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailConfig.java new file mode 100644 index 000000000..4b00e0276 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailConfig.java @@ -0,0 +1,67 @@ +package ru.javaops.masterjava.service.mail; + +import com.typesafe.config.Config; +import org.apache.commons.mail.DefaultAuthenticator; +import org.apache.commons.mail.Email; +import org.apache.commons.mail.EmailException; +import org.apache.commons.mail.HtmlEmail; +import ru.javaops.masterjava.config.Configs; + +import javax.mail.Authenticator; +import java.nio.charset.StandardCharsets; + +public class MailConfig { + private static final MailConfig INSTANCE = + new MailConfig(Configs.getConfig("mail.conf", "mail")); + + final private String host; + final private int port; + final private boolean useSSL; + final private boolean useTLS; + final private boolean debug; + final private String username; + final private Authenticator auth; + final private String fromName; + + private MailConfig(Config conf) { + host = conf.getString("host"); + port = conf.getInt("port"); + username = conf.getString("username"); + auth = new DefaultAuthenticator(username, conf.getString("password")); + useSSL = conf.getBoolean("useSSL"); + useTLS = conf.getBoolean("useTLS"); + debug = conf.getBoolean("debug"); + fromName = conf.getString("fromName"); + } + + public T prepareEmail(T email) throws EmailException { + email.setFrom(username, fromName); + email.setHostName(host); + if (useSSL) { + email.setSslSmtpPort(String.valueOf(port)); + } else { + email.setSmtpPort(port); + } + email.setSSLOnConnect(useSSL); + email.setStartTLSEnabled(useTLS); + email.setDebug(debug); + email.setAuthenticator(auth); + email.setCharset(StandardCharsets.UTF_8.name()); + return email; + } + + public static HtmlEmail createHtmlEmail() throws EmailException { + return INSTANCE.prepareEmail(new HtmlEmail()); + } + + @Override + public String toString() { + return "\nhost='" + host + '\'' + + "\nport=" + port + + "\nuseSSL=" + useSSL + + "\nuseTLS=" + useTLS + + "\ndebug=" + debug + + "\nusername='" + username + '\'' + + "\nfromName='" + fromName + '\''; + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index ecfff61c6..edd82b270 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -1,6 +1,9 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.collect.ImmutableMap; import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.apache.commons.mail.EmailException; import java.util.List; @@ -8,5 +11,23 @@ public class MailSender { static void sendMail(List to, List cc, String subject, String body) { log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + try { + val email = MailConfig.createHtmlEmail(); + email.setSubject(subject); + email.setHtmlMsg(body); + for (Addressee addressee : to) { + email.addTo(addressee.getEmail(), addressee.getName()); + } + for (Addressee addressee : cc) { + email.addCc(addressee.getEmail(), addressee.getName()); + } + + // https://yandex.ru/blog/company/66296 + email.setHeaders(ImmutableMap.of("List-Unsubscribe", "")); + + email.send(); + } catch (EmailException e) { + log.error(e.getMessage(), e); + } } } diff --git a/services/mail-service/src/main/resources/mail.conf b/services/mail-service/src/main/resources/mail.conf new file mode 100644 index 000000000..fdc6ca092 --- /dev/null +++ b/services/mail-service/src/main/resources/mail.conf @@ -0,0 +1,12 @@ +mail { + host: smtp.yandex.ru + port: 465 + username: "user@yandex.ru" + password: password + useSSL: true + useTLS: false + debug: true + fromName: MasterJava +} + +include required(file("/apps/masterjava/config/mail.conf")) From 49eb21c687bc4be5e5228980b5c95b95d675b4f8 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:00:34 +0300 Subject: [PATCH 47/87] 7 2 HW6 mail history --- persist/pom.xml | 19 ++++++++ .../masterjava/service/mail/Addressee.java | 14 +++++- services/mail-service/pom.xml | 12 ++++++ .../masterjava/service/mail/MailSender.java | 9 ++++ .../service/mail/persist/MailCase.java | 27 ++++++++++++ .../service/mail/persist/MailCaseDao.java | 24 +++++++++++ .../service/mail/MailServiceClient.java | 4 +- .../service/mail/MailServicePublisher.java | 7 ++- .../service/mail/persist/MailCaseDaoTest.java | 22 ++++++++++ .../mail/persist/MailCaseTestData.java | 43 +++++++++++++++++++ sql/databaseChangeLog.sql | 13 ++++++ 11 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCaseDao.java create mode 100644 services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseDaoTest.java create mode 100644 services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java diff --git a/persist/pom.xml b/persist/pom.xml index 4953ba4d0..f6f0732bf 100644 --- a/persist/pom.xml +++ b/persist/pom.xml @@ -15,6 +15,25 @@ 1.0-SNAPSHOT Persist + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + test-jar + + + + + + + ${project.groupId} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java index a6d578b6a..0795631f5 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java @@ -13,6 +13,18 @@ public class Addressee { private String name; public Addressee(String email) { - this(email, null); + email = email.trim(); + int idx = email.indexOf('<'); + if (idx == -1) { + this.email = email; + } else { + this.name = email.substring(0, idx).trim(); + this.email = email.substring(idx + 1, email.length() - 1).trim(); + } + } + + @Override + public String toString() { + return name == null ? email : name + " <" + email + '>'; } } diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 5235cb429..d7fe82562 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -27,5 +27,17 @@ commons-email 1.5 + + ${project.groupId} + persist + ${project.version} + + + ${project.groupId} + persist + ${project.version} + test-jar + test + diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index edd82b270..8aada2614 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -4,13 +4,19 @@ import lombok.extern.slf4j.Slf4j; import lombok.val; import org.apache.commons.mail.EmailException; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.service.mail.persist.MailCase; +import ru.javaops.masterjava.service.mail.persist.MailCaseDao; import java.util.List; @Slf4j public class MailSender { + private static final MailCaseDao MAIL_CASE_DAO = DBIProvider.getDao(MailCaseDao.class); + static void sendMail(List to, List cc, String subject, String body) { log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + String state = "OK"; try { val email = MailConfig.createHtmlEmail(); email.setSubject(subject); @@ -28,6 +34,9 @@ static void sendMail(List to, List cc, String subject, Str email.send(); } catch (EmailException e) { log.error(e.getMessage(), e); + state = e.getMessage(); } + MAIL_CASE_DAO.insert(MailCase.of(to, cc, subject, state)); + log.info("Sent with state: " + state); } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java new file mode 100644 index 000000000..90f050f15 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java @@ -0,0 +1,27 @@ +package ru.javaops.masterjava.service.mail.persist; + +import com.bertoncelj.jdbi.entitymapper.Column; +import com.google.common.base.Joiner; +import lombok.*; +import ru.javaops.masterjava.persist.model.BaseEntity; +import ru.javaops.masterjava.service.mail.Addressee; + +import java.util.Date; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) // compare without id +@ToString +public class MailCase extends BaseEntity { + private @Column("list_to") String listTo; + private @Column("list_cc") String listCc; + private String subject; + private String state; + private Date datetime; + + public static MailCase of(List to, List cc, String subject, String state){ + return new MailCase(Joiner.on(", ").join(to), Joiner.on(", ").join(cc), subject, state, new Date()); + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCaseDao.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCaseDao.java new file mode 100644 index 000000000..21bdfebff --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCaseDao.java @@ -0,0 +1,24 @@ +package ru.javaops.masterjava.service.mail.persist; + +import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; +import org.skife.jdbi.v2.sqlobject.*; +import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; +import ru.javaops.masterjava.persist.dao.AbstractDao; + +import java.util.Date; +import java.util.List; + +@RegisterMapperFactory(EntityMapperFactory.class) +public abstract class MailCaseDao implements AbstractDao { + + @SqlUpdate("TRUNCATE mail_hist") + @Override + public abstract void clean(); + + @SqlQuery("SELECT * FROM mail_hist WHERE datetime >= :after ORDER BY datetime DESC") + public abstract List getAfter(@Bind("after") Date date); + + @SqlUpdate("INSERT INTO mail_hist (list_to, list_cc, subject, state, datetime) VALUES (:listTo, :listCc, :subject, :state, :datetime)") + @GetGeneratedKeys + public abstract int insert(@BindBean MailCase mails); +} diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index f2bfc79ad..8ddfed518 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -15,6 +15,8 @@ public static void main(String[] args) throws MalformedURLException { new QName("http://mail.service.masterjava.javaops.ru/", "MailServiceImplService")); MailService mailService = service.getPort(MailService.class); - mailService.sendMail(ImmutableList.of(new Addressee("masterjava@javaops.ru", null)), null, "Subject", "Body"); + mailService.sendMail(ImmutableList.of( + new Addressee("masterjava@javaops.ru", null), + new Addressee("Bad Email ")), null, "Subject", "Body"); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java index 88de00ae3..bb9253cec 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java @@ -1,14 +1,13 @@ package ru.javaops.masterjava.service.mail; +import ru.javaops.masterjava.persist.DBITestProvider; + import javax.xml.ws.Endpoint; -/** - * User: gkislin - * Date: 28.05.2014 - */ public class MailServicePublisher { public static void main(String[] args) { + DBITestProvider.initDBI(); Endpoint.publish("http://localhost:8080/mail/mailService", new MailServiceImpl()); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseDaoTest.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseDaoTest.java new file mode 100644 index 000000000..54e40a508 --- /dev/null +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseDaoTest.java @@ -0,0 +1,22 @@ +package ru.javaops.masterjava.service.mail.persist; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import ru.javaops.masterjava.persist.dao.AbstractDaoTest; + +public class MailCaseDaoTest extends AbstractDaoTest { + public MailCaseDaoTest() { + super(MailCaseDao.class); + } + + @Before + public void setUp() throws Exception { + MailCaseTestData.setUp(); + } + + @Test + public void getAll() throws Exception { + Assert.assertEquals(MailCaseTestData.MAIL_CASES, dao.getAfter(MailCaseTestData.DATE_FROM)); + } +} diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java new file mode 100644 index 000000000..046f00c4f --- /dev/null +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java @@ -0,0 +1,43 @@ +package ru.javaops.masterjava.service.mail.persist; + +import com.google.common.collect.ImmutableList; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.service.mail.Addressee; + +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; + +public class MailCaseTestData { + private static final Instant now = Instant.now(); + static final Date DATE_FROM = Date.from(now.minus(Duration.ofDays(1))); + + static final List MAIL_CASES = ImmutableList.of( + MailCase.of( + ImmutableList.of( + new Addressee("ИмяTo1 Фамилия1 "), + new Addressee("Имя2 Фамилия2 ")), + ImmutableList.of( + new Addressee("ИмяCc1 Фамилия1 "), + new Addressee("ИмяCc2 Фамилия2 ")), + "subject1", "state1" + ), + new MailCase("toMail2@ya.ru", null, "subject2", "state2", + Date.from(now.minus(Duration.ofMinutes(1)))), + new MailCase(null, "ccMail3@ya.ru", "subject3", "state3", DATE_FROM) + ); + + private static final MailCase MAIL_CASE_EXCLUDED = + new MailCase("toMail4@ya.ru", "ccMail4@ya.ru", "subject4", "state4", + Date.from(now.minus(Duration.ofDays(2)))); + + public static void setUp() { + MailCaseDao dao = DBIProvider.getDao(MailCaseDao.class); + dao.clean(); + DBIProvider.getDBI().useTransaction((conn, status) -> { + MAIL_CASES.forEach(dao::insert); + dao.insert(MAIL_CASE_EXCLUDED); + }); + } +} diff --git a/sql/databaseChangeLog.sql b/sql/databaseChangeLog.sql index 60ec863d9..22f840808 100644 --- a/sql/databaseChangeLog.sql +++ b/sql/databaseChangeLog.sql @@ -32,3 +32,16 @@ CREATE TABLE user_group ( group_id INTEGER NOT NULL REFERENCES groups (id), CONSTRAINT users_group_idx UNIQUE (user_id, group_id) ); + +--changeset gkislin:3 +CREATE TABLE mail_hist ( + id SERIAL PRIMARY KEY, + list_to TEXT NULL, + list_cc TEXT NULL, + subject TEXT NULL, + state TEXT NOT NULL, + datetime TIMESTAMP NOT NULL +); + +COMMENT ON TABLE mail_hist IS 'История отправки email'; +COMMENT ON COLUMN mail_hist.datetime IS 'Время отправки'; From 3d1ccb63adc20dcf4f56dde5593136908d7cc4f5 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:02:08 +0300 Subject: [PATCH 48/87] 7 3 HW6 ProjectGroupProcessor --- .../masterjava/persist/dao/GroupDao.java | 9 ++-- .../masterjava/upload/PayloadProcessor.java | 8 ++- .../upload/ProjectGroupProcessor.java | 52 +++++++++++++++++++ .../masterjava/upload/UserProcessor.java | 8 +-- 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 web/upload/src/main/java/ru/javaops/masterjava/upload/ProjectGroupProcessor.java diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java index 7eb23fd59..9fc78e2c7 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/GroupDao.java @@ -2,13 +2,11 @@ import com.bertoncelj.jdbi.entitymapper.EntityMapperFactory; import one.util.streamex.StreamEx; -import org.skife.jdbi.v2.sqlobject.BindBean; -import org.skife.jdbi.v2.sqlobject.GetGeneratedKeys; -import org.skife.jdbi.v2.sqlobject.SqlQuery; -import org.skife.jdbi.v2.sqlobject.SqlUpdate; +import org.skife.jdbi.v2.sqlobject.*; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; import ru.javaops.masterjava.persist.model.Group; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -34,4 +32,7 @@ public void insert(Group groups) { int id = insertGeneratedId(groups); groups.setId(id); } + + @SqlBatch("INSERT INTO groups (name, type, project_id) VALUES (:name, CAST(:type AS group_type), :projectId)") + public abstract void insertBatch(@BindBean Collection groups); } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java index 3edd101ef..07435a398 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/PayloadProcessor.java @@ -2,6 +2,8 @@ import lombok.AllArgsConstructor; import lombok.val; +import ru.javaops.masterjava.xml.schema.ObjectFactory; +import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; import javax.xml.bind.JAXBException; @@ -10,6 +12,8 @@ import java.util.List; public class PayloadProcessor { + public static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); + private final ProjectGroupProcessor projectGroupProcessor = new ProjectGroupProcessor(); private final CityProcessor cityProcessor = new CityProcessor(); private final UserProcessor userProcessor = new UserProcessor(); @@ -24,10 +28,10 @@ public String toString() { } } - public List process(InputStream is, int chunkSize) throws XMLStreamException, JAXBException { final StaxStreamProcessor processor = new StaxStreamProcessor(is); + val groups = projectGroupProcessor.process(processor); val cities = cityProcessor.process(processor); - return userProcessor.process(processor, cities, chunkSize); + return userProcessor.process(processor, groups, cities, chunkSize); } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/ProjectGroupProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/ProjectGroupProcessor.java new file mode 100644 index 000000000..b07eed203 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/ProjectGroupProcessor.java @@ -0,0 +1,52 @@ +package ru.javaops.masterjava.upload; + +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import ru.javaops.masterjava.persist.DBIProvider; +import ru.javaops.masterjava.persist.dao.GroupDao; +import ru.javaops.masterjava.persist.dao.ProjectDao; +import ru.javaops.masterjava.persist.model.Group; +import ru.javaops.masterjava.persist.model.Project; +import ru.javaops.masterjava.persist.model.type.GroupType; +import ru.javaops.masterjava.xml.schema.Payload; +import ru.javaops.masterjava.xml.util.StaxStreamProcessor; + +import javax.xml.bind.JAXBException; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; +import java.util.ArrayList; +import java.util.Map; + +import static ru.javaops.masterjava.upload.PayloadProcessor.jaxbParser; + +@Slf4j +public class ProjectGroupProcessor { + private final ProjectDao projectDao = DBIProvider.getDao(ProjectDao.class); + private final GroupDao groupDao = DBIProvider.getDao(GroupDao.class); + + public Map process(StaxStreamProcessor processor) throws XMLStreamException, JAXBException { + val projectMap = projectDao.getAsMap(); + val groupMap = groupDao.getAsMap(); + val newGroups = new ArrayList(); + + processor.doUntil(XMLEvent.START_ELEMENT, "Projects"); + Payload.Projects projects = jaxbParser.createUnmarshaller().unmarshal(processor.getReader(), Payload.Projects.class); + projects.getProject().forEach(p -> { + Project project = projectMap.get(p.getName()); + if (project == null) { + project = new Project(p.getName(), p.getDescription()); + log.info("Insert project " + project); + projectDao.insert(project); + } + final int projectId = project.getId(); + p.getGroup().forEach(g -> { + if (!groupMap.containsKey(g.getName())) { + newGroups.add(new Group(g.getName(), GroupType.valueOf(g.getType().value()), projectId)); + } + }); + }); + log.info("Insert groups " + newGroups); + groupDao.insertBatch(newGroups); + return groupDao.getAsMap(); + } +} diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index 170521ac9..f9df5513d 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -5,11 +5,10 @@ import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.City; +import ru.javaops.masterjava.persist.model.Group; import ru.javaops.masterjava.persist.model.User; import ru.javaops.masterjava.persist.model.type.UserFlag; import ru.javaops.masterjava.upload.PayloadProcessor.FailedEmails; -import ru.javaops.masterjava.xml.schema.ObjectFactory; -import ru.javaops.masterjava.xml.util.JaxbParser; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; import javax.xml.bind.JAXBException; @@ -24,11 +23,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +import static ru.javaops.masterjava.upload.PayloadProcessor.jaxbParser; + @Slf4j public class UserProcessor { private static final int NUMBER_THREADS = 4; - private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); private static UserDao userDao = DBIProvider.getDao(UserDao.class); private ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_THREADS); @@ -36,7 +36,7 @@ public class UserProcessor { /* * return failed users chunks */ - public List process(final StaxStreamProcessor processor, Map cities, int chunkSize) throws XMLStreamException, JAXBException { + public List process(final StaxStreamProcessor processor, Map groups, Map cities, int chunkSize) throws XMLStreamException, JAXBException { log.info("Start processing with chunkSize=" + chunkSize); Map>> chunkFutures = new LinkedHashMap<>(); // ordered map (emailRange -> chunk future) From bea8b1778d5de2b4794c24dc395125c25f7f4578 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:03:05 +0300 Subject: [PATCH 49/87] 7 4 HW6 refactor UserProcessor --- .../masterjava/persist/dao/UserDao.java | 4 +- .../masterjava/upload/UserProcessor.java | 64 ++++++++++++++----- web/upload/src/test/resources/payload_bad.xml | 2 +- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java index c57c4f2e2..516067d69 100644 --- a/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java +++ b/persist/src/main/java/ru/javaops/masterjava/persist/dao/UserDao.java @@ -55,11 +55,11 @@ public int getSeqAndSkip(int step) { public abstract int[] insertBatch(@BindBean List users, @BatchChunkSize int chunkSize); - public List insertAndGetConflictEmails(List users) { + public List insertAndGetConflictEmails(List users) { int[] result = insertBatch(users, users.size()); return IntStreamEx.range(0, users.size()) .filter(i -> result[i] == 0) - .mapToObj(index -> users.get(index).getEmail()) + .mapToObj(users::get) .toList(); } } diff --git a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java index f9df5513d..519524e5e 100644 --- a/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UserProcessor.java @@ -1,12 +1,16 @@ package ru.javaops.masterjava.upload; +import com.google.common.base.Splitter; import lombok.extern.slf4j.Slf4j; import lombok.val; +import one.util.streamex.StreamEx; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; +import ru.javaops.masterjava.persist.dao.UserGroupDao; import ru.javaops.masterjava.persist.model.City; import ru.javaops.masterjava.persist.model.Group; import ru.javaops.masterjava.persist.model.User; +import ru.javaops.masterjava.persist.model.UserGroup; import ru.javaops.masterjava.persist.model.type.UserFlag; import ru.javaops.masterjava.upload.PayloadProcessor.FailedEmails; import ru.javaops.masterjava.xml.util.StaxStreamProcessor; @@ -14,10 +18,7 @@ import javax.xml.bind.JAXBException; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -43,27 +44,41 @@ public List process(final StaxStreamProcessor processor, Map chunk = new ArrayList<>(chunkSize); + List chunkUserGroups = new ArrayList<>(chunkSize); val unmarshaller = jaxbParser.createUnmarshaller(); List failed = new ArrayList<>(); while (processor.doUntil(XMLEvent.START_ELEMENT, "User")) { - String cityRef = processor.getAttribute("city"); // unmarshal doesn't get city ref + // unmarshal doesn't get refs + val cityRef = processor.getAttribute("city"); + val groupRefs = processor.getAttribute("groupRefs"); ru.javaops.masterjava.xml.schema.User xmlUser = unmarshaller.unmarshal(processor.getReader(), ru.javaops.masterjava.xml.schema.User.class); + String email = xmlUser.getEmail(); if (cities.get(cityRef) == null) { - failed.add(new FailedEmails(xmlUser.getEmail(), "City '" + cityRef + "' is not present in DB")); + failed.add(new FailedEmails(email, "City '" + cityRef + "' is not present in DB")); } else { - final User user = new User(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value()), cityRef); - chunk.add(user); - if (chunk.size() == chunkSize) { - addChunkFutures(chunkFutures, chunk); - chunk = new ArrayList<>(chunkSize); - id = userDao.getSeqAndSkip(chunkSize); + List groupNames = (groupRefs == null) ? + Collections.emptyList() : + Splitter.on(' ').splitToList(groupRefs); + if (!groups.keySet().containsAll(groupNames)) { + failed.add(new FailedEmails(email, "One of group from '" + groupRefs + "' is not present in DB")); + } else { + final User user = new User(id++, xmlUser.getValue(), email, UserFlag.valueOf(xmlUser.getFlag().value()), cityRef); + chunk.add(user); + List userGroups = StreamEx.of(groupNames).map(name -> new UserGroup(user.getId(), groups.get(name).getId())).toList(); + chunkUserGroups.addAll(userGroups); + if (chunk.size() == chunkSize) { + addChunkFutures(chunkFutures, chunk, chunkUserGroups); + chunk = new ArrayList<>(chunkSize); + chunkUserGroups = new ArrayList<>(chunkSize); + id = userDao.getSeqAndSkip(chunkSize); + } } } } if (!chunk.isEmpty()) { - addChunkFutures(chunkFutures, chunk); + addChunkFutures(chunkFutures, chunk, chunkUserGroups); } List allAlreadyPresents = new ArrayList<>(); @@ -83,9 +98,28 @@ public List process(final StaxStreamProcessor processor, Map>> chunkFutures, List chunk) { + private void addChunkFutures(Map>> chunkFutures, List chunk, List chunkUserGroups) { String emailRange = String.format("[%s-%s]", chunk.get(0).getEmail(), chunk.get(chunk.size() - 1).getEmail()); - Future> future = executorService.submit(() -> userDao.insertAndGetConflictEmails(chunk)); + Future> future = executorService.submit(() -> { + // https://www.programcreek.com/java-api-examples/index.php?api=org.skife.jdbi.v2.TransactionCallback + List alreadyPresentsEmails = DBIProvider.getDBI().inTransaction((handle, status) -> { + UserDao tUserDao = handle.attach(UserDao.class); + UserGroupDao tUserGroupDao = handle.attach(UserGroupDao.class); + List alreadyPresents = tUserDao.insertAndGetConflictEmails(chunk); + Set alreadyPresentsIds = StreamEx.of(alreadyPresents).map(User::getId).toSet(); + tUserGroupDao.insertBatch( + StreamEx.of(chunkUserGroups) + .filter(ug -> !alreadyPresentsIds.contains(ug.getUserId())) + .toList() + ); + return StreamEx.of(alreadyPresents).map(User::getEmail).toList(); + }); + + // let gc clear chunk after insert + chunk.clear(); + chunkUserGroups.clear(); + return alreadyPresentsEmails; + }); chunkFutures.put(emailRange, future); log.info("Submit chunk: " + emailRange); } diff --git a/web/upload/src/test/resources/payload_bad.xml b/web/upload/src/test/resources/payload_bad.xml index a6f67cdd2..bea72576b 100644 --- a/web/upload/src/test/resources/payload_bad.xml +++ b/web/upload/src/test/resources/payload_bad.xml @@ -21,7 +21,7 @@ Минск - Full Name + Full Name Admin Deleted User1 From e7c9ba46e47cd61bd7c066ee81777fcec86bb044 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:04:36 +0300 Subject: [PATCH 50/87] 7 5 customize WSDL --- .../main/webapp/WEB-INF/wsdl/mailService.wsdl | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl diff --git a/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl b/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl new file mode 100644 index 000000000..82e52ffd1 --- /dev/null +++ b/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e2298315498ba06f9f68d3e0c05c4adfb38894be Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:05:17 +0300 Subject: [PATCH 51/87] 7 6 publish customizedWSDL --- .../masterjava/service/mail/MailService.java | 10 +++++----- .../masterjava/service/mail/MailServiceImpl.java | 4 +++- .../masterjava/service/mail/MailServiceClient.java | 5 ++--- .../service/mail/MailServicePublisher.java | 14 +++++++++++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index ab4d91311..45be54108 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -5,11 +5,11 @@ import javax.jws.WebService; import java.util.List; -/** - * gkislin - * 15.11.2016 - */ -@WebService +@WebService(targetNamespace = "http://mail.javaops.ru/") +//@SOAPBinding( +// style = SOAPBinding.Style.DOCUMENT, +// use= SOAPBinding.Use.LITERAL, +// parameterStyle = SOAPBinding.ParameterStyle.WRAPPED) public interface MailService { @WebMethod diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 46bff4609..82138bcb9 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -3,7 +3,9 @@ import javax.jws.WebService; import java.util.List; -@WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService") +@WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" +// , wsdlLocation = "WEB-INF/wsdl/mailService.wsdl" +) public class MailServiceImpl implements MailService { public void sendMail(List to, List cc, String subject, String body) { MailSender.sendMail(to, cc, subject, body); diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index 8ddfed518..e388aecf3 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -12,11 +12,10 @@ public class MailServiceClient { public static void main(String[] args) throws MalformedURLException { Service service = Service.create( new URL("http://localhost:8080/mail/mailService?wsdl"), - new QName("http://mail.service.masterjava.javaops.ru/", "MailServiceImplService")); + new QName("http://mail.javaops.ru/", "MailServiceImplService")); MailService mailService = service.getPort(MailService.class); mailService.sendMail(ImmutableList.of( - new Addressee("masterjava@javaops.ru", null), - new Addressee("Bad Email ")), null, "Subject", "Body"); + new Addressee("masterjava@javaops.ru", null)), null, "Subject", "Body"); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java index bb9253cec..1901d0fc3 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java @@ -1,13 +1,25 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.collect.ImmutableList; import ru.javaops.masterjava.persist.DBITestProvider; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; import javax.xml.ws.Endpoint; +import java.io.File; +import java.util.List; public class MailServicePublisher { public static void main(String[] args) { DBITestProvider.initDBI(); - Endpoint.publish("http://localhost:8080/mail/mailService", new MailServiceImpl()); + + Endpoint endpoint = Endpoint.create(new MailServiceImpl()); + List metadata = ImmutableList.of( + new StreamSource( + new File("services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl"))); + + endpoint.setMetadata(metadata); + endpoint.publish("http://localhost:8080/mail/mailService"); } } From 88817f666f206f90946e735d88399a4e5d302f6e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:05:52 +0300 Subject: [PATCH 52/87] 7 7 deploy Tomcat --- services/mail-service/pom.xml | 38 +++++++++++++++++++ .../src/main/webapp/WEB-INF/sun-jaxws.xml | 5 +++ 2 files changed, 43 insertions(+) create mode 100644 services/mail-service/src/main/webapp/WEB-INF/sun-jaxws.xml diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index d7fe82562..46265b71e 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -39,5 +39,43 @@ test-jar test + + com.sun.xml.ws + jaxws-rt + 2.3.0 + + + org.jvnet.mimepull + mimepull + + + javax.xml.bind + jaxb-api + + + javax.annotation + javax.annotation-api + + + org.jvnet.staxex + stax-ex + + + javax.xml.soap + javax.xml.soap-api + + + + + org.jvnet.staxex + stax-ex + 1.7.8 + + + javax.activation + activation + + + diff --git a/services/mail-service/src/main/webapp/WEB-INF/sun-jaxws.xml b/services/mail-service/src/main/webapp/WEB-INF/sun-jaxws.xml new file mode 100644 index 000000000..5043df68d --- /dev/null +++ b/services/mail-service/src/main/webapp/WEB-INF/sun-jaxws.xml @@ -0,0 +1,5 @@ + + + + From 36f43b1824c358726a468f29fe311b041ca75713 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:06:57 +0300 Subject: [PATCH 53/87] 7 8 create client --- .../ru/javaops/masterjava/web/WsClient.java | 40 ++++++++++++ common/src/main/resources/hosts.conf | 4 ++ .../masterjava/service/mail/MailWSClient.java | 27 ++++++++ .../service/mail/MailWSClientMain.java | 11 ++++ .../src/test/resources/wsdl/mailService.wsdl | 65 +++++++++++++++++++ 5 files changed, 147 insertions(+) create mode 100644 common/src/main/java/ru/javaops/masterjava/web/WsClient.java create mode 100644 common/src/main/resources/hosts.conf create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java create mode 100644 services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java create mode 100644 services/mail-api/src/test/resources/wsdl/mailService.wsdl diff --git a/common/src/main/java/ru/javaops/masterjava/web/WsClient.java b/common/src/main/java/ru/javaops/masterjava/web/WsClient.java new file mode 100644 index 000000000..13f027ff4 --- /dev/null +++ b/common/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -0,0 +1,40 @@ +package ru.javaops.masterjava.web; + +import com.typesafe.config.Config; +import ru.javaops.masterjava.config.Configs; + +import javax.xml.namespace.QName; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.Service; +import java.net.URL; +import java.util.Map; + +public class WsClient { + private static Config HOSTS; + + private final Class serviceClass; + private final Service service; + private String endpointAddress; + + static { + HOSTS = Configs.getConfig("hosts.conf", "hosts"); + } + + public WsClient(URL wsdlUrl, QName qname, Class serviceClass) { + this.serviceClass = serviceClass; + this.service = Service.create(wsdlUrl, qname); + } + + public void init(String host, String endpointAddress) { + this.endpointAddress = HOSTS.getString(host) + endpointAddress; + } + + // Post is not thread-safe (http://stackoverflow.com/a/10601916/548473) + public T getPort() { + T port = service.getPort(serviceClass); + BindingProvider bp = (BindingProvider) port; + Map requestContext = bp.getRequestContext(); + requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); + return port; + } +} diff --git a/common/src/main/resources/hosts.conf b/common/src/main/resources/hosts.conf new file mode 100644 index 000000000..660c9c067 --- /dev/null +++ b/common/src/main/resources/hosts.conf @@ -0,0 +1,4 @@ +hosts { + mail = "http://localhost:8080" +} +include file("/apps/masterjava/config/hosts.conf") diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java new file mode 100644 index 000000000..7ac7a778c --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -0,0 +1,27 @@ +package ru.javaops.masterjava.service.mail; + +import com.google.common.io.Resources; +import lombok.extern.slf4j.Slf4j; +import ru.javaops.masterjava.web.WsClient; + +import javax.xml.namespace.QName; +import java.util.List; + +@Slf4j +public class MailWSClient { + private static final WsClient WS_CLIENT; + + static { + WS_CLIENT = new WsClient<>(Resources.getResource("wsdl/mailService.wsdl"), + new QName("http://mail.javaops.ru/", "MailServiceImplService"), + MailService.class); + + WS_CLIENT.init("mail", "/mail/mailService?wsdl"); + } + + + public static void sendMail(final List to, final List cc, final String subject, final String body) { + log.info("Send mail to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + WS_CLIENT.getPort().sendMail(to, cc, subject, body); + } +} diff --git a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java new file mode 100644 index 000000000..27f83cbc3 --- /dev/null +++ b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java @@ -0,0 +1,11 @@ +package ru.javaops.masterjava.service.mail; + +import com.google.common.collect.ImmutableList; + +public class MailWSClientMain { + public static void main(String[] args) { + MailWSClient.sendMail( + ImmutableList.of(new Addressee("To ")), + ImmutableList.of(new Addressee("Copy ")), "Subject", "Body"); + } +} diff --git a/services/mail-api/src/test/resources/wsdl/mailService.wsdl b/services/mail-api/src/test/resources/wsdl/mailService.wsdl new file mode 100644 index 000000000..82e52ffd1 --- /dev/null +++ b/services/mail-api/src/test/resources/wsdl/mailService.wsdl @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b7f3b8969f8eecc5bdd9eb33bc3c1b86cb9db4fd Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:34:04 +0300 Subject: [PATCH 54/87] 7 9 refactoring send to group --- .../masterjava/service/mail/Addressee.java | 6 ++--- .../masterjava/service/mail/GroupResult.java | 23 +++++++++++++++++ .../masterjava/service/mail/MailResult.java | 25 +++++++++++++++++++ .../masterjava/service/mail/MailService.java | 8 +++--- .../masterjava/service/mail/MailWSClient.java | 6 ++--- .../service/mail/MailWSClientMain.java | 8 +++--- .../masterjava/service/mail/MailSender.java | 13 +++++++--- .../service/mail/MailServiceImpl.java | 6 ++--- .../service/mail/persist/MailCase.java | 4 +-- .../service/mail/MailServiceClient.java | 4 +-- .../mail/persist/MailCaseTestData.java | 5 ++-- 11 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java index 0795631f5..f13a3faf1 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java @@ -1,13 +1,11 @@ package ru.javaops.masterjava.service.mail; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.NonNull; +import lombok.*; @Data @AllArgsConstructor @NoArgsConstructor +@EqualsAndHashCode(of = "email") public class Addressee { private @NonNull String email; private String name; diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java new file mode 100644 index 000000000..da2117a34 --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java @@ -0,0 +1,23 @@ +package ru.javaops.masterjava.service.mail; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GroupResult { + private int success; // number of successfully sent email + private List failed; // failed emails with causes + private String failedCause; // global fail cause + + @Override + public String toString() { + return "Success: " + success + '\n' + + (failed == null ? "" : "Failed: " + failed.toString() + '\n') + + (failedCause == null ? "" : "Failed cause: " + failedCause); + } +} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java new file mode 100644 index 000000000..95151f7ea --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java @@ -0,0 +1,25 @@ +package ru.javaops.masterjava.service.mail; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MailResult { + public static final String OK = "OK"; + + private @NonNull String email; + private String result; + + public boolean isOk() { + return OK.equals(result); + } + + @Override + public String toString() { + return '\'' + email + "' result '" + result + '\''; + } +} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index 45be54108..c6aaab107 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -3,7 +3,7 @@ import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; -import java.util.List; +import java.util.Set; @WebService(targetNamespace = "http://mail.javaops.ru/") //@SOAPBinding( @@ -13,9 +13,9 @@ public interface MailService { @WebMethod - void sendMail( - @WebParam(name = "to") List to, - @WebParam(name = "cc") List cc, + String sendToGroup( + @WebParam(name = "to") Set to, + @WebParam(name = "cc") Set cc, @WebParam(name = "subject") String subject, @WebParam(name = "body") String body); } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index 7ac7a778c..dc65d094a 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,7 +5,7 @@ import ru.javaops.masterjava.web.WsClient; import javax.xml.namespace.QName; -import java.util.List; +import java.util.Set; @Slf4j public class MailWSClient { @@ -20,8 +20,8 @@ public class MailWSClient { } - public static void sendMail(final List to, final List cc, final String subject, final String body) { + public static void sendToGroup(final Set to, final Set cc, final String subject, final String body) { log.info("Send mail to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - WS_CLIENT.getPort().sendMail(to, cc, subject, body); + WS_CLIENT.getPort().sendToGroup(to, cc, subject, body); } } diff --git a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java index 27f83cbc3..641c60781 100644 --- a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java +++ b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java @@ -1,11 +1,11 @@ package ru.javaops.masterjava.service.mail; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; public class MailWSClientMain { public static void main(String[] args) { - MailWSClient.sendMail( - ImmutableList.of(new Addressee("To ")), - ImmutableList.of(new Addressee("Copy ")), "Subject", "Body"); + MailWSClient.sendToGroup( + ImmutableSet.of(new Addressee("To ")), + ImmutableSet.of(new Addressee("Copy ")), "Subject", "Body"); } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index 8aada2614..a09ff7c7d 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -1,6 +1,7 @@ package ru.javaops.masterjava.service.mail; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.apache.commons.mail.EmailException; @@ -8,15 +9,20 @@ import ru.javaops.masterjava.service.mail.persist.MailCase; import ru.javaops.masterjava.service.mail.persist.MailCaseDao; -import java.util.List; +import java.util.Set; @Slf4j public class MailSender { private static final MailCaseDao MAIL_CASE_DAO = DBIProvider.getDao(MailCaseDao.class); - static void sendMail(List to, List cc, String subject, String body) { + static MailResult sendTo(Addressee to, String subject, String body) { + val state = sendToGroup(ImmutableSet.of(to), ImmutableSet.of(), subject, body); + return new MailResult(to.getEmail(), state); + } + + static String sendToGroup(Set to, Set cc, String subject, String body) { log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - String state = "OK"; + String state = MailResult.OK; try { val email = MailConfig.createHtmlEmail(); email.setSubject(subject); @@ -38,5 +44,6 @@ static void sendMail(List to, List cc, String subject, Str } MAIL_CASE_DAO.insert(MailCase.of(to, cc, subject, state)); log.info("Sent with state: " + state); + return state; } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 82138bcb9..5cca212b7 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -1,13 +1,13 @@ package ru.javaops.masterjava.service.mail; import javax.jws.WebService; -import java.util.List; +import java.util.Set; @WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" // , wsdlLocation = "WEB-INF/wsdl/mailService.wsdl" ) public class MailServiceImpl implements MailService { - public void sendMail(List to, List cc, String subject, String body) { - MailSender.sendMail(to, cc, subject, body); + public String sendToGroup(Set to, Set cc, String subject, String body) { + return MailSender.sendToGroup(to, cc, subject, body); } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java index 90f050f15..becbfd6db 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/persist/MailCase.java @@ -7,7 +7,7 @@ import ru.javaops.masterjava.service.mail.Addressee; import java.util.Date; -import java.util.List; +import java.util.Set; @Data @AllArgsConstructor @@ -21,7 +21,7 @@ public class MailCase extends BaseEntity { private String state; private Date datetime; - public static MailCase of(List to, List cc, String subject, String state){ + public static MailCase of(Set to, Set cc, String subject, String state){ return new MailCase(Joiner.on(", ").join(to), Joiner.on(", ").join(cc), subject, state, new Date()); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index e388aecf3..fee740f69 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -1,6 +1,6 @@ package ru.javaops.masterjava.service.mail; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import javax.xml.namespace.QName; import javax.xml.ws.Service; @@ -15,7 +15,7 @@ public static void main(String[] args) throws MalformedURLException { new QName("http://mail.javaops.ru/", "MailServiceImplService")); MailService mailService = service.getPort(MailService.class); - mailService.sendMail(ImmutableList.of( + mailService.sendToGroup(ImmutableSet.of( new Addressee("masterjava@javaops.ru", null)), null, "Subject", "Body"); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java index 046f00c4f..3acda5c79 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/persist/MailCaseTestData.java @@ -1,6 +1,7 @@ package ru.javaops.masterjava.service.mail.persist; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.service.mail.Addressee; @@ -15,10 +16,10 @@ public class MailCaseTestData { static final List MAIL_CASES = ImmutableList.of( MailCase.of( - ImmutableList.of( + ImmutableSet.of( new Addressee("ИмяTo1 Фамилия1 "), new Addressee("Имя2 Фамилия2 ")), - ImmutableList.of( + ImmutableSet.of( new Addressee("ИмяCc1 Фамилия1 "), new Addressee("ИмяCc2 Фамилия2 ")), "subject1", "state1" From 33d281e46c2ffa65c616ba5dca2bfc8e67c01fd4 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 14:34:47 +0300 Subject: [PATCH 55/87] 7 10 send bulk --- .../ru/javaops/masterjava/service/mail/MailService.java | 7 +++++++ .../javaops/masterjava/service/mail/MailServiceImpl.java | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index c6aaab107..6aca997fb 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -18,4 +18,11 @@ String sendToGroup( @WebParam(name = "cc") Set cc, @WebParam(name = "subject") String subject, @WebParam(name = "body") String body); + + @WebMethod + GroupResult sendBulk( + @WebParam(name = "to") Set to, + @WebParam(name = "subject") String subject, + @WebParam(name = "body") String body); + } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 5cca212b7..e0331f43e 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -10,4 +10,9 @@ public class MailServiceImpl implements MailService { public String sendToGroup(Set to, Set cc, String subject, String body) { return MailSender.sendToGroup(to, cc, subject, body); } + + @Override + public GroupResult sendBulk(Set to, String subject, String body) { + return MailServiceExecutor.sendBulk(to, subject, body); + } } From d0b3e76a43daa46730482b592b2d545d0f5aee5a Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:00:15 +0300 Subject: [PATCH 56/87] 8 1 HW7 wsdl share --- .../wsdl/mailService.wsdl | 0 parent/pom.xml | 1 + services/mail-api/pom.xml | 11 ++++ services/mail-service/pom.xml | 23 +++++++ .../main/webapp/WEB-INF/wsdl/mailService.wsdl | 65 ------------------- 5 files changed, 35 insertions(+), 65 deletions(-) rename {services/mail-api/src/test/resources => config_templates}/wsdl/mailService.wsdl (100%) delete mode 100644 services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl diff --git a/services/mail-api/src/test/resources/wsdl/mailService.wsdl b/config_templates/wsdl/mailService.wsdl similarity index 100% rename from services/mail-api/src/test/resources/wsdl/mailService.wsdl rename to config_templates/wsdl/mailService.wsdl diff --git a/parent/pom.xml b/parent/pom.xml index 09839f9da..c3e719ccc 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -18,6 +18,7 @@ 1.2.3 1.7.25 + /apps/masterjava/config/ false diff --git a/services/mail-api/pom.xml b/services/mail-api/pom.xml index 9bc8f887e..d8fe13cb0 100644 --- a/services/mail-api/pom.xml +++ b/services/mail-api/pom.xml @@ -15,6 +15,17 @@ 1.0-SNAPSHOT Mail API + + + + ${masterjava.config} + + wsdl/mailService.wsdl + + + + + ${project.groupId} diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 46265b71e..df12ad33c 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -16,6 +16,29 @@ war Mail Service + + mail + + + org.apache.maven.plugins + maven-war-plugin + 3.2.0 + + false + + + ${masterjava.config} + + wsdl/mailService.wsdl + + WEB-INF + + + + + + + ${project.groupId} diff --git a/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl b/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl deleted file mode 100644 index 82e52ffd1..000000000 --- a/services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From af41db255d62fe2343e9f35d18d61eb8622c2256 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:01:10 +0300 Subject: [PATCH 57/87] 8 2 app conf --- .../java/ru/javaops/masterjava/config/Configs.java | 14 ++++++++++---- config_templates/app.conf | 6 ++++++ config_templates/version.html | 11 +++++++++++ parent-web/pom.xml | 13 +++++++++++++ services/mail-service/pom.xml | 10 ++++++++++ .../service/mail/MailServicePublisher.java | 5 ++--- 6 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 config_templates/app.conf create mode 100644 config_templates/version.html diff --git a/common/src/main/java/ru/javaops/masterjava/config/Configs.java b/common/src/main/java/ru/javaops/masterjava/config/Configs.java index d11483da2..1c9b1ccd7 100644 --- a/common/src/main/java/ru/javaops/masterjava/config/Configs.java +++ b/common/src/main/java/ru/javaops/masterjava/config/Configs.java @@ -3,10 +3,8 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; -/** - * gkislin - * 01.11.2016 - */ +import java.io.File; + public class Configs { public static Config getConfig(String resource) { @@ -16,4 +14,12 @@ public static Config getConfig(String resource) { public static Config getConfig(String resource, String domain) { return getConfig(resource).getConfig(domain); } + + public static File getConfigFile(String path) { + return new File(AppConfig.APP_CONFIG.getString("configDir"), path); + } + + private static class AppConfig { + private static final Config APP_CONFIG = getConfig("app.conf", "app"); + } } diff --git a/config_templates/app.conf b/config_templates/app.conf new file mode 100644 index 000000000..bf966139f --- /dev/null +++ b/config_templates/app.conf @@ -0,0 +1,6 @@ +app { + groupId = ${project.groupId} + projectName = ${project.name} + version = ${project.version} + configDir = "${masterjava.config}" +} diff --git a/config_templates/version.html b/config_templates/version.html new file mode 100644 index 000000000..3cffc3842 --- /dev/null +++ b/config_templates/version.html @@ -0,0 +1,11 @@ + + + + ${project.name} + + +${project.groupId}:${project.name}:${project.version}
+configDir=${masterjava.config}
+Многопоточность. Maven. XML. Веб сервисы. + + diff --git a/parent-web/pom.xml b/parent-web/pom.xml index 6eb7245ae..6a9a23b91 100644 --- a/parent-web/pom.xml +++ b/parent-web/pom.xml @@ -24,6 +24,18 @@ 3.2.0 false + + + src/main/webapp + + + ${masterjava.config} + + version.html + + true + + @@ -75,6 +87,7 @@ true logback.xml + app.conf diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index df12ad33c..e3c6d9efa 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -26,6 +26,16 @@ false + + src/main/webapp + + + ${masterjava.config} + + version.html + + true + ${masterjava.config} diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java index 1901d0fc3..88a747cdd 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java @@ -1,12 +1,12 @@ package ru.javaops.masterjava.service.mail; import com.google.common.collect.ImmutableList; +import ru.javaops.masterjava.config.Configs; import ru.javaops.masterjava.persist.DBITestProvider; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.ws.Endpoint; -import java.io.File; import java.util.List; public class MailServicePublisher { @@ -16,8 +16,7 @@ public static void main(String[] args) { Endpoint endpoint = Endpoint.create(new MailServiceImpl()); List metadata = ImmutableList.of( - new StreamSource( - new File("services/mail-service/src/main/webapp/WEB-INF/wsdl/mailService.wsdl"))); + new StreamSource(Configs.getConfigFile("wsdl/mailService.wsdl"))); endpoint.setMetadata(metadata); endpoint.publish("http://localhost:8080/mail/mailService"); From 9cd86fd5a98a2d9d25f19e7d8acc80d987a89b09 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:01:46 +0300 Subject: [PATCH 58/87] 8 3 HW7 update wsdl --- config_templates/wsdl/mailService.wsdl | 79 ++++++++++++++++--- .../service/mail/MailServiceClient.java | 11 ++- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/config_templates/wsdl/mailService.wsdl b/config_templates/wsdl/mailService.wsdl index 82e52ffd1..9469cb197 100644 --- a/config_templates/wsdl/mailService.wsdl +++ b/config_templates/wsdl/mailService.wsdl @@ -8,10 +8,12 @@ name="MailServiceImplService"> - - + + + + - + @@ -19,6 +21,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -33,21 +68,43 @@ - - + + + + + - - + + + + + - - - + + + + + + + - + + + + + + + + + + diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index fee740f69..1495d8a5e 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -15,7 +15,14 @@ public static void main(String[] args) throws MalformedURLException { new QName("http://mail.javaops.ru/", "MailServiceImplService")); MailService mailService = service.getPort(MailService.class); - mailService.sendToGroup(ImmutableSet.of( - new Addressee("masterjava@javaops.ru", null)), null, "Subject", "Body"); + + String state = mailService.sendToGroup(ImmutableSet.of(new Addressee("masterjava@javaops.ru", null)), null, + "Group mail subject", "Group mail body"); + System.out.println("Group mail state: " + state); + + GroupResult groupResult = mailService.sendBulk(ImmutableSet.of( + new Addressee("Мастер Java "), + new Addressee("Bad Email ")), "Bulk mail subject", "Bulk mail body"); + System.out.println("\nBulk mail groupResult:\n" + groupResult); } } From df3094e3caaae9e3dfd8754cc8def691ecaae97e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:02:39 +0300 Subject: [PATCH 59/87] 8 4 HW7 webapp --- .../masterjava/service/mail/MailWSClient.java | 23 +++++- web/webapp/pom.xml | 5 ++ .../masterjava/webapp/SendServlet.java | 36 +++++++++ .../main/webapp/WEB-INF/templates/users.html | 75 ++++++++++++++----- 4 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index dc65d094a..cfca8b812 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -1,5 +1,8 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; import ru.javaops.masterjava.web.WsClient; @@ -20,8 +23,22 @@ public class MailWSClient { } - public static void sendToGroup(final Set to, final Set cc, final String subject, final String body) { - log.info("Send mail to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - WS_CLIENT.getPort().sendToGroup(to, cc, subject, body); + public static String sendToGroup(final Set to, final Set cc, final String subject, final String body) { + log.info("Send to group to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + String status = WS_CLIENT.getPort().sendToGroup(to, cc, subject, body); + log.info("Send to group with status: " + status); + return status; + } + + public static GroupResult sendBulk(final Set to, final String subject, final String body) { + log.info("Send bulk to '" + to + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); + GroupResult result = WS_CLIENT.getPort().sendBulk(to, subject, body); + log.info("Sent bulk with result: " + result); + return result; + } + + public static Set split(String addressees) { + Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split(addressees); + return ImmutableSet.copyOf(Iterables.transform(split, Addressee::new)); } } diff --git a/web/webapp/pom.xml b/web/webapp/pom.xml index b543f44ec..2784c00ae 100644 --- a/web/webapp/pom.xml +++ b/web/webapp/pom.xml @@ -26,5 +26,10 @@ persist ${project.version}
+ + ${project.groupId} + mail-api + ${project.version} +
diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java new file mode 100644 index 000000000..250b94866 --- /dev/null +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java @@ -0,0 +1,36 @@ +package ru.javaops.masterjava.webapp; + +import lombok.extern.slf4j.Slf4j; +import ru.javaops.masterjava.service.mail.GroupResult; +import ru.javaops.masterjava.service.mail.MailWSClient; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebServlet("/send") +@Slf4j +public class SendServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String result; + try { + log.info("Start sending"); + req.setCharacterEncoding("UTF-8"); + resp.setCharacterEncoding("UTF-8"); + String users = req.getParameter("users"); + String subject = req.getParameter("subject"); + String body = req.getParameter("body"); + GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body); + result = groupResult.toString(); + log.info("Processing finished with result: {}", result); + } catch (Exception e) { + log.error("Processing failed", e); + result = e.toString(); + } + resp.getWriter().write(result); + } +} diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index c03ddba96..d88ac521a 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -3,25 +3,62 @@ Users + + + - - - - - - - - - - - - - - - - - - -
#Full NameEmailFlag
+
+ + + + + + + + + + + + + + + + + + + +
#Full NameEmailFlag +
+
+

+ +

+

+
+

+

+ +

+

+
+ From bb8ba33e93d612bae78f7832444478066ee7ce4f Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:06:10 +0300 Subject: [PATCH 60/87] 8 5 soap exceptions --- .../ru/javaops/masterjava/ExceptionType.java | 32 +++++++++ config_templates/wsdl/common.xsd | 33 +++++++++ config_templates/wsdl/mailService.wsdl | 18 ++++- services/common-ws/pom.xml | 71 +++++++++++++++++++ .../main/java/ru/javaops/web/FaultInfo.java | 19 +++++ .../ru/javaops/web/WebStateException.java | 32 +++++++++ .../main/java/ru/javaops}/web/WsClient.java | 7 +- services/mail-api/pom.xml | 3 +- .../masterjava/service/mail/GroupResult.java | 4 +- .../masterjava/service/mail/MailResult.java | 2 +- .../masterjava/service/mail/MailService.java | 6 +- .../masterjava/service/mail/MailWSClient.java | 7 +- .../service/mail/MailWSClientMain.java | 8 ++- services/mail-service/pom.xml | 39 +--------- .../masterjava/service/mail/MailSender.java | 13 +++- .../service/mail/MailServiceExecutor.java | 1 - .../service/mail/MailServiceImpl.java | 6 +- .../service/mail/MailServiceClient.java | 3 +- .../service/mail/MailServicePublisher.java | 3 +- services/pom.xml | 1 + 20 files changed, 248 insertions(+), 60 deletions(-) create mode 100644 common/src/main/java/ru/javaops/masterjava/ExceptionType.java create mode 100644 config_templates/wsdl/common.xsd create mode 100644 services/common-ws/pom.xml create mode 100644 services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java create mode 100644 services/common-ws/src/main/java/ru/javaops/web/WebStateException.java rename {common/src/main/java/ru/javaops/masterjava => services/common-ws/src/main/java/ru/javaops}/web/WsClient.java (81%) diff --git a/common/src/main/java/ru/javaops/masterjava/ExceptionType.java b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java new file mode 100644 index 000000000..829f7b29b --- /dev/null +++ b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java @@ -0,0 +1,32 @@ +package ru.javaops.masterjava; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ExceptionType { + SYSTEM("Системная ошибка"), + DATA_BASE("Ошибка базы данных"), + STATE("Неверное состояние приложения"), + AUTHORIZATION("Ошибка авторизации"), + CONFIGURATION("Ошибка конфигурирования"), + ILLEGAL_ARGUMENT("Неверный аргумент"), + BPM("Ошибка бизнес-процесса"), + FILE("Ошибка при работе с файловой системой"), + REPORTS("Ошибка в отчете"), + EMAIL("Ошибка при отправке почты"), + TEMPLATE("Ошибка в шаблонах"), + ONE_C("Ошибка в системе 1C"), + ATTACH("Ошибка вложенного файла"), + LDAP("Ошибка соединения с LDAP"), + SOAP("Ошибка веб-сервиса"), + NETWORK("Сетевая Ошибка"); + + final private String descr; + + @Override + public String toString() { + return name() + " (" + descr + ')'; + } +} diff --git a/config_templates/wsdl/common.xsd b/config_templates/wsdl/common.xsd new file mode 100644 index 000000000..6686c068b --- /dev/null +++ b/config_templates/wsdl/common.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config_templates/wsdl/mailService.wsdl b/config_templates/wsdl/mailService.wsdl index 9469cb197..2b6259052 100644 --- a/config_templates/wsdl/mailService.wsdl +++ b/config_templates/wsdl/mailService.wsdl @@ -3,11 +3,14 @@ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://mail.javaops.ru/" xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:common="http://common.javaops.ru/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://mail.javaops.ru/" name="MailServiceImplService"> + + @@ -40,7 +43,6 @@ - @@ -80,17 +82,25 @@ + + + + + + @@ -103,6 +113,9 @@ + + + @@ -112,6 +125,9 @@ + + + diff --git a/services/common-ws/pom.xml b/services/common-ws/pom.xml new file mode 100644 index 000000000..755f7d8af --- /dev/null +++ b/services/common-ws/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + ru.javaops + parent + ../../parent/pom.xml + 1.0-SNAPSHOT + + + common-ws + 1.0-SNAPSHOT + Common Web Services + + + + ${project.groupId} + common + ${project.version} + + + + com.sun.xml.ws + jaxws-rt + 2.3.0 + + + org.jvnet.mimepull + mimepull + + + javax.xml.bind + jaxb-api + + + javax.annotation + javax.annotation-api + + + org.jvnet.staxex + stax-ex + + + javax.xml.soap + javax.xml.soap-api + + + + + org.jvnet.staxex + stax-ex + 1.7.8 + + + javax.activation + activation + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + diff --git a/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java b/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java new file mode 100644 index 000000000..c2c644ecc --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java @@ -0,0 +1,19 @@ +package ru.javaops.web; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import ru.javaops.masterjava.ExceptionType; + +@Data +@RequiredArgsConstructor +@NoArgsConstructor +public class FaultInfo { + private @NonNull ExceptionType type; + + @Override + public String toString() { + return type.toString(); + } +} diff --git a/services/common-ws/src/main/java/ru/javaops/web/WebStateException.java b/services/common-ws/src/main/java/ru/javaops/web/WebStateException.java new file mode 100644 index 000000000..6268f58b0 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/web/WebStateException.java @@ -0,0 +1,32 @@ +package ru.javaops.web; + + +import lombok.Getter; +import ru.javaops.masterjava.ExceptionType; + +import javax.xml.ws.WebFault; + +@WebFault(name = "webStateException", targetNamespace = "http://common.javaops.ru/") +@Getter +public class WebStateException extends Exception { + private FaultInfo faultInfo; + + public WebStateException(String cause, FaultInfo faultInfo) { + super(cause); + this.faultInfo = faultInfo; + } + + public WebStateException(String cause, ExceptionType type) { + this(cause, new FaultInfo(type)); + } + + public WebStateException(Throwable cause, ExceptionType type) { + super(cause); + this.faultInfo = new FaultInfo(type); + } + + @Override + public String toString() { + return faultInfo.toString() + '\n' + super.toString(); + } +} diff --git a/common/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/web/WsClient.java similarity index 81% rename from common/src/main/java/ru/javaops/masterjava/web/WsClient.java rename to services/common-ws/src/main/java/ru/javaops/web/WsClient.java index 13f027ff4..a2d62ffdb 100644 --- a/common/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/web/WsClient.java @@ -1,6 +1,7 @@ -package ru.javaops.masterjava.web; +package ru.javaops.web; import com.typesafe.config.Config; +import ru.javaops.masterjava.ExceptionType; import ru.javaops.masterjava.config.Configs; import javax.xml.namespace.QName; @@ -37,4 +38,8 @@ public T getPort() { requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); return port; } + + public static WebStateException getWebStateException(Throwable t, ExceptionType type) { + return (t instanceof WebStateException) ? (WebStateException) t : new WebStateException(t, type); + } } diff --git a/services/mail-api/pom.xml b/services/mail-api/pom.xml index d8fe13cb0..d3fa05788 100644 --- a/services/mail-api/pom.xml +++ b/services/mail-api/pom.xml @@ -21,6 +21,7 @@ ${masterjava.config} wsdl/mailService.wsdl + wsdl/common.xsd @@ -29,7 +30,7 @@ ${project.groupId} - common + common-ws ${project.version} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java index da2117a34..1ab1614b7 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java @@ -12,12 +12,10 @@ public class GroupResult { private int success; // number of successfully sent email private List failed; // failed emails with causes - private String failedCause; // global fail cause @Override public String toString() { return "Success: " + success + '\n' + - (failed == null ? "" : "Failed: " + failed.toString() + '\n') + - (failedCause == null ? "" : "Failed cause: " + failedCause); + (failed == null ? "" : "Failed: " + failed.toString()); } } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java index 95151f7ea..0202b330f 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java @@ -20,6 +20,6 @@ public boolean isOk() { @Override public String toString() { - return '\'' + email + "' result '" + result + '\''; + return "'" + email + "' result: " + result; } } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index 6aca997fb..a4b2909d0 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -1,5 +1,7 @@ package ru.javaops.masterjava.service.mail; +import ru.javaops.web.WebStateException; + import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; @@ -17,12 +19,12 @@ String sendToGroup( @WebParam(name = "to") Set to, @WebParam(name = "cc") Set cc, @WebParam(name = "subject") String subject, - @WebParam(name = "body") String body); + @WebParam(name = "body") String body) throws WebStateException; @WebMethod GroupResult sendBulk( @WebParam(name = "to") Set to, @WebParam(name = "subject") String subject, - @WebParam(name = "body") String body); + @WebParam(name = "body") String body) throws WebStateException; } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index cfca8b812..4c9c5f00a 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,7 +5,8 @@ import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; -import ru.javaops.masterjava.web.WsClient; +import ru.javaops.web.WebStateException; +import ru.javaops.web.WsClient; import javax.xml.namespace.QName; import java.util.Set; @@ -23,14 +24,14 @@ public class MailWSClient { } - public static String sendToGroup(final Set to, final Set cc, final String subject, final String body) { + public static String sendToGroup(final Set to, final Set cc, final String subject, final String body) throws WebStateException { log.info("Send to group to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); String status = WS_CLIENT.getPort().sendToGroup(to, cc, subject, body); log.info("Send to group with status: " + status); return status; } - public static GroupResult sendBulk(final Set to, final String subject, final String body) { + public static GroupResult sendBulk(final Set to, final String subject, final String body) throws WebStateException { log.info("Send bulk to '" + to + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); GroupResult result = WS_CLIENT.getPort().sendBulk(to, subject, body); log.info("Sent bulk with result: " + result); diff --git a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java index 641c60781..2a117a26d 100644 --- a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java +++ b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java @@ -1,11 +1,15 @@ package ru.javaops.masterjava.service.mail; import com.google.common.collect.ImmutableSet; +import lombok.extern.slf4j.Slf4j; +import ru.javaops.web.WebStateException; +@Slf4j public class MailWSClientMain { - public static void main(String[] args) { - MailWSClient.sendToGroup( + public static void main(String[] args) throws WebStateException { + String state = MailWSClient.sendToGroup( ImmutableSet.of(new Addressee("To ")), ImmutableSet.of(new Addressee("Copy ")), "Subject", "Body"); + System.out.println(state); } } diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index e3c6d9efa..85767563f 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -40,6 +40,7 @@ ${masterjava.config} wsdl/mailService.wsdl + wsdl/common.xsd WEB-INF @@ -72,43 +73,5 @@ test-jar test
- - com.sun.xml.ws - jaxws-rt - 2.3.0 - - - org.jvnet.mimepull - mimepull - - - javax.xml.bind - jaxb-api - - - javax.annotation - javax.annotation-api - - - org.jvnet.staxex - stax-ex - - - javax.xml.soap - javax.xml.soap-api - - - - - org.jvnet.staxex - stax-ex - 1.7.8 - - - javax.activation - activation - - -
diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index a09ff7c7d..23d51ffeb 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -5,9 +5,11 @@ import lombok.extern.slf4j.Slf4j; import lombok.val; import org.apache.commons.mail.EmailException; +import ru.javaops.masterjava.ExceptionType; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.service.mail.persist.MailCase; import ru.javaops.masterjava.service.mail.persist.MailCaseDao; +import ru.javaops.web.WebStateException; import java.util.Set; @@ -15,12 +17,12 @@ public class MailSender { private static final MailCaseDao MAIL_CASE_DAO = DBIProvider.getDao(MailCaseDao.class); - static MailResult sendTo(Addressee to, String subject, String body) { + static MailResult sendTo(Addressee to, String subject, String body) throws WebStateException { val state = sendToGroup(ImmutableSet.of(to), ImmutableSet.of(), subject, body); return new MailResult(to.getEmail(), state); } - static String sendToGroup(Set to, Set cc, String subject, String body) { + static String sendToGroup(Set to, Set cc, String subject, String body) throws WebStateException { log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); String state = MailResult.OK; try { @@ -42,7 +44,12 @@ static String sendToGroup(Set to, Set cc, String subject, log.error(e.getMessage(), e); state = e.getMessage(); } - MAIL_CASE_DAO.insert(MailCase.of(to, cc, subject, state)); + try { + MAIL_CASE_DAO.insert(MailCase.of(to, cc, subject, state)); + } catch (Exception e) { + log.error("Mail history saving exception", e); + throw new WebStateException(e, ExceptionType.DATA_BASE); + } log.info("Sent with state: " + state); return state; } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java index 545bded32..1ee57c01a 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java @@ -9,7 +9,6 @@ public class MailServiceExecutor { 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"; public GroupResult sendToList(final String template, final Set emails) throws Exception { return new GroupResult(0, Collections.emptyList(), null); diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index e0331f43e..65215458e 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -1,5 +1,7 @@ package ru.javaops.masterjava.service.mail; +import ru.javaops.web.WebStateException; + import javax.jws.WebService; import java.util.Set; @@ -7,12 +9,12 @@ // , wsdlLocation = "WEB-INF/wsdl/mailService.wsdl" ) public class MailServiceImpl implements MailService { - public String sendToGroup(Set to, Set cc, String subject, String body) { + public String sendToGroup(Set to, Set cc, String subject, String body) throws WebStateException { return MailSender.sendToGroup(to, cc, subject, body); } @Override - public GroupResult sendBulk(Set to, String subject, String body) { + public GroupResult sendBulk(Set to, String subject, String body) throws WebStateException { return MailServiceExecutor.sendBulk(to, subject, body); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index 1495d8a5e..5ec260012 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -1,6 +1,7 @@ package ru.javaops.masterjava.service.mail; import com.google.common.collect.ImmutableSet; +import ru.javaops.web.WebStateException; import javax.xml.namespace.QName; import javax.xml.ws.Service; @@ -9,7 +10,7 @@ public class MailServiceClient { - public static void main(String[] args) throws MalformedURLException { + public static void main(String[] args) throws MalformedURLException, WebStateException { Service service = Service.create( new URL("http://localhost:8080/mail/mailService?wsdl"), new QName("http://mail.javaops.ru/", "MailServiceImplService")); diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java index 88a747cdd..1d77f0f27 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServicePublisher.java @@ -16,7 +16,8 @@ public static void main(String[] args) { Endpoint endpoint = Endpoint.create(new MailServiceImpl()); List metadata = ImmutableList.of( - new StreamSource(Configs.getConfigFile("wsdl/mailService.wsdl"))); + new StreamSource(Configs.getConfigFile("wsdl/mailService.wsdl")), + new StreamSource(Configs.getConfigFile("wsdl/common.xsd"))); endpoint.setMetadata(metadata); endpoint.publish("http://localhost:8080/mail/mailService"); diff --git a/services/pom.xml b/services/pom.xml index 62ffc5e38..4d04cba32 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -9,6 +9,7 @@ Services + common-ws mail-api mail-service From 40c67d9664bc865d1851f81aa15adc62a2d1c827 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:07:04 +0300 Subject: [PATCH 61/87] 8 6 fix wsdl and schema --- .../java/ru/javaops/masterjava/ExceptionType.java | 3 +++ config_templates/wsdl/common.xsd | 6 ++++-- .../src/main/java/ru/javaops/web/FaultInfo.java | 3 +++ .../javaops/masterjava/service/mail/Addressee.java | 10 +++++++++- .../javaops/masterjava/service/mail/GroupResult.java | 7 +++++-- .../javaops/masterjava/service/mail/MailResult.java | 12 ++++++++++-- services/mail-service/src/main/resources/mail.conf | 2 +- 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/ru/javaops/masterjava/ExceptionType.java b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java index 829f7b29b..90b6c3e05 100644 --- a/common/src/main/java/ru/javaops/masterjava/ExceptionType.java +++ b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java @@ -3,8 +3,11 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import javax.xml.bind.annotation.XmlType; + @Getter @AllArgsConstructor +@XmlType(namespace = "http://common.javaops.ru/") public enum ExceptionType { SYSTEM("Системная ошибка"), DATA_BASE("Ошибка базы данных"), diff --git a/config_templates/wsdl/common.xsd b/config_templates/wsdl/common.xsd index 6686c068b..9dd8d13a6 100644 --- a/config_templates/wsdl/common.xsd +++ b/config_templates/wsdl/common.xsd @@ -1,12 +1,14 @@ + - + - + diff --git a/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java b/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java index c2c644ecc..d3525c5b9 100644 --- a/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java +++ b/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java @@ -6,9 +6,12 @@ import lombok.RequiredArgsConstructor; import ru.javaops.masterjava.ExceptionType; +import javax.xml.bind.annotation.XmlType; + @Data @RequiredArgsConstructor @NoArgsConstructor +@XmlType(namespace = "http://common.javaops.ru/") public class FaultInfo { private @NonNull ExceptionType type; diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java index f13a3faf1..b4e76c741 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Addressee.java @@ -2,12 +2,20 @@ import lombok.*; -@Data +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; + @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(of = "email") +@XmlAccessorType(XmlAccessType.FIELD) +@Getter public class Addressee { + @XmlAttribute private @NonNull String email; + @XmlValue private String name; public Addressee(String email) { diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java index 1ab1614b7..dfb0b5406 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/GroupResult.java @@ -1,14 +1,17 @@ package ru.javaops.masterjava.service.mail; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; import java.util.List; -@Data @AllArgsConstructor @NoArgsConstructor +@XmlAccessorType(XmlAccessType.FIELD) +@Getter public class GroupResult { private int success; // number of successfully sent email private List failed; // failed emails with causes diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java index 0202b330f..a25c947af 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailResult.java @@ -1,17 +1,25 @@ package ru.javaops.masterjava.service.mail; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; -@Data +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; + @AllArgsConstructor @NoArgsConstructor +@XmlAccessorType(XmlAccessType.FIELD) +@Getter public class MailResult { public static final String OK = "OK"; + @XmlAttribute private @NonNull String email; + @XmlValue private String result; public boolean isOk() { diff --git a/services/mail-service/src/main/resources/mail.conf b/services/mail-service/src/main/resources/mail.conf index fdc6ca092..dc86da5a1 100644 --- a/services/mail-service/src/main/resources/mail.conf +++ b/services/mail-service/src/main/resources/mail.conf @@ -5,7 +5,7 @@ mail { password: password useSSL: true useTLS: false - debug: true + debug: false fromName: MasterJava } From fbbb72867b486b7cea2c46a91144ea9f90fada14 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 15:07:28 +0300 Subject: [PATCH 62/87] 8 7 mvn plugins --- persist/pom.xml | 24 +++++++++++++++++++ services/mail-service/pom.xml | 43 +++++++++++++++-------------------- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/persist/pom.xml b/persist/pom.xml index f6f0732bf..8bf515655 100644 --- a/persist/pom.xml +++ b/persist/pom.xml @@ -31,6 +31,30 @@ + + + + org.liquibase + liquibase-maven-plugin + 3.5.3 + + ../sql/databaseChangeLog.sql + org.postgresql.Driver + jdbc:postgresql://localhost:5432/masterjava + user + password + + + + + + process-resources + + update + + + + diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 85767563f..334f404d6 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -21,31 +21,24 @@ org.apache.maven.plugins - maven-war-plugin - 3.2.0 - - false - - - src/main/webapp - - - ${masterjava.config} - - version.html - - true - - - ${masterjava.config} - - wsdl/mailService.wsdl - wsdl/common.xsd - - WEB-INF - - - + maven-antrun-plugin + 1.8 + + + prepare-package + + run + + + + + + + + + + + From 1203287c0111aa9543ad020906e800a72966070e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:41:49 +0300 Subject: [PATCH 63/87] 9 0 fix package --- .../main/java/ru/javaops/{ => masterjava}/web/FaultInfo.java | 2 +- .../ru/javaops/{ => masterjava}/web/WebStateException.java | 2 +- .../main/java/ru/javaops/{ => masterjava}/web/WsClient.java | 2 +- .../java/ru/javaops/masterjava/service/mail/MailService.java | 2 +- .../java/ru/javaops/masterjava/service/mail/MailWSClient.java | 4 ++-- .../ru/javaops/masterjava/service/mail/MailWSClientMain.java | 2 +- .../java/ru/javaops/masterjava/service/mail/MailSender.java | 2 +- .../ru/javaops/masterjava/service/mail/MailServiceImpl.java | 2 +- .../ru/javaops/masterjava/service/mail/MailServiceClient.java | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) rename services/common-ws/src/main/java/ru/javaops/{ => masterjava}/web/FaultInfo.java (92%) rename services/common-ws/src/main/java/ru/javaops/{ => masterjava}/web/WebStateException.java (95%) rename services/common-ws/src/main/java/ru/javaops/{ => masterjava}/web/WsClient.java (97%) diff --git a/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/FaultInfo.java similarity index 92% rename from services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java rename to services/common-ws/src/main/java/ru/javaops/masterjava/web/FaultInfo.java index d3525c5b9..ed0c09b1f 100644 --- a/services/common-ws/src/main/java/ru/javaops/web/FaultInfo.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/FaultInfo.java @@ -1,4 +1,4 @@ -package ru.javaops.web; +package ru.javaops.masterjava.web; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/services/common-ws/src/main/java/ru/javaops/web/WebStateException.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WebStateException.java similarity index 95% rename from services/common-ws/src/main/java/ru/javaops/web/WebStateException.java rename to services/common-ws/src/main/java/ru/javaops/masterjava/web/WebStateException.java index 6268f58b0..90f9fc44d 100644 --- a/services/common-ws/src/main/java/ru/javaops/web/WebStateException.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WebStateException.java @@ -1,4 +1,4 @@ -package ru.javaops.web; +package ru.javaops.masterjava.web; import lombok.Getter; diff --git a/services/common-ws/src/main/java/ru/javaops/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java similarity index 97% rename from services/common-ws/src/main/java/ru/javaops/web/WsClient.java rename to services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index a2d62ffdb..f2b2e80d4 100644 --- a/services/common-ws/src/main/java/ru/javaops/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -1,4 +1,4 @@ -package ru.javaops.web; +package ru.javaops.masterjava.web; import com.typesafe.config.Config; import ru.javaops.masterjava.ExceptionType; diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index a4b2909d0..e7ac35588 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -1,6 +1,6 @@ package ru.javaops.masterjava.service.mail; -import ru.javaops.web.WebStateException; +import ru.javaops.masterjava.web.WebStateException; import javax.jws.WebMethod; import javax.jws.WebParam; diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index 4c9c5f00a..134dc1e02 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,8 +5,8 @@ import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; -import ru.javaops.web.WebStateException; -import ru.javaops.web.WsClient; +import ru.javaops.masterjava.web.WebStateException; +import ru.javaops.masterjava.web.WsClient; import javax.xml.namespace.QName; import java.util.Set; diff --git a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java index 2a117a26d..4c1d3187b 100644 --- a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java +++ b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java @@ -2,7 +2,7 @@ import com.google.common.collect.ImmutableSet; import lombok.extern.slf4j.Slf4j; -import ru.javaops.web.WebStateException; +import ru.javaops.masterjava.web.WebStateException; @Slf4j public class MailWSClientMain { diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index 23d51ffeb..0c599144c 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -9,7 +9,7 @@ import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.service.mail.persist.MailCase; import ru.javaops.masterjava.service.mail.persist.MailCaseDao; -import ru.javaops.web.WebStateException; +import ru.javaops.masterjava.web.WebStateException; import java.util.Set; diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 65215458e..919e5a1dd 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -1,6 +1,6 @@ package ru.javaops.masterjava.service.mail; -import ru.javaops.web.WebStateException; +import ru.javaops.masterjava.web.WebStateException; import javax.jws.WebService; import java.util.Set; diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index 5ec260012..f61a8513b 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -1,7 +1,7 @@ package ru.javaops.masterjava.service.mail; import com.google.common.collect.ImmutableSet; -import ru.javaops.web.WebStateException; +import ru.javaops.masterjava.web.WebStateException; import javax.xml.namespace.QName; import javax.xml.ws.Service; From 8150d79577ef0db5290eb20bc768fa72d3fddd66 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:43:21 +0300 Subject: [PATCH 64/87] 9 1 HW8 service attach --- .../ru/javaops/masterjava/ExceptionType.java | 2 +- config_templates/wsdl/common.xsd | 2 +- config_templates/wsdl/mailService.wsdl | 10 +++++++++ .../masterjava/service/mail/Attachment.java | 22 +++++++++++++++++++ .../masterjava/service/mail/MailService.java | 7 ++++-- .../masterjava/service/mail/MailWSClient.java | 9 ++++---- .../service/mail/MailWSClientMain.java | 12 ++++++++-- .../service/mail/MailServiceImpl.java | 5 +++-- .../service/mail/MailServiceClient.java | 11 ++++++++-- .../masterjava/webapp/SendServlet.java | 2 +- 10 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Attachment.java diff --git a/common/src/main/java/ru/javaops/masterjava/ExceptionType.java b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java index 90b6c3e05..f0152574c 100644 --- a/common/src/main/java/ru/javaops/masterjava/ExceptionType.java +++ b/common/src/main/java/ru/javaops/masterjava/ExceptionType.java @@ -21,7 +21,7 @@ public enum ExceptionType { EMAIL("Ошибка при отправке почты"), TEMPLATE("Ошибка в шаблонах"), ONE_C("Ошибка в системе 1C"), - ATTACH("Ошибка вложенного файла"), + ATTACHMENT("Ошибка вложенного файла"), LDAP("Ошибка соединения с LDAP"), SOAP("Ошибка веб-сервиса"), NETWORK("Сетевая Ошибка"); diff --git a/config_templates/wsdl/common.xsd b/config_templates/wsdl/common.xsd index 9dd8d13a6..1f6f8b15c 100644 --- a/config_templates/wsdl/common.xsd +++ b/config_templates/wsdl/common.xsd @@ -26,7 +26,7 @@ - + diff --git a/config_templates/wsdl/mailService.wsdl b/config_templates/wsdl/mailService.wsdl index 2b6259052..cc76456c8 100644 --- a/config_templates/wsdl/mailService.wsdl +++ b/config_templates/wsdl/mailService.wsdl @@ -4,6 +4,7 @@ xmlns:tns="http://mail.javaops.ru/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:common="http://common.javaops.ru/" + xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://mail.javaops.ru/" name="MailServiceImplService"> @@ -22,6 +23,7 @@ + @@ -35,6 +37,7 @@ + @@ -68,6 +71,13 @@ + + + + + + + diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Attachment.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Attachment.java new file mode 100644 index 000000000..d383528ee --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/Attachment.java @@ -0,0 +1,22 @@ +package ru.javaops.masterjava.service.mail; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.activation.DataHandler; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlMimeType; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@XmlAccessorType(XmlAccessType.FIELD) +public class Attachment { + // http://stackoverflow.com/questions/12250423/jax-ws-datahandler-getname-is-blank-when-called-from-client-side + protected String name; + + @XmlMimeType("application/octet-stream") + private DataHandler dataHandler; +} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java index e7ac35588..5c824b3a1 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailService.java @@ -5,6 +5,7 @@ import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; +import java.util.List; import java.util.Set; @WebService(targetNamespace = "http://mail.javaops.ru/") @@ -19,12 +20,14 @@ String sendToGroup( @WebParam(name = "to") Set to, @WebParam(name = "cc") Set cc, @WebParam(name = "subject") String subject, - @WebParam(name = "body") String body) throws WebStateException; + @WebParam(name = "body") String body, + @WebParam(name = "attachments") List attachments) throws WebStateException; @WebMethod GroupResult sendBulk( @WebParam(name = "to") Set to, @WebParam(name = "subject") String subject, - @WebParam(name = "body") String body) throws WebStateException; + @WebParam(name = "body") String body, + @WebParam(name = "attachments") List attachments) throws WebStateException; } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index 134dc1e02..ce0ef0fe0 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -9,6 +9,7 @@ import ru.javaops.masterjava.web.WsClient; import javax.xml.namespace.QName; +import java.util.List; import java.util.Set; @Slf4j @@ -24,16 +25,16 @@ public class MailWSClient { } - public static String sendToGroup(final Set to, final Set cc, final String subject, final String body) throws WebStateException { + public static String sendToGroup(final Set to, final Set cc, final String subject, final String body, List attachments) throws WebStateException { log.info("Send to group to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - String status = WS_CLIENT.getPort().sendToGroup(to, cc, subject, body); + String status = WS_CLIENT.getPort().sendToGroup(to, cc, subject, body, attachments); log.info("Send to group with status: " + status); return status; } - public static GroupResult sendBulk(final Set to, final String subject, final String body) throws WebStateException { + public static GroupResult sendBulk(final Set to, final String subject, final String body, List attachments) throws WebStateException { log.info("Send bulk to '" + to + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - GroupResult result = WS_CLIENT.getPort().sendBulk(to, subject, body); + GroupResult result = WS_CLIENT.getPort().sendBulk(to, subject, body, attachments); log.info("Sent bulk with result: " + result); return result; } diff --git a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java index 4c1d3187b..72433d1fe 100644 --- a/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java +++ b/services/mail-api/src/test/java/ru/javaops/masterjava/service/mail/MailWSClientMain.java @@ -1,15 +1,23 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import lombok.extern.slf4j.Slf4j; import ru.javaops.masterjava.web.WebStateException; +import javax.activation.DataHandler; +import java.io.File; +import java.net.MalformedURLException; + @Slf4j public class MailWSClientMain { - public static void main(String[] args) throws WebStateException { + public static void main(String[] args) throws WebStateException, MalformedURLException { String state = MailWSClient.sendToGroup( ImmutableSet.of(new Addressee("To ")), - ImmutableSet.of(new Addressee("Copy ")), "Subject", "Body"); + ImmutableSet.of(new Addressee("Copy ")), "Subject", "Body", + ImmutableList.of(new Attachment("version.html", + new DataHandler(new File("config_templates/version.html").toURI().toURL())) + )); System.out.println(state); } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 919e5a1dd..ef9ea7e47 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -3,18 +3,19 @@ import ru.javaops.masterjava.web.WebStateException; import javax.jws.WebService; +import java.util.List; import java.util.Set; @WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" // , wsdlLocation = "WEB-INF/wsdl/mailService.wsdl" ) public class MailServiceImpl implements MailService { - public String sendToGroup(Set to, Set cc, String subject, String body) throws WebStateException { + public String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { return MailSender.sendToGroup(to, cc, subject, body); } @Override - public GroupResult sendBulk(Set to, String subject, String body) throws WebStateException { + public GroupResult sendBulk(Set to, String subject, String body, List attachments) throws WebStateException { return MailServiceExecutor.sendBulk(to, subject, body); } } diff --git a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java index f61a8513b..6aaa54317 100644 --- a/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java +++ b/services/mail-service/src/test/java/ru/javaops/masterjava/service/mail/MailServiceClient.java @@ -1,12 +1,16 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import ru.javaops.masterjava.web.WebStateException; +import javax.activation.DataHandler; import javax.xml.namespace.QName; import javax.xml.ws.Service; +import java.io.File; import java.net.MalformedURLException; import java.net.URL; +import java.util.List; public class MailServiceClient { @@ -15,15 +19,18 @@ public static void main(String[] args) throws MalformedURLException, WebStateExc new URL("http://localhost:8080/mail/mailService?wsdl"), new QName("http://mail.javaops.ru/", "MailServiceImplService")); + List attachments = ImmutableList.of( + new Attachment("version.html", new DataHandler(new File("config_templates/version.html").toURI().toURL()))); + MailService mailService = service.getPort(MailService.class); String state = mailService.sendToGroup(ImmutableSet.of(new Addressee("masterjava@javaops.ru", null)), null, - "Group mail subject", "Group mail body"); + "Group mail subject", "Group mail body", attachments); System.out.println("Group mail state: " + state); GroupResult groupResult = mailService.sendBulk(ImmutableSet.of( new Addressee("Мастер Java "), - new Addressee("Bad Email ")), "Bulk mail subject", "Bulk mail body"); + new Addressee("Bad Email ")), "Bulk mail subject", "Bulk mail body", attachments); System.out.println("\nBulk mail groupResult:\n" + groupResult); } } diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java index 250b94866..3be937f59 100644 --- a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java @@ -24,7 +24,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String users = req.getParameter("users"); String subject = req.getParameter("subject"); String body = req.getParameter("body"); - GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body); + GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body, null); result = groupResult.toString(); log.info("Processing finished with result: {}", result); } catch (Exception e) { From cd54bdeedbf9d33587c99992cc05a3355f613e22 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:44:07 +0300 Subject: [PATCH 65/87] 9 2 HW8 MTOM --- services/common-ws/pom.xml | 6 ++++++ .../main/java/ru/javaops/masterjava/web/WsClient.java | 5 +++-- .../ru/javaops/masterjava/service/mail/MailWSClient.java | 9 +++++++-- .../javaops/masterjava/service/mail/MailServiceImpl.java | 3 +++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/services/common-ws/pom.xml b/services/common-ws/pom.xml index 755f7d8af..fde1805ea 100644 --- a/services/common-ws/pom.xml +++ b/services/common-ws/pom.xml @@ -61,6 +61,12 @@ + + org.jvnet.mimepull + mimepull + 1.9.7 + + javax.servlet javax.servlet-api diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index f2b2e80d4..6ca32585a 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -7,6 +7,7 @@ import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; +import javax.xml.ws.WebServiceFeature; import java.net.URL; import java.util.Map; @@ -31,8 +32,8 @@ public void init(String host, String endpointAddress) { } // Post is not thread-safe (http://stackoverflow.com/a/10601916/548473) - public T getPort() { - T port = service.getPort(serviceClass); + public T getPort(WebServiceFeature... features) { + T port = service.getPort(serviceClass, features); BindingProvider bp = (BindingProvider) port; Map requestContext = bp.getRequestContext(); requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index ce0ef0fe0..e6b332f45 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -9,6 +9,7 @@ import ru.javaops.masterjava.web.WsClient; import javax.xml.namespace.QName; +import javax.xml.ws.soap.MTOMFeature; import java.util.List; import java.util.Set; @@ -27,18 +28,22 @@ public class MailWSClient { public static String sendToGroup(final Set to, final Set cc, final String subject, final String body, List attachments) throws WebStateException { log.info("Send to group to '" + to + "' cc '" + cc + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - String status = WS_CLIENT.getPort().sendToGroup(to, cc, subject, body, attachments); + String status = getPort().sendToGroup(to, cc, subject, body, attachments); log.info("Send to group with status: " + status); return status; } public static GroupResult sendBulk(final Set to, final String subject, final String body, List attachments) throws WebStateException { log.info("Send bulk to '" + to + "' subject '" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); - GroupResult result = WS_CLIENT.getPort().sendBulk(to, subject, body, attachments); + GroupResult result = getPort().sendBulk(to, subject, body, attachments); log.info("Sent bulk with result: " + result); return result; } + private static MailService getPort() { + return WS_CLIENT.getPort(new MTOMFeature(1024)); + } + public static Set split(String addressees) { Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split(addressees); return ImmutableSet.copyOf(Iterables.transform(split, Addressee::new)); diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index ef9ea7e47..b10c1faea 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -3,12 +3,15 @@ import ru.javaops.masterjava.web.WebStateException; import javax.jws.WebService; +import javax.xml.ws.soap.MTOM; import java.util.List; import java.util.Set; @WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" // , wsdlLocation = "WEB-INF/wsdl/mailService.wsdl" ) +//@StreamingAttachment(parseEagerly=true, memoryThreshold=40000L) +@MTOM public class MailServiceImpl implements MailService { public String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { return MailSender.sendToGroup(to, cc, subject, body); From 2938e7101ad98c8a9dcc6c281bad51752d738295 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:45:15 +0300 Subject: [PATCH 66/87] 9 3 HW8 webapp attach --- .../service/mail/util/Attachments.java | 47 +++++++++++++++++++ .../masterjava/webapp/SendServlet.java | 10 +++- .../main/webapp/WEB-INF/templates/users.html | 29 +++++++++--- 3 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java new file mode 100644 index 000000000..f8dddeef3 --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java @@ -0,0 +1,47 @@ +package ru.javaops.masterjava.service.mail.util; + +import lombok.AllArgsConstructor; +import ru.javaops.masterjava.service.mail.Attachment; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Attachments { + public static Attachment getAttachment(String name, InputStream inputStream) { + return new Attachment(name, new DataHandler(new InputStreamDataSource(inputStream))); + } + + // http://stackoverflow.com/a/10783565/548473 + @AllArgsConstructor + private static class InputStreamDataSource implements DataSource { + private InputStream inputStream; + + @Override + public InputStream getInputStream() throws IOException { + if (inputStream == null) { + throw new IOException("Second getInputStream() call is not supported"); + } + InputStream res = inputStream; + inputStream = null; + return res; + } + + @Override + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public String getContentType() { + return "application/octet-stream"; + } + + @Override + public String getName() { + return ""; + } + } +} diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java index 3be937f59..b6ac85969 100644 --- a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java @@ -1,18 +1,23 @@ package ru.javaops.masterjava.webapp; +import com.google.common.collect.ImmutableList; import lombok.extern.slf4j.Slf4j; import ru.javaops.masterjava.service.mail.GroupResult; import ru.javaops.masterjava.service.mail.MailWSClient; +import ru.javaops.masterjava.service.mail.util.Attachments; import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; import java.io.IOException; @WebServlet("/send") @Slf4j +@MultipartConfig public class SendServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -24,7 +29,10 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String users = req.getParameter("users"); String subject = req.getParameter("subject"); String body = req.getParameter("body"); - GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body, null); + Part filePart = req.getPart("attach"); + GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body, + filePart == null ? null : + ImmutableList.of(Attachments.getAttachment(filePart.getSubmittedFileName(), filePart.getInputStream()))); result = groupResult.toString(); log.info("Processing finished with result: {}", result); } catch (Exception e) { diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index d88ac521a..3b730358d 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -39,6 +39,9 @@


+

+ +

@@ -50,14 +53,26 @@ var users = $("input:checkbox:checked").map(function () { return this.value; }).get(); + + // https://stackoverflow.com/a/5976031/548473 + var fd = new FormData(); + fd.append('users', users); + fd.append('subject', $("#subject").val()); + fd.append('body', $("#body").val()); + var attach = $('#attach')[0].files[0]; + if (attach) fd.append('attach', attach); + // https://stackoverflow.com/a/22213543/548473 - $.post("send", "users=" + users + "&subject=" + $("#subject").val() + "&body=" + $("#body").val()) - .done(function (result) { - $('#result').html(result); - }) - .fail(function (result) { - $('#result').html(result); - }); + $.post({ + url: 'send', + data: fd, + contentType: false, + processData: false + }).done(function (result) { + $('#result').html(result); + }).fail(function (result) { + $('#result').html(result); + }); } From 27a4fd22bd7ecab821bcbccb3dc2599fe97764cc Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:46:05 +0300 Subject: [PATCH 67/87] 9 4 HW8 mail attach --- .../masterjava/service/mail/MailSender.java | 23 +++++++++++++++---- .../service/mail/MailServiceImpl.java | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java index 0c599144c..006c4d421 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailSender.java @@ -4,25 +4,28 @@ import com.google.common.collect.ImmutableSet; import lombok.extern.slf4j.Slf4j; import lombok.val; -import org.apache.commons.mail.EmailException; import ru.javaops.masterjava.ExceptionType; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.service.mail.persist.MailCase; import ru.javaops.masterjava.service.mail.persist.MailCaseDao; import ru.javaops.masterjava.web.WebStateException; +import javax.mail.internet.MimeUtility; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Set; @Slf4j public class MailSender { private static final MailCaseDao MAIL_CASE_DAO = DBIProvider.getDao(MailCaseDao.class); - static MailResult sendTo(Addressee to, String subject, String body) throws WebStateException { - val state = sendToGroup(ImmutableSet.of(to), ImmutableSet.of(), subject, body); + static MailResult sendTo(Addressee to, String subject, String body, List attachments) throws WebStateException { + val state = sendToGroup(ImmutableSet.of(to), ImmutableSet.of(), subject, body, attachments); return new MailResult(to.getEmail(), state); } - static String sendToGroup(Set to, Set cc, String subject, String body) throws WebStateException { + static String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { log.info("Send mail to \'" + to + "\' cc \'" + cc + "\' subject \'" + subject + (log.isDebugEnabled() ? "\nbody=" + body : "")); String state = MailResult.OK; try { @@ -35,12 +38,15 @@ static String sendToGroup(Set to, Set cc, String subject, for (Addressee addressee : cc) { email.addCc(addressee.getEmail(), addressee.getName()); } + for (Attachment attach : attachments) { + email.attach(attach.getDataHandler().getDataSource(), encodeWord(attach.getName()), null); + } // https://yandex.ru/blog/company/66296 email.setHeaders(ImmutableMap.of("List-Unsubscribe", "")); email.send(); - } catch (EmailException e) { + } catch (Exception e) { log.error(e.getMessage(), e); state = e.getMessage(); } @@ -53,4 +59,11 @@ static String sendToGroup(Set to, Set cc, String subject, log.info("Sent with state: " + state); return state; } + + public static String encodeWord(String word) throws UnsupportedEncodingException { + if (word == null) { + return null; + } + return MimeUtility.encodeWord(word, StandardCharsets.UTF_8.name(), null); + } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index b10c1faea..34375a0d2 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -14,11 +14,11 @@ @MTOM public class MailServiceImpl implements MailService { public String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { - return MailSender.sendToGroup(to, cc, subject, body); + return MailSender.sendToGroup(to, cc, subject, body, attachments); } @Override public GroupResult sendBulk(Set to, String subject, String body, List attachments) throws WebStateException { - return MailServiceExecutor.sendBulk(to, subject, body); + return MailServiceExecutor.sendBulk(to, subject, body, attachments); } } From ce8a4c5c409b73d15ca094fec42cdbf340685f61 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:46:31 +0300 Subject: [PATCH 68/87] 9 5 HW8 git revision --- config_templates/app.conf | 2 ++ config_templates/version.html | 8 ++++++-- parent/pom.xml | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/config_templates/app.conf b/config_templates/app.conf index bf966139f..59b9dedce 100644 --- a/config_templates/app.conf +++ b/config_templates/app.conf @@ -3,4 +3,6 @@ app { projectName = ${project.name} version = ${project.version} configDir = "${masterjava.config}" + branch.name = ${scmBranch} + commit.hash = ${buildNumber} } diff --git a/config_templates/version.html b/config_templates/version.html index 3cffc3842..140d4b0eb 100644 --- a/config_templates/version.html +++ b/config_templates/version.html @@ -4,8 +4,12 @@ ${project.name} -${project.groupId}:${project.name}:${project.version}
-configDir=${masterjava.config}
+
+${project.groupId}:${project.artifactId}:${project.version}
+branch.name = ${scmBranch}
+commit.hash = ${buildNumber}
+configDir = ${masterjava.config}
+
Многопоточность. Maven. XML. Веб сервисы. diff --git a/parent/pom.xml b/parent/pom.xml index c3e719ccc..bf7e706eb 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -61,6 +61,23 @@ + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + + validate + + create + + + + + false + false + + @@ -99,4 +116,11 @@ + + scm:git:https://github.com/JavaWebinar/masterjava.git + scm:git:https://github.com/JavaWebinar/masterjava.git + HEAD + https://github.com/vitchurb/masterjava.git + + From 37f7e2b75cb4b18de7f31977b5d4caa91cf85e46 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:47:55 +0300 Subject: [PATCH 69/87] 9 6 msg ctx auth --- .../ru/javaops/masterjava/web/AuthUtil.java | 33 +++++++++++++++++++ .../ru/javaops/masterjava/web/WsClient.java | 6 ++++ .../masterjava/service/mail/MailWSClient.java | 9 ++++- .../service/mail/MailServiceImpl.java | 20 +++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/AuthUtil.java diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/AuthUtil.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/AuthUtil.java new file mode 100644 index 000000000..f8fea01c8 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/AuthUtil.java @@ -0,0 +1,33 @@ +package ru.javaops.masterjava.web; + +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.HttpServletResponse; +import javax.xml.bind.DatatypeConverter; +import java.util.List; +import java.util.Map; + +import static com.google.common.net.HttpHeaders.AUTHORIZATION; + +@Slf4j +public class AuthUtil { + + public static String encodeBasicAuthHeader(String name, String passw) { + String authString = name + ":" + passw; + return "Basic " + DatatypeConverter.printBase64Binary(authString.getBytes()); + } + + public static int checkBasicAuth(Map> headers, String basicAuthCredentials) { + List autHeaders = headers.get(AUTHORIZATION); + if ((autHeaders == null || autHeaders.isEmpty())) { + log.warn("Unauthorized access"); + return HttpServletResponse.SC_UNAUTHORIZED; + } else { + if (!autHeaders.get(0).equals(basicAuthCredentials)) { + log.warn("Wrong password access"); + return HttpServletResponse.SC_FORBIDDEN; + } + return 0; + } + } +} diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index 6ca32585a..ac7617819 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -40,6 +40,12 @@ public T getPort(WebServiceFeature... features) { return port; } + public static void setAuth(T port, String user, String password) { + Map requestContext = ((BindingProvider) port).getRequestContext(); + requestContext.put(BindingProvider.USERNAME_PROPERTY, user); + requestContext.put(BindingProvider.PASSWORD_PROPERTY, password); + } + public static WebStateException getWebStateException(Throwable t, ExceptionType type) { return (t instanceof WebStateException) ? (WebStateException) t : new WebStateException(t, type); } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index e6b332f45..cfd411088 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,6 +5,7 @@ import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; +import ru.javaops.masterjava.web.AuthUtil; import ru.javaops.masterjava.web.WebStateException; import ru.javaops.masterjava.web.WsClient; @@ -16,6 +17,10 @@ @Slf4j public class MailWSClient { private static final WsClient WS_CLIENT; + public static final String USER = "user"; + public static final String PASSWORD = "password"; + + public static String AUTH_HEADER = AuthUtil.encodeBasicAuthHeader(USER, PASSWORD); static { WS_CLIENT = new WsClient<>(Resources.getResource("wsdl/mailService.wsdl"), @@ -41,7 +46,9 @@ public static GroupResult sendBulk(final Set to, final String subject } private static MailService getPort() { - return WS_CLIENT.getPort(new MTOMFeature(1024)); + MailService port = WS_CLIENT.getPort(new MTOMFeature(1024)); + WsClient.setAuth(port, USER, PASSWORD); + return port; } public static Set split(String addressees) { diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 34375a0d2..ef63e6bc9 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -1,10 +1,15 @@ package ru.javaops.masterjava.service.mail; +import ru.javaops.masterjava.web.AuthUtil; import ru.javaops.masterjava.web.WebStateException; +import javax.annotation.Resource; import javax.jws.WebService; +import javax.xml.ws.WebServiceContext; +import javax.xml.ws.handler.MessageContext; import javax.xml.ws.soap.MTOM; import java.util.List; +import java.util.Map; import java.util.Set; @WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" @@ -13,7 +18,22 @@ //@StreamingAttachment(parseEagerly=true, memoryThreshold=40000L) @MTOM public class MailServiceImpl implements MailService { + + @Resource + private WebServiceContext wsContext; + public String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { + MessageContext mCtx = wsContext.getMessageContext(); + Map> headers = (Map>) mCtx.get(MessageContext.HTTP_REQUEST_HEADERS); + +// HttpServletRequest request = (HttpServletRequest) mCtx.get(MessageContext.SERVLET_REQUEST); +// HttpServletResponse response = (HttpServletResponse) mCtx.get(MessageContext.SERVLET_RESPONSE); + + int code = AuthUtil.checkBasicAuth(headers, MailWSClient.AUTH_HEADER); + if (code != 0) { + mCtx.put(MessageContext.HTTP_RESPONSE_CODE, code); + throw new SecurityException(); + } return MailSender.sendToGroup(to, cc, subject, body, attachments); } From 0bba1457d7065b26f5d633b5391f8cea578b7539 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:50:22 +0300 Subject: [PATCH 70/87] 9_7_logging_handlers.patch --- .../ru/javaops/masterjava/web/WsClient.java | 10 ++ .../web/handler/SoapBaseHandler.java | 23 +++ .../web/handler/SoapLoggingHandlers.java | 143 ++++++++++++++++++ services/mail-api/pom.xml | 5 + .../masterjava/service/mail/MailWSClient.java | 4 + .../service/mail/util/Attachments.java | 9 +- .../service/mail/MailServiceImpl.java | 2 + .../src/main/resources/mailWsHandlers.xml | 8 + 8 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java create mode 100644 services/mail-service/src/main/resources/mailWsHandlers.xml diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index ac7617819..e65de940a 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -5,10 +5,13 @@ import ru.javaops.masterjava.config.Configs; import javax.xml.namespace.QName; +import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import javax.xml.ws.WebServiceFeature; +import javax.xml.ws.handler.Handler; import java.net.URL; +import java.util.List; import java.util.Map; public class WsClient { @@ -46,6 +49,13 @@ public static void setAuth(T port, String user, String password) { requestContext.put(BindingProvider.PASSWORD_PROPERTY, password); } + public static void setHandler(T port, Handler handler) { + Binding binding = ((BindingProvider) port).getBinding(); + List handlerList = binding.getHandlerChain(); + handlerList.add(handler); + binding.setHandlerChain(handlerList); + } + public static WebStateException getWebStateException(Throwable t, ExceptionType type) { return (t instanceof WebStateException) ? (WebStateException) t : new WebStateException(t, type); } diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java new file mode 100644 index 000000000..5cd1622b0 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java @@ -0,0 +1,23 @@ +package ru.javaops.masterjava.web.handler; + +import com.sun.xml.ws.api.handler.MessageHandler; +import com.sun.xml.ws.api.handler.MessageHandlerContext; + +import javax.xml.namespace.QName; +import javax.xml.ws.handler.MessageContext; +import java.util.Set; + +public abstract class SoapBaseHandler implements MessageHandler { + + public Set getHeaders() { + return null; + } + + @Override + public void close(MessageContext context) { + } + + protected static boolean isOutbound(MessageHandlerContext context) { + return (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); + } +} diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java new file mode 100644 index 000000000..7af6cc145 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java @@ -0,0 +1,143 @@ +package ru.javaops.masterjava.web.handler; + + +import com.sun.xml.txw2.output.IndentingXMLStreamWriter; +import com.sun.xml.ws.api.handler.MessageHandlerContext; +import com.sun.xml.ws.api.message.Message; +import com.sun.xml.ws.api.streaming.XMLStreamWriterFactory; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.event.Level; + +import javax.xml.stream.XMLStreamWriter; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.Map; + +/** + * Refactored from: + * + * @see {http://weblogs.java.net/blog/ramapulavarthi/archive/2007/12/extend_your_web.html + * http://fisheye5.cenqua.com/browse/jax-ws-sources/jaxws-ri/samples/efficient_handler/src/efficient_handler/common/LoggingHandler.java?r=MAIN} + *

+ * This simple LoggingHandler will log the contents of incoming + * and outgoing messages. This is implemented as a MessageHandler + * for better performance over SOAPHandler. + */ +@Slf4j +public abstract class SoapLoggingHandlers extends SoapBaseHandler { + + private final Level loggingLevel; + + protected SoapLoggingHandlers(Level loggingLevel) { + this.loggingLevel = loggingLevel; + } + + private static final Map HANDLER_MAP = new EnumMap(Level.class) { + { + put(Level.TRACE, HANDLER.DEBUG); + put(Level.DEBUG, HANDLER.DEBUG); + put(Level.INFO, HANDLER.INFO); + put(Level.WARN, HANDLER.ERROR); + put(Level.ERROR, HANDLER.ERROR); + } + }; + + protected enum HANDLER { + NONE { + @Override + public void handleFault(MessageHandlerContext mhc) { + } + + @Override + public void handleMessage(MessageHandlerContext mhc, boolean isRequest) { + } + }, + ERROR { + private static final String REQUEST_MSG = "REQUEST_MSG"; + + public void handleFault(MessageHandlerContext context) { + log.error("Fault SOAP request:\n" + getMessageText(((Message) context.get(REQUEST_MSG)))); + } + + public void handleMessage(MessageHandlerContext context, boolean isRequest) { + if (isRequest) { + context.put(REQUEST_MSG, context.getMessage().copy()); + } + } + }, + INFO { + public void handleFault(MessageHandlerContext context) { + ERROR.handleFault(context); + } + + public void handleMessage(MessageHandlerContext context, boolean isRequest) { + ERROR.handleMessage(context, isRequest); + log.info((isRequest ? "SOAP request: " : "SOAP response: ") + context.getMessage().getPayloadLocalPart()); + } + }, + DEBUG { + public void handleFault(MessageHandlerContext context) { + log.error("Fault SOAP message:\n" + getMessageText(context.getMessage().copy())); + } + + public void handleMessage(MessageHandlerContext context, boolean isRequest) { + log.info((isRequest ? "SOAP request:\n" : "SOAP response:\n") + getMessageText(context.getMessage().copy())); + } + }; + + public abstract void handleMessage(MessageHandlerContext mhc, boolean isRequest); + + public abstract void handleFault(MessageHandlerContext mhc); + + protected static String getMessageText(Message msg) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + XMLStreamWriter writer = XMLStreamWriterFactory.create(out, "UTF-8"); + IndentingXMLStreamWriter wrap = new IndentingXMLStreamWriter(writer); + msg.writeTo(wrap); + return out.toString(StandardCharsets.UTF_8.name()); + } catch (Exception e) { + log.warn("Coudn't get SOAP message for logging", e); + return null; + } + } + } + + abstract protected boolean isRequest(boolean isOutbound); + + @Override + public boolean handleMessage(MessageHandlerContext mhc) { + HANDLER_MAP.get(loggingLevel).handleMessage(mhc, isRequest(isOutbound(mhc))); + return true; + } + + @Override + public boolean handleFault(MessageHandlerContext mhc) { + HANDLER_MAP.get(loggingLevel).handleFault(mhc); + return true; + } + + public static class ClientHandler extends SoapLoggingHandlers { + public ClientHandler(Level loggingLevel) { + super(loggingLevel); + } + + @Override + protected boolean isRequest(boolean isOutbound) { + return isOutbound; + } + } + + public static class ServerHandler extends SoapLoggingHandlers { + + public ServerHandler() { + super(Level.INFO); + } + + @Override + protected boolean isRequest(boolean isOutbound) { + return !isOutbound; + } + } +} diff --git a/services/mail-api/pom.xml b/services/mail-api/pom.xml index d3fa05788..d5292ba46 100644 --- a/services/mail-api/pom.xml +++ b/services/mail-api/pom.xml @@ -33,6 +33,11 @@ common-ws ${project.version} + + commons-io + commons-io + 2.6 + diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index cfd411088..1aa3496a3 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,9 +5,11 @@ import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; +import org.slf4j.event.Level; import ru.javaops.masterjava.web.AuthUtil; import ru.javaops.masterjava.web.WebStateException; import ru.javaops.masterjava.web.WsClient; +import ru.javaops.masterjava.web.handler.SoapLoggingHandlers; import javax.xml.namespace.QName; import javax.xml.ws.soap.MTOMFeature; @@ -19,6 +21,7 @@ public class MailWSClient { private static final WsClient WS_CLIENT; public static final String USER = "user"; public static final String PASSWORD = "password"; + private static final SoapLoggingHandlers.ClientHandler LOGGING_HANDLER = new SoapLoggingHandlers.ClientHandler(Level.DEBUG); public static String AUTH_HEADER = AuthUtil.encodeBasicAuthHeader(USER, PASSWORD); @@ -48,6 +51,7 @@ public static GroupResult sendBulk(final Set to, final String subject private static MailService getPort() { MailService port = WS_CLIENT.getPort(new MTOMFeature(1024)); WsClient.setAuth(port, USER, PASSWORD); + WsClient.setHandler(port, LOGGING_HANDLER); return port; } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java index f8dddeef3..5b0b0d0f5 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java @@ -1,6 +1,7 @@ package ru.javaops.masterjava.service.mail.util; import lombok.AllArgsConstructor; +import org.apache.commons.io.input.CloseShieldInputStream; import ru.javaops.masterjava.service.mail.Attachment; import javax.activation.DataHandler; @@ -14,6 +15,7 @@ public static Attachment getAttachment(String name, InputStream inputStream) { return new Attachment(name, new DataHandler(new InputStreamDataSource(inputStream))); } + // http://stackoverflow.com/questions/2830561/how-to-convert-an-inputstream-to-a-datahandler // http://stackoverflow.com/a/10783565/548473 @AllArgsConstructor private static class InputStreamDataSource implements DataSource { @@ -21,12 +23,7 @@ private static class InputStreamDataSource implements DataSource { @Override public InputStream getInputStream() throws IOException { - if (inputStream == null) { - throw new IOException("Second getInputStream() call is not supported"); - } - InputStream res = inputStream; - inputStream = null; - return res; + return new CloseShieldInputStream(inputStream); } @Override diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index ef63e6bc9..80eb7a87e 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -4,6 +4,7 @@ import ru.javaops.masterjava.web.WebStateException; import javax.annotation.Resource; +import javax.jws.HandlerChain; import javax.jws.WebService; import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; @@ -17,6 +18,7 @@ ) //@StreamingAttachment(parseEagerly=true, memoryThreshold=40000L) @MTOM +@HandlerChain(file = "mailWsHandlers.xml") public class MailServiceImpl implements MailService { @Resource diff --git a/services/mail-service/src/main/resources/mailWsHandlers.xml b/services/mail-service/src/main/resources/mailWsHandlers.xml new file mode 100644 index 000000000..7da87a695 --- /dev/null +++ b/services/mail-service/src/main/resources/mailWsHandlers.xml @@ -0,0 +1,8 @@ + + + + SoapLoggingHandler + ru.javaops.masterjava.web.handler.SoapLoggingHandlers$ServerHandler + + + From 7de6b0d68db22602f72b37429aa4ecf933f6c877 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 21:51:29 +0300 Subject: [PATCH 71/87] 9 8 prepare HW9 --- common/src/main/resources/hosts.conf | 8 +++++++- .../ru/javaops/masterjava/web/Statistics.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/Statistics.java diff --git a/common/src/main/resources/hosts.conf b/common/src/main/resources/hosts.conf index 660c9c067..6b0157bfe 100644 --- a/common/src/main/resources/hosts.conf +++ b/common/src/main/resources/hosts.conf @@ -1,4 +1,10 @@ hosts { - mail = "http://localhost:8080" + mail { + endpoint = "http://localhost:8080" + debug.client = DEBUG + debug.server = INFO + user = "user" + password = "password" + } } include file("/apps/masterjava/config/hosts.conf") diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/Statistics.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/Statistics.java new file mode 100644 index 000000000..32d11f9bb --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/Statistics.java @@ -0,0 +1,18 @@ +package ru.javaops.masterjava.web; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class Statistics { + public enum RESULT { + SUCCESS, FAIL + } + + public static void count(String payload, long startTime, RESULT result) { + long now = System.currentTimeMillis(); + int ms = (int) (now - startTime); + log.info(payload + " " + result.name() + " execution time(ms): " + ms); + // place for statistics staff + + } +} From 921bf9b6544229a2cb6dcaf1e1947b8bfcca281d Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:07:00 +0300 Subject: [PATCH 72/87] 6 5 web services --- .../service/mail/MailServiceExecutor.java | 85 ++++++++----------- 1 file changed, 34 insertions(+), 51 deletions(-) diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java index 1ee57c01a..d59f32628 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java @@ -1,74 +1,57 @@ package ru.javaops.masterjava.service.mail; -import java.util.Collections; +import lombok.extern.slf4j.Slf4j; +import one.util.streamex.StreamEx; +import ru.javaops.masterjava.ExceptionType; +import ru.javaops.masterjava.web.WebStateException; +import ru.javaops.masterjava.web.WsClient; + +import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.*; +@Slf4j public class MailServiceExecutor { - 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"; - public GroupResult sendToList(final String template, final Set emails) throws Exception { - return new GroupResult(0, Collections.emptyList(), null); - } + private static final ExecutorService mailExecutor = Executors.newFixedThreadPool(8); + public static GroupResult sendBulk(final Set addressees, final String subject, final String body, List attachments) throws WebStateException { + final CompletionService completionService = new ExecutorCompletionService<>(mailExecutor); + List> futures = StreamEx.of(addressees) + .map(addressee -> completionService.submit(() -> MailSender.sendTo(addressee, subject, body, attachments))) + .toList(); - // dummy realization - public MailResult sendToUser(String template, String email) throws Exception { - try { - Thread.sleep(500); //delay - } catch (InterruptedException e) { - // log cancel; - return null; + private void cancel(String cause, Throwable t) throws WebStateException { + futures.forEach(f -> f.cancel(true)); + return new GroupResult(success, failed, cause); + if (cause != null) { + throw new WebStateException(cause, ExceptionType.EMAIL); + } else { + throw WsClient.getWebStateException(t, ExceptionType.EMAIL); + } } - return Math.random() < 0.7 ? MailResult.ok(email) : MailResult.error(email, "Error"); - } - public static class MailResult { - private final String email; - private final String result; + return new Callable() { + private int success = 0; + return - private static MailResult ok(String email) { - return new MailResult(email, OK); - } + cancelWithFail(INTERRUPTED_EXCEPTION); - private static MailResult error(String email, String error) { - return new MailResult(email, error); + GroupResult groupResult = new GroupResult(success, failed, null); + log.info("groupResult: {}",groupResult); + return groupResult; } - public boolean isOk() { - return OK.equals(result); - } - private MailResult(String email, String cause) { - this.email = email; - this.result = cause; + private GroupResult cancelWithFail (String cause){ } + }. - @Override - public String toString() { - return '(' + email + ',' + result + ')'; - } + call(); +} } - 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); - } - } -} From e697b3e590ee37332e43c49242f5ce7b6f9cc095 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:14:27 +0300 Subject: [PATCH 73/87] 10 1 HW9 handlers --- .../ru/javaops/masterjava/web/WsClient.java | 2 +- .../web/handler/SoapBaseHandler.java | 1 + .../web/handler/SoapLoggingHandlers.java | 6 +++ .../handler/SoapServerSecurityHandler.java | 43 +++++++++++++++++++ .../web/handler/SoapStatisticHandler.java | 32 ++++++++++++++ .../masterjava/service/mail/MailWSClient.java | 3 -- .../masterjava/service/mail/MailHandlers.java | 11 +++++ .../service/mail/MailServiceImpl.java | 16 +++---- .../src/main/resources/mailWsHandlers.xml | 8 ++++ 9 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapServerSecurityHandler.java create mode 100644 services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapStatisticHandler.java create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index e65de940a..6cd401e26 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -31,7 +31,7 @@ public WsClient(URL wsdlUrl, QName qname, Class serviceClass) { } public void init(String host, String endpointAddress) { - this.endpointAddress = HOSTS.getString(host) + endpointAddress; + this.endpointAddress = HOSTS.getConfig(host).getString("endpoint") + endpointAddress; } // Post is not thread-safe (http://stackoverflow.com/a/10601916/548473) diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java index 5cd1622b0..75eb667a0 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapBaseHandler.java @@ -9,6 +9,7 @@ public abstract class SoapBaseHandler implements MessageHandler { + @Override public Set getHeaders() { return null; } diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java index 7af6cc145..7f53e1337 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java @@ -56,10 +56,12 @@ public void handleMessage(MessageHandlerContext mhc, boolean isRequest) { ERROR { private static final String REQUEST_MSG = "REQUEST_MSG"; + @Override public void handleFault(MessageHandlerContext context) { log.error("Fault SOAP request:\n" + getMessageText(((Message) context.get(REQUEST_MSG)))); } + @Override public void handleMessage(MessageHandlerContext context, boolean isRequest) { if (isRequest) { context.put(REQUEST_MSG, context.getMessage().copy()); @@ -67,20 +69,24 @@ public void handleMessage(MessageHandlerContext context, boolean isRequest) { } }, INFO { + @Override public void handleFault(MessageHandlerContext context) { ERROR.handleFault(context); } + @Override public void handleMessage(MessageHandlerContext context, boolean isRequest) { ERROR.handleMessage(context, isRequest); log.info((isRequest ? "SOAP request: " : "SOAP response: ") + context.getMessage().getPayloadLocalPart()); } }, DEBUG { + @Override public void handleFault(MessageHandlerContext context) { log.error("Fault SOAP message:\n" + getMessageText(context.getMessage().copy())); } + @Override public void handleMessage(MessageHandlerContext context, boolean isRequest) { log.info((isRequest ? "SOAP request:\n" : "SOAP response:\n") + getMessageText(context.getMessage().copy())); } diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapServerSecurityHandler.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapServerSecurityHandler.java new file mode 100644 index 000000000..2fae09072 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapServerSecurityHandler.java @@ -0,0 +1,43 @@ +package ru.javaops.masterjava.web.handler; + +import com.sun.xml.ws.api.handler.MessageHandlerContext; +import lombok.extern.slf4j.Slf4j; +import ru.javaops.masterjava.web.AuthUtil; + +import javax.xml.ws.handler.MessageContext; +import java.util.List; +import java.util.Map; + +import static ru.javaops.masterjava.web.AuthUtil.encodeBasicAuthHeader; + +@Slf4j +abstract public class SoapServerSecurityHandler extends SoapBaseHandler { + + private String authHeader; + + public SoapServerSecurityHandler(String user, String password) { + this(encodeBasicAuthHeader(user, password)); + } + + public SoapServerSecurityHandler(String authHeader) { + this.authHeader = authHeader; + } + + @Override + public boolean handleMessage(MessageHandlerContext ctx) { + if (!isOutbound(ctx) && authHeader != null) { + Map> headers = (Map>) ctx.get(MessageContext.HTTP_REQUEST_HEADERS); + int code = AuthUtil.checkBasicAuth(headers, authHeader); + if (code != 0) { + ctx.put(MessageContext.HTTP_RESPONSE_CODE, code); + throw new SecurityException(); + } + } + return true; + } + + @Override + public boolean handleFault(MessageHandlerContext context) { + return true; + } +} diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapStatisticHandler.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapStatisticHandler.java new file mode 100644 index 000000000..436cafd06 --- /dev/null +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapStatisticHandler.java @@ -0,0 +1,32 @@ +package ru.javaops.masterjava.web.handler; + +import com.sun.xml.ws.api.handler.MessageHandlerContext; +import ru.javaops.masterjava.web.Statistics; + +public class SoapStatisticHandler extends SoapBaseHandler { + + private static final String PAYLOAD = "PAYLOAD"; + private static final String START_TIME = "START_TIME"; + + @Override + public boolean handleMessage(MessageHandlerContext context) { + if (isOutbound(context)) { + count(context, Statistics.RESULT.SUCCESS); + } else { + String payload = context.getMessage().getPayloadLocalPart(); + context.put(PAYLOAD, payload); + context.put(START_TIME, System.currentTimeMillis()); + } + return true; + } + + @Override + public boolean handleFault(MessageHandlerContext context) { + count(context, Statistics.RESULT.FAIL); + return true; + } + + private void count(MessageHandlerContext context, Statistics.RESULT result) { + Statistics.count((String) context.get(PAYLOAD), (Long) context.get(START_TIME), result); + } +} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index 1aa3496a3..d19633556 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -6,7 +6,6 @@ import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; import org.slf4j.event.Level; -import ru.javaops.masterjava.web.AuthUtil; import ru.javaops.masterjava.web.WebStateException; import ru.javaops.masterjava.web.WsClient; import ru.javaops.masterjava.web.handler.SoapLoggingHandlers; @@ -23,8 +22,6 @@ public class MailWSClient { public static final String PASSWORD = "password"; private static final SoapLoggingHandlers.ClientHandler LOGGING_HANDLER = new SoapLoggingHandlers.ClientHandler(Level.DEBUG); - public static String AUTH_HEADER = AuthUtil.encodeBasicAuthHeader(USER, PASSWORD); - static { WS_CLIENT = new WsClient<>(Resources.getResource("wsdl/mailService.wsdl"), new QName("http://mail.javaops.ru/", "MailServiceImplService"), diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java new file mode 100644 index 000000000..9f6591ec2 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java @@ -0,0 +1,11 @@ +package ru.javaops.masterjava.service.mail; + +import ru.javaops.masterjava.web.handler.SoapServerSecurityHandler; + +public class MailHandlers { + public static class SecurityHandler extends SoapServerSecurityHandler { + public SecurityHandler() { + super(MailWSClient.USER, MailWSClient.PASSWORD); + } + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java index 80eb7a87e..1d17ae6c3 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceImpl.java @@ -1,16 +1,11 @@ package ru.javaops.masterjava.service.mail; -import ru.javaops.masterjava.web.AuthUtil; import ru.javaops.masterjava.web.WebStateException; -import javax.annotation.Resource; import javax.jws.HandlerChain; import javax.jws.WebService; -import javax.xml.ws.WebServiceContext; -import javax.xml.ws.handler.MessageContext; import javax.xml.ws.soap.MTOM; import java.util.List; -import java.util.Map; import java.util.Set; @WebService(endpointInterface = "ru.javaops.masterjava.service.mail.MailService", targetNamespace = "http://mail.javaops.ru/" @@ -21,21 +16,24 @@ @HandlerChain(file = "mailWsHandlers.xml") public class MailServiceImpl implements MailService { - @Resource - private WebServiceContext wsContext; +// @Resource +// private WebServiceContext wsContext; + @Override public String sendToGroup(Set to, Set cc, String subject, String body, List attachments) throws WebStateException { +/* MessageContext mCtx = wsContext.getMessageContext(); Map> headers = (Map>) mCtx.get(MessageContext.HTTP_REQUEST_HEADERS); -// HttpServletRequest request = (HttpServletRequest) mCtx.get(MessageContext.SERVLET_REQUEST); -// HttpServletResponse response = (HttpServletResponse) mCtx.get(MessageContext.SERVLET_RESPONSE); + HttpServletRequest request = (HttpServletRequest) mCtx.get(MessageContext.SERVLET_REQUEST); + HttpServletResponse response = (HttpServletResponse) mCtx.get(MessageContext.SERVLET_RESPONSE); int code = AuthUtil.checkBasicAuth(headers, MailWSClient.AUTH_HEADER); if (code != 0) { mCtx.put(MessageContext.HTTP_RESPONSE_CODE, code); throw new SecurityException(); } +*/ return MailSender.sendToGroup(to, cc, subject, body, attachments); } diff --git a/services/mail-service/src/main/resources/mailWsHandlers.xml b/services/mail-service/src/main/resources/mailWsHandlers.xml index 7da87a695..8ecf4e5db 100644 --- a/services/mail-service/src/main/resources/mailWsHandlers.xml +++ b/services/mail-service/src/main/resources/mailWsHandlers.xml @@ -4,5 +4,13 @@ SoapLoggingHandler ru.javaops.masterjava.web.handler.SoapLoggingHandlers$ServerHandler + + SoapStatisticHandler + ru.javaops.masterjava.web.handler.SoapStatisticHandler + + + SoapStatisticHandler + ru.javaops.masterjava.service.mail.MailHandlers$SecurityHandler + From 1f95c6626d5ecafb265d67aba841812c0c9334e8 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:15:53 +0300 Subject: [PATCH 74/87] 10 2 HW9 host config --- common/src/main/resources/defaults.conf | 4 ++ common/src/main/resources/hosts.conf | 4 -- .../ru/javaops/masterjava/web/WsClient.java | 52 +++++++++++++++++-- .../web/handler/SoapLoggingHandlers.java | 4 +- .../masterjava/service/mail/MailWSClient.java | 14 ++--- .../masterjava/service/mail/MailHandlers.java | 9 +++- .../src/main/resources/mailWsHandlers.xml | 6 +-- 7 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 common/src/main/resources/defaults.conf diff --git a/common/src/main/resources/defaults.conf b/common/src/main/resources/defaults.conf new file mode 100644 index 000000000..62fca5ecb --- /dev/null +++ b/common/src/main/resources/defaults.conf @@ -0,0 +1,4 @@ +client.debugLevel = INFO +server.debugLevel = INFO +user = null +password = null diff --git a/common/src/main/resources/hosts.conf b/common/src/main/resources/hosts.conf index 6b0157bfe..3196f4620 100644 --- a/common/src/main/resources/hosts.conf +++ b/common/src/main/resources/hosts.conf @@ -1,10 +1,6 @@ hosts { mail { endpoint = "http://localhost:8080" - debug.client = DEBUG - debug.server = INFO - user = "user" - password = "password" } } include file("/apps/masterjava/config/hosts.conf") diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java index 6cd401e26..2631d0067 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/WsClient.java @@ -1,8 +1,10 @@ package ru.javaops.masterjava.web; import com.typesafe.config.Config; +import org.slf4j.event.Level; import ru.javaops.masterjava.ExceptionType; import ru.javaops.masterjava.config.Configs; +import ru.javaops.masterjava.web.handler.SoapLoggingHandlers; import javax.xml.namespace.QName; import javax.xml.ws.Binding; @@ -19,7 +21,40 @@ public class WsClient { private final Class serviceClass; private final Service service; - private String endpointAddress; + private HostConfig hostConfig; + + public static class HostConfig { + public final String endpoint; + public final Level serverDebugLevel; + public final String user; + public final String password; + public final String authHeader; + public final SoapLoggingHandlers.ClientHandler clientLoggingHandler; + + public HostConfig(Config config, String endpointAddress) { + endpoint = config.getString("endpoint") + endpointAddress; + serverDebugLevel = config.getEnum(Level.class, "server.debugLevel"); + +// https://github.com/typesafehub/config/issues/282 + if (!config.getIsNull("user") && !config.getIsNull("password")) { + user = config.getString("user"); + password = config.getString("password"); + authHeader = AuthUtil.encodeBasicAuthHeader(user, password); + } else { + user = password = authHeader = null; + } + clientLoggingHandler = config.getIsNull("client.debugLevel") ? null : + new SoapLoggingHandlers.ClientHandler(config.getEnum(Level.class, "client.debugLevel")); + } + + public boolean hasAuthorization() { + return authHeader != null; + } + + public boolean hasHandler() { + return clientLoggingHandler != null; + } + } static { HOSTS = Configs.getConfig("hosts.conf", "hosts"); @@ -31,7 +66,12 @@ public WsClient(URL wsdlUrl, QName qname, Class serviceClass) { } public void init(String host, String endpointAddress) { - this.endpointAddress = HOSTS.getConfig(host).getString("endpoint") + endpointAddress; + this.hostConfig = new HostConfig( + HOSTS.getConfig(host).withFallback(Configs.getConfig("defaults.conf")), endpointAddress); + } + + public HostConfig getHostConfig() { + return hostConfig; } // Post is not thread-safe (http://stackoverflow.com/a/10601916/548473) @@ -39,7 +79,13 @@ public T getPort(WebServiceFeature... features) { T port = service.getPort(serviceClass, features); BindingProvider bp = (BindingProvider) port; Map requestContext = bp.getRequestContext(); - requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); + requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, hostConfig.endpoint); + if (hostConfig.hasAuthorization()) { + setAuth(port, hostConfig.user, hostConfig.password); + } + if (hostConfig.hasHandler()) { + setHandler(port, hostConfig.clientLoggingHandler); + } return port; } diff --git a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java index 7f53e1337..2ed298f1b 100644 --- a/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java +++ b/services/common-ws/src/main/java/ru/javaops/masterjava/web/handler/SoapLoggingHandlers.java @@ -137,8 +137,8 @@ protected boolean isRequest(boolean isOutbound) { public static class ServerHandler extends SoapLoggingHandlers { - public ServerHandler() { - super(Level.INFO); + public ServerHandler(Level loggingLevel) { + super(loggingLevel); } @Override diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index d19633556..08ccca1ac 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -5,10 +5,8 @@ import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; -import org.slf4j.event.Level; import ru.javaops.masterjava.web.WebStateException; import ru.javaops.masterjava.web.WsClient; -import ru.javaops.masterjava.web.handler.SoapLoggingHandlers; import javax.xml.namespace.QName; import javax.xml.ws.soap.MTOMFeature; @@ -18,9 +16,6 @@ @Slf4j public class MailWSClient { private static final WsClient WS_CLIENT; - public static final String USER = "user"; - public static final String PASSWORD = "password"; - private static final SoapLoggingHandlers.ClientHandler LOGGING_HANDLER = new SoapLoggingHandlers.ClientHandler(Level.DEBUG); static { WS_CLIENT = new WsClient<>(Resources.getResource("wsdl/mailService.wsdl"), @@ -46,14 +41,15 @@ public static GroupResult sendBulk(final Set to, final String subject } private static MailService getPort() { - MailService port = WS_CLIENT.getPort(new MTOMFeature(1024)); - WsClient.setAuth(port, USER, PASSWORD); - WsClient.setHandler(port, LOGGING_HANDLER); - return port; + return WS_CLIENT.getPort(new MTOMFeature(1024)); } public static Set split(String addressees) { Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split(addressees); return ImmutableSet.copyOf(Iterables.transform(split, Addressee::new)); } + + public static WsClient.HostConfig getHostConfig() { + return WS_CLIENT.getHostConfig(); + } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java index 9f6591ec2..6dc9987dd 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailHandlers.java @@ -1,11 +1,18 @@ package ru.javaops.masterjava.service.mail; +import ru.javaops.masterjava.web.handler.SoapLoggingHandlers; import ru.javaops.masterjava.web.handler.SoapServerSecurityHandler; public class MailHandlers { public static class SecurityHandler extends SoapServerSecurityHandler { public SecurityHandler() { - super(MailWSClient.USER, MailWSClient.PASSWORD); + super(MailWSClient.getHostConfig().authHeader); + } + } + + public static class LoggingHandler extends SoapLoggingHandlers.ServerHandler { + public LoggingHandler() { + super(MailWSClient.getHostConfig().serverDebugLevel); } } } diff --git a/services/mail-service/src/main/resources/mailWsHandlers.xml b/services/mail-service/src/main/resources/mailWsHandlers.xml index 8ecf4e5db..5508af1f8 100644 --- a/services/mail-service/src/main/resources/mailWsHandlers.xml +++ b/services/mail-service/src/main/resources/mailWsHandlers.xml @@ -1,15 +1,15 @@ - SoapLoggingHandler - ru.javaops.masterjava.web.handler.SoapLoggingHandlers$ServerHandler + MailLoggingHandler + ru.javaops.masterjava.service.mail.MailHandlers$LoggingHandler SoapStatisticHandler ru.javaops.masterjava.web.handler.SoapStatisticHandler - SoapStatisticHandler + MailSecurityHandler ru.javaops.masterjava.service.mail.MailHandlers$SecurityHandler From 5af08d8c524d08beb43e717ca21962ba47377e1d Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:23:53 +0300 Subject: [PATCH 75/87] 10 3 JAX RS --- services/mail-service/pom.xml | 33 +++++++++++ .../masterjava/service/mail/rest/MailRS.java | 32 +++++++++++ .../service/mail/rest/MailRestConfig.java | 12 ++++ web/common-web/pom.xml | 11 +++- .../main/webapp/WEB-INF/templates/users.html | 56 ++++++++++++++----- 5 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 334f404d6..72f62c11f 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -16,6 +16,10 @@ war Mail Service + + 2.26 + + mail @@ -66,5 +70,34 @@ test-jar test + + + org.glassfish.jersey.containers + jersey-container-servlet + ${jersey.version} + + + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + + + org.javassist + javassist + + + + + org.glassfish.jersey.media + jersey-media-moxy + ${jersey.version} + + + org.glassfish.jersey.ext + jersey-bean-validation + ${jersey.version} + diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java new file mode 100644 index 000000000..986ff247b --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java @@ -0,0 +1,32 @@ +package ru.javaops.masterjava.service.mail.rest; + + +import org.hibernate.validator.constraints.NotBlank; +import ru.javaops.masterjava.service.mail.GroupResult; +import ru.javaops.masterjava.service.mail.MailServiceExecutor; +import ru.javaops.masterjava.service.mail.MailWSClient; +import ru.javaops.masterjava.web.WebStateException; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import java.util.Collections; + +@Path("/") +public class MailRS { + @GET + @Path("test") + @Produces(MediaType.TEXT_PLAIN) + public String test() { + return "Test"; + } + + @POST + @Path("send") + @Produces(MediaType.APPLICATION_JSON) + public GroupResult send(@NotBlank @FormParam("users") String users, + @FormParam("subject") String subject, + @NotBlank @FormParam("body") String body) throws WebStateException { + + return MailServiceExecutor.sendBulk(MailWSClient.split(users), subject, body, Collections.emptyList()); + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java new file mode 100644 index 000000000..df6bac189 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java @@ -0,0 +1,12 @@ +package ru.javaops.masterjava.service.mail.rest; + +import org.glassfish.jersey.server.ResourceConfig; + +import javax.ws.rs.ApplicationPath; + +@ApplicationPath("rest") +public class MailRestConfig extends ResourceConfig { + public MailRestConfig() { + packages("ru.javaops.masterjava.service.mail.rest"); + } +} diff --git a/web/common-web/pom.xml b/web/common-web/pom.xml index fd0f5eb49..9174a31fc 100644 --- a/web/common-web/pom.xml +++ b/web/common-web/pom.xml @@ -31,13 +31,22 @@ org.thymeleaf thymeleaf - 3.0.8.RELEASE + 3.0.9.RELEASE org.slf4j slf4j-api + + org.javassist + javassist + + + org.javassist + javassist + 3.22.0-GA + diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index 3b730358d..327fa68ad 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -42,36 +42,64 @@

+

+ SOAP
+ REST
+


 
 

From 0b7f31af9d8fa922252f1c49c11d8c3f1b251e1a Mon Sep 17 00:00:00 2001
From: iStemmer 
Date: Sat, 27 Jul 2019 22:29:25 +0300
Subject: [PATCH 76/87] 10 4 jersey logging

---
 parent-web/pom.xml                            |  4 ++
 parent/pom.xml                                | 15 ++++++
 .../service/mail/rest/MailRestConfig.java     |  5 ++
 .../src/main/resources/logback.xml            | 46 +++++++++++++++++++
 4 files changed, 70 insertions(+)
 create mode 100644 services/mail-service/src/main/resources/logback.xml

diff --git a/parent-web/pom.xml b/parent-web/pom.xml
index 6a9a23b91..ff866f3e1 100644
--- a/parent-web/pom.xml
+++ b/parent-web/pom.xml
@@ -16,6 +16,10 @@
     1.0-SNAPSHOT
     Parent Web
 
+    
+        true
+    
+
     
         
             
diff --git a/parent/pom.xml b/parent/pom.xml
index bf7e706eb..637816212 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -101,6 +101,21 @@
             provided
         
+ + org.slf4j + jul-to-slf4j + ${slf4j.version} + + + + junit diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java index df6bac189..bb552ff64 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java @@ -1,12 +1,17 @@ package ru.javaops.masterjava.service.mail.rest; import org.glassfish.jersey.server.ResourceConfig; +import org.slf4j.bridge.SLF4JBridgeHandler; import javax.ws.rs.ApplicationPath; @ApplicationPath("rest") public class MailRestConfig extends ResourceConfig { + public MailRestConfig() { + // Set Jersey log to SLF4J instead of JUL + // http://stackoverflow.com/questions/4121722 + SLF4JBridgeHandler.install(); packages("ru.javaops.masterjava.service.mail.rest"); } } diff --git a/services/mail-service/src/main/resources/logback.xml b/services/mail-service/src/main/resources/logback.xml new file mode 100644 index 000000000..5011ab18f --- /dev/null +++ b/services/mail-service/src/main/resources/logback.xml @@ -0,0 +1,46 @@ + + + + + + true + + + + + + + + + ${LOG_DIR}/mail.log + + UTF-8 + %d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%file:%line] - %msg%n + + + + ${LOG_DIR}/archived/mail.%d{yyyy-MM-dd}.%i.log + + + 5MB + + + + + + + UTF-8 + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} [%file:%line] - %msg%n + + + + + + + + + + + + From 31bf9c0db7814221d125e8ec1b36037e2a8c270b Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:52:35 +0300 Subject: [PATCH 77/87] 10 5 JMS --- config_templates/context.xml | 14 ++++ parent-web/pom.xml | 1 + services/mail-service/pom.xml | 7 ++ .../mail/listeners/JmsMailListener.java | 63 ++++++++++++++++ web/webapp/pom.xml | 7 ++ .../masterjava/webapp/JmsSendServlet.java | 73 +++++++++++++++++++ ...{SendServlet.java => SoapSendServlet.java} | 4 +- .../main/webapp/WEB-INF/templates/users.html | 7 +- 8 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java create mode 100644 web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java rename web/webapp/src/main/java/ru/javaops/masterjava/webapp/{SendServlet.java => SoapSendServlet.java} (95%) diff --git a/config_templates/context.xml b/config_templates/context.xml index 40bb8aa0c..c8d390dd0 100644 --- a/config_templates/context.xml +++ b/config_templates/context.xml @@ -46,4 +46,18 @@ password="password" url="jdbc:postgresql://localhost:5432/masterjava"/> + + + + diff --git a/parent-web/pom.xml b/parent-web/pom.xml index ff866f3e1..271c37ceb 100644 --- a/parent-web/pom.xml +++ b/parent-web/pom.xml @@ -18,6 +18,7 @@ true + 5.15.2 diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 72f62c11f..4ca95a500 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -99,5 +99,12 @@ jersey-bean-validation ${jersey.version} + + + org.apache.activemq + activemq-all + ${activemq.version} + provided + diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java new file mode 100644 index 000000000..dd8f327f8 --- /dev/null +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java @@ -0,0 +1,63 @@ +package ru.javaops.masterjava.service.mail.listeners; + +import lombok.extern.slf4j.Slf4j; + +import javax.jms.*; +import javax.naming.InitialContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +@Slf4j +public class JmsMailListener implements ServletContextListener { + private Thread listenerThread = null; + private QueueConnection connection; + + @Override + public void contextInitialized(ServletContextEvent sce) { + try { + InitialContext initCtx = new InitialContext(); + QueueConnectionFactory connectionFactory = + (QueueConnectionFactory) initCtx.lookup("java:comp/env/jms/ConnectionFactory"); + connection = connectionFactory.createQueueConnection(); + QueueSession queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = (Queue) initCtx.lookup("java:comp/env/jms/queue/MailQueue"); + QueueReceiver receiver = queueSession.createReceiver(queue); + connection.start(); + log.info("Listen JMS messages ..."); + listenerThread = new Thread(() -> { + try { + while (!Thread.interrupted()) { + Message m = receiver.receive(); + // TODO implement mail sending + if (m instanceof TextMessage) { + TextMessage tm = (TextMessage) m; + String text = tm.getText(); + log.info("Received TextMessage with text '{}'", text); + } + } + } catch (Exception e) { + log.error("Receiving messages failed: " + e.getMessage(), e); + } + }); + listenerThread.start(); + } catch (Exception e) { + log.error("JMS failed: " + e.getMessage(), e); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + if (connection != null) { + try { + connection.close(); + } catch (JMSException ex) { + log.warn("Couldn't close JMSConnection: ", ex); + } + } + if (listenerThread != null) { + listenerThread.interrupt(); + } + } +} diff --git a/web/webapp/pom.xml b/web/webapp/pom.xml index 2784c00ae..c9c8d9c1d 100644 --- a/web/webapp/pom.xml +++ b/web/webapp/pom.xml @@ -31,5 +31,12 @@ mail-api ${project.version} + + + org.apache.activemq + activemq-client + provided + ${activemq.version} + diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java new file mode 100644 index 000000000..2107d39a7 --- /dev/null +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java @@ -0,0 +1,73 @@ +package ru.javaops.masterjava.webapp; + +import lombok.extern.slf4j.Slf4j; + +import javax.jms.*; +import javax.naming.InitialContext; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.IllegalStateException; + +@WebServlet("/sendJms") +@Slf4j +public class JmsSendServlet extends HttpServlet { + private Connection connection; + private Session session; + private MessageProducer producer; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + try { + InitialContext initCtx = new InitialContext(); + ConnectionFactory connectionFactory = (ConnectionFactory) initCtx.lookup("java:comp/env/jms/ConnectionFactory"); + connection = connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = session.createProducer((Destination) initCtx.lookup("java:comp/env/jms/queue/MailQueue")); + } catch (Exception e) { + throw new IllegalStateException("JMS init failed", e); + } + } + + @Override + public void destroy() { + if (connection != null) { + try { + connection.close(); + } catch (JMSException ex) { + log.warn("Couldn't close JMSConnection: ", ex); + } + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String result; + try { + log.info("Start sending"); + req.setCharacterEncoding("UTF-8"); + resp.setCharacterEncoding("UTF-8"); + String users = req.getParameter("users"); + String subject = req.getParameter("subject"); + String body = req.getParameter("body"); + result = sendJms(users, subject, body); + log.info("Processing finished with result: {}", result); + } catch (Exception e) { + log.error("Processing failed", e); + result = e.toString(); + } + resp.getWriter().write(result); + } + + private synchronized String sendJms(String users, String subject, String body) throws JMSException { + TextMessage testMessage = session.createTextMessage(); + testMessage.setText(subject); + producer.send(testMessage); + return "Successfully sent JMS message"; + } +} diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java similarity index 95% rename from web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java rename to web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java index b6ac85969..b751d2c5c 100644 --- a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SendServlet.java +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java @@ -15,10 +15,10 @@ import javax.servlet.http.Part; import java.io.IOException; -@WebServlet("/send") +@WebServlet("/sendSoap") @Slf4j @MultipartConfig -public class SendServlet extends HttpServlet { +public class SoapSendServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String result; diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index 327fa68ad..acc8aa56e 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -45,6 +45,7 @@

SOAP
REST
+ JMS

@@ -79,14 +80,14 @@ var attach = $('#attach')[0].files[0]; if (attach) data.append('attach', attach); param = { - url: "send", + url: "sendSoap", data: data, contentType: false, processData: false }; - } else if(transport === "REST"){ + } else if (transport === "REST" || transport === "JMS") { param = { - url: "/mail/rest/send", + url: (transport === "REST" ? "/mail/rest/send" : "sendJms"), data: "users=" + users + "&subject=" + $("#subject").val() + "&body=" + $("#body").val(), contentType: "application/x-www-form-urlencoded" }; From 6907154400e62a31c5bdcc968cfce3125354347a Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:53:42 +0300 Subject: [PATCH 78/87] 10 6 tomcat auth --- .../src/main/resources/mailWsHandlers.xml | 4 --- .../src/main/webapp/WEB-INF/web.xml | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 services/mail-service/src/main/webapp/WEB-INF/web.xml diff --git a/services/mail-service/src/main/resources/mailWsHandlers.xml b/services/mail-service/src/main/resources/mailWsHandlers.xml index 5508af1f8..a622d158d 100644 --- a/services/mail-service/src/main/resources/mailWsHandlers.xml +++ b/services/mail-service/src/main/resources/mailWsHandlers.xml @@ -8,9 +8,5 @@ SoapStatisticHandler ru.javaops.masterjava.web.handler.SoapStatisticHandler - - MailSecurityHandler - ru.javaops.masterjava.service.mail.MailHandlers$SecurityHandler - diff --git a/services/mail-service/src/main/webapp/WEB-INF/web.xml b/services/mail-service/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..5a4bd7190 --- /dev/null +++ b/services/mail-service/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + mailService + /mailService + + + tomcat + + + + + BASIC + Tomcat basic auth + + + + tomcat + + From 6029b2c46e291ba444f2ba7370cd30c248ee8d8e Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:57:33 +0300 Subject: [PATCH 79/87] 11 1 HW10 jersey attach --- .../service/mail/util/Attachments.java | 11 +++--- services/mail-service/pom.xml | 11 ++++++ .../masterjava/service/mail/rest/MailRS.java | 35 ++++++++++++++++--- .../service/mail/rest/MailRestConfig.java | 2 ++ .../main/webapp/WEB-INF/templates/users.html | 8 ++--- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java index 5b0b0d0f5..eac1edbfe 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java @@ -18,26 +18,29 @@ public static Attachment getAttachment(String name, InputStream inputStream) { // http://stackoverflow.com/questions/2830561/how-to-convert-an-inputstream-to-a-datahandler // http://stackoverflow.com/a/10783565/548473 @AllArgsConstructor - private static class InputStreamDataSource implements DataSource { + private static class InputStreamDataSource implements ProxyDataSource { private InputStream inputStream; @Override public InputStream getInputStream() throws IOException { return new CloseShieldInputStream(inputStream); } + } + + public interface ProxyDataSource extends DataSource { @Override - public OutputStream getOutputStream() throws IOException { + default OutputStream getOutputStream() throws IOException { throw new UnsupportedOperationException("Not implemented"); } @Override - public String getContentType() { + default String getContentType() { return "application/octet-stream"; } @Override - public String getName() { + default String getName() { return ""; } } diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml index 4ca95a500..7367feb96 100644 --- a/services/mail-service/pom.xml +++ b/services/mail-service/pom.xml @@ -99,6 +99,17 @@ jersey-bean-validation ${jersey.version} + + org.glassfish.jersey.media + jersey-media-multipart + ${jersey.version} + + + org.jvnet.mimepull + mimepull + + + org.apache.activemq diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java index 986ff247b..05fd322f7 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java @@ -1,15 +1,23 @@ package ru.javaops.masterjava.service.mail.rest; +import com.google.common.collect.ImmutableList; +import org.glassfish.jersey.media.multipart.BodyPartEntity; +import org.glassfish.jersey.media.multipart.FormDataBodyPart; +import org.glassfish.jersey.media.multipart.FormDataParam; import org.hibernate.validator.constraints.NotBlank; +import ru.javaops.masterjava.service.mail.Attachment; import ru.javaops.masterjava.service.mail.GroupResult; import ru.javaops.masterjava.service.mail.MailServiceExecutor; import ru.javaops.masterjava.service.mail.MailWSClient; +import ru.javaops.masterjava.service.mail.util.Attachments; import ru.javaops.masterjava.web.WebStateException; +import javax.activation.DataHandler; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; -import java.util.Collections; +import java.io.UnsupportedEncodingException; +import java.util.List; @Path("/") public class MailRS { @@ -23,10 +31,27 @@ public String test() { @POST @Path("send") @Produces(MediaType.APPLICATION_JSON) - public GroupResult send(@NotBlank @FormParam("users") String users, - @FormParam("subject") String subject, - @NotBlank @FormParam("body") String body) throws WebStateException { + @Consumes(MediaType.MULTIPART_FORM_DATA) + public GroupResult send(@NotBlank @FormDataParam("users") String users, + @FormDataParam("subject") String subject, + @NotBlank @FormDataParam("body") String body, + @FormDataParam("attach") FormDataBodyPart attachBodyPart) throws WebStateException { - return MailServiceExecutor.sendBulk(MailWSClient.split(users), subject, body, Collections.emptyList()); + final List attachments; + if (attachBodyPart == null) { + attachments = ImmutableList.of(); + } else { + try { + String attachName = attachBodyPart.getContentDisposition().getFileName(); +// UTF-8 encoding workaround: https://java.net/jira/browse/JERSEY-3032 + String utf8name = new String(attachName.getBytes("ISO8859_1"), "UTF-8"); + BodyPartEntity bodyPartEntity = ((BodyPartEntity) attachBodyPart.getEntity()); + + attachments = ImmutableList.of(new Attachment(utf8name, new DataHandler((Attachments.ProxyDataSource) bodyPartEntity::getInputStream))); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + return MailServiceExecutor.sendBulk(MailWSClient.split(users), subject, body, attachments); } } diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java index bb552ff64..c5990a45f 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRestConfig.java @@ -1,5 +1,6 @@ package ru.javaops.masterjava.service.mail.rest; +import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; import org.slf4j.bridge.SLF4JBridgeHandler; @@ -13,5 +14,6 @@ public MailRestConfig() { // http://stackoverflow.com/questions/4121722 SLF4JBridgeHandler.install(); packages("ru.javaops.masterjava.service.mail.rest"); + register(MultiPartFeature.class); } } diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index acc8aa56e..477fc54f6 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -71,7 +71,7 @@ } var param; - if (transport === "SOAP") { + if (transport === "SOAP" || transport === "REST") { // https://stackoverflow.com/a/5976031/548473 var data = new FormData(); data.append('users', users); @@ -80,14 +80,14 @@ var attach = $('#attach')[0].files[0]; if (attach) data.append('attach', attach); param = { - url: "sendSoap", + url: (transport === "REST" ? "/mail/rest/send" : "sendSoap"), data: data, contentType: false, processData: false }; - } else if (transport === "REST" || transport === "JMS") { + } else if (transport === "JMS") { param = { - url: (transport === "REST" ? "/mail/rest/send" : "sendJms"), + url: "sendJms", data: "users=" + users + "&subject=" + $("#subject").val() + "&body=" + $("#body").val(), contentType: "application/x-www-form-urlencoded" }; From 1a8c61613417f570b73531cbae6058f10db0e860 Mon Sep 17 00:00:00 2001 From: iStemmer Date: Sat, 27 Jul 2019 22:59:40 +0300 Subject: [PATCH 80/87] 11 2 HW10 JMS attach --- .../masterjava/service/mail/MailWSClient.java | 8 --- .../service/mail/util/Attachments.java | 47 ------------- .../service/mail/util/MailUtils.java | 70 +++++++++++++++++++ .../service/mail/MailServiceExecutor.java | 17 +++++ .../mail/listeners/JmsMailListener.java | 18 +++-- .../masterjava/service/mail/rest/MailRS.java | 7 +- .../masterjava/webapp/JmsSendServlet.java | 29 +++++--- .../masterjava/webapp/SoapSendServlet.java | 9 ++- .../main/webapp/WEB-INF/templates/users.html | 48 +++++-------- 9 files changed, 144 insertions(+), 109 deletions(-) delete mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java create mode 100644 services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/MailUtils.java diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java index 08ccca1ac..57db8b108 100644 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/MailWSClient.java @@ -1,8 +1,5 @@ package ru.javaops.masterjava.service.mail; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; import ru.javaops.masterjava.web.WebStateException; @@ -44,11 +41,6 @@ private static MailService getPort() { return WS_CLIENT.getPort(new MTOMFeature(1024)); } - public static Set split(String addressees) { - Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split(addressees); - return ImmutableSet.copyOf(Iterables.transform(split, Addressee::new)); - } - public static WsClient.HostConfig getHostConfig() { return WS_CLIENT.getHostConfig(); } diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java deleted file mode 100644 index eac1edbfe..000000000 --- a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/Attachments.java +++ /dev/null @@ -1,47 +0,0 @@ -package ru.javaops.masterjava.service.mail.util; - -import lombok.AllArgsConstructor; -import org.apache.commons.io.input.CloseShieldInputStream; -import ru.javaops.masterjava.service.mail.Attachment; - -import javax.activation.DataHandler; -import javax.activation.DataSource; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class Attachments { - public static Attachment getAttachment(String name, InputStream inputStream) { - return new Attachment(name, new DataHandler(new InputStreamDataSource(inputStream))); - } - - // http://stackoverflow.com/questions/2830561/how-to-convert-an-inputstream-to-a-datahandler - // http://stackoverflow.com/a/10783565/548473 - @AllArgsConstructor - private static class InputStreamDataSource implements ProxyDataSource { - private InputStream inputStream; - - @Override - public InputStream getInputStream() throws IOException { - return new CloseShieldInputStream(inputStream); - } - } - - public interface ProxyDataSource extends DataSource { - - @Override - default OutputStream getOutputStream() throws IOException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - default String getContentType() { - return "application/octet-stream"; - } - - @Override - default String getName() { - return ""; - } - } -} diff --git a/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/MailUtils.java b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/MailUtils.java new file mode 100644 index 000000000..a0783dbcf --- /dev/null +++ b/services/mail-api/src/main/java/ru/javaops/masterjava/service/mail/util/MailUtils.java @@ -0,0 +1,70 @@ +package ru.javaops.masterjava.service.mail.util; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.sun.istack.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.CloseShieldInputStream; +import ru.javaops.masterjava.service.mail.Addressee; +import ru.javaops.masterjava.service.mail.Attachment; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import java.io.*; +import java.util.Set; + +public class MailUtils { + + public static Set split(String addressees) { + Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split(addressees); + return ImmutableSet.copyOf(Iterables.transform(split, Addressee::new)); + } + + @Data + @AllArgsConstructor + public static class MailObject implements Serializable { + private @NotNull String users; + private String subject; + private @NotNull String body; + private String attachName; + private byte[] attachData; + } + + public static MailObject getMailObject(String users, String subject, String body, String name, InputStream is) { + try { + return new MailObject(users, subject, body, name, is == null ? null : IOUtils.toByteArray(is)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public static Attachment getAttachment(String name, byte[] attachData) { + return new Attachment(name, new DataHandler((ProxyDataSource) () -> new ByteArrayInputStream(attachData))); + } + + public static Attachment getAttachment(String name, InputStream inputStream) { + // http://stackoverflow.com/questions/2830561/how-to-convert-an-inputstream-to-a-datahandler + // http://stackoverflow.com/a/5924019/548473 + return new Attachment(name, new DataHandler((ProxyDataSource) () -> new CloseShieldInputStream(inputStream))); + } + + public interface ProxyDataSource extends DataSource { + @Override + default OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + default String getContentType() { + return "application/octet-stream"; + } + + @Override + default String getName() { + return ""; + } + } +} diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java index d59f32628..a1ffa0eba 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/MailServiceExecutor.java @@ -1,8 +1,11 @@ package ru.javaops.masterjava.service.mail; +import com.google.common.collect.ImmutableList; import lombok.extern.slf4j.Slf4j; import one.util.streamex.StreamEx; import ru.javaops.masterjava.ExceptionType; +import ru.javaops.masterjava.service.mail.util.MailUtils; +import ru.javaops.masterjava.service.mail.util.MailUtils.MailObject; import ru.javaops.masterjava.web.WebStateException; import ru.javaops.masterjava.web.WsClient; @@ -19,6 +22,20 @@ public class MailServiceExecutor { private static final ExecutorService mailExecutor = Executors.newFixedThreadPool(8); + public static void sendAsync(MailObject mailObject) { + Set addressees = MailUtils.split(mailObject.getUsers()); + addressees.forEach(addressee -> + mailExecutor.submit(() -> { + try { + MailSender.sendTo(addressee, mailObject.getSubject(), mailObject.getBody(), + ImmutableList.of(MailUtils.getAttachment(mailObject.getAttachName(), mailObject.getAttachData()))); + } catch (WebStateException e) { + // already logged + } + }) + ); + } + public static GroupResult sendBulk(final Set addressees, final String subject, final String body, List attachments) throws WebStateException { final CompletionService completionService = new ExecutorCompletionService<>(mailExecutor); List> futures = StreamEx.of(addressees) diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java index dd8f327f8..dc602a276 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/listeners/JmsMailListener.java @@ -1,6 +1,9 @@ package ru.javaops.masterjava.service.mail.listeners; import lombok.extern.slf4j.Slf4j; +import org.apache.activemq.ActiveMQConnectionFactory; +import ru.javaops.masterjava.service.mail.MailServiceExecutor; +import ru.javaops.masterjava.service.mail.util.MailUtils; import javax.jms.*; import javax.naming.InitialContext; @@ -18,8 +21,9 @@ public class JmsMailListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { try { InitialContext initCtx = new InitialContext(); - QueueConnectionFactory connectionFactory = - (QueueConnectionFactory) initCtx.lookup("java:comp/env/jms/ConnectionFactory"); + ActiveMQConnectionFactory connectionFactory = + (ActiveMQConnectionFactory) initCtx.lookup("java:comp/env/jms/ConnectionFactory"); + connectionFactory.setTrustAllPackages(true); connection = connectionFactory.createQueueConnection(); QueueSession queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = (Queue) initCtx.lookup("java:comp/env/jms/queue/MailQueue"); @@ -30,11 +34,11 @@ public void contextInitialized(ServletContextEvent sce) { try { while (!Thread.interrupted()) { Message m = receiver.receive(); - // TODO implement mail sending - if (m instanceof TextMessage) { - TextMessage tm = (TextMessage) m; - String text = tm.getText(); - log.info("Received TextMessage with text '{}'", text); + if (m instanceof ObjectMessage) { + ObjectMessage om = (ObjectMessage) m; + MailUtils.MailObject mailObject = (MailUtils.MailObject) om.getObject(); + log.info("Received MailObject {}", mailObject); + MailServiceExecutor.sendAsync(mailObject); } } } catch (Exception e) { diff --git a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java index 05fd322f7..eb24fb5d1 100644 --- a/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java +++ b/services/mail-service/src/main/java/ru/javaops/masterjava/service/mail/rest/MailRS.java @@ -9,8 +9,7 @@ import ru.javaops.masterjava.service.mail.Attachment; import ru.javaops.masterjava.service.mail.GroupResult; import ru.javaops.masterjava.service.mail.MailServiceExecutor; -import ru.javaops.masterjava.service.mail.MailWSClient; -import ru.javaops.masterjava.service.mail.util.Attachments; +import ru.javaops.masterjava.service.mail.util.MailUtils; import ru.javaops.masterjava.web.WebStateException; import javax.activation.DataHandler; @@ -47,11 +46,11 @@ public GroupResult send(@NotBlank @FormDataParam("users") String users, String utf8name = new String(attachName.getBytes("ISO8859_1"), "UTF-8"); BodyPartEntity bodyPartEntity = ((BodyPartEntity) attachBodyPart.getEntity()); - attachments = ImmutableList.of(new Attachment(utf8name, new DataHandler((Attachments.ProxyDataSource) bodyPartEntity::getInputStream))); + attachments = ImmutableList.of(new Attachment(utf8name, new DataHandler((MailUtils.ProxyDataSource) bodyPartEntity::getInputStream))); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } - return MailServiceExecutor.sendBulk(MailWSClient.split(users), subject, body, attachments); + return MailServiceExecutor.sendBulk(MailUtils.split(users), subject, body, attachments); } } diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java index 2107d39a7..36ca04a7d 100644 --- a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/JmsSendServlet.java @@ -1,20 +1,25 @@ package ru.javaops.masterjava.webapp; import lombok.extern.slf4j.Slf4j; +import ru.javaops.masterjava.service.mail.util.MailUtils; +import ru.javaops.masterjava.service.mail.util.MailUtils.MailObject; import javax.jms.*; import javax.naming.InitialContext; import javax.servlet.ServletConfig; import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; import java.io.IOException; import java.lang.IllegalStateException; @WebServlet("/sendJms") @Slf4j +@MultipartConfig public class JmsSendServlet extends HttpServlet { private Connection connection; private Session session; @@ -46,16 +51,22 @@ public void destroy() { } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String result; try { log.info("Start sending"); req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); - String users = req.getParameter("users"); - String subject = req.getParameter("subject"); - String body = req.getParameter("body"); - result = sendJms(users, subject, body); + Part filePart = req.getPart("attach"); + + MailObject mailObject = MailUtils.getMailObject( + req.getParameter("users"), + req.getParameter("subject"), + req.getParameter("body"), + filePart == null ? null : filePart.getSubmittedFileName(), + filePart == null ? null : filePart.getInputStream()); + + result = sendJms(mailObject); log.info("Processing finished with result: {}", result); } catch (Exception e) { log.error("Processing failed", e); @@ -64,10 +75,10 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S resp.getWriter().write(result); } - private synchronized String sendJms(String users, String subject, String body) throws JMSException { - TextMessage testMessage = session.createTextMessage(); - testMessage.setText(subject); - producer.send(testMessage); + private synchronized String sendJms(MailObject mailObject) throws JMSException { + ObjectMessage om = session.createObjectMessage(); + om.setObject(mailObject); + producer.send(om); return "Successfully sent JMS message"; } } diff --git a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java index b751d2c5c..b254ab9c7 100644 --- a/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java +++ b/web/webapp/src/main/java/ru/javaops/masterjava/webapp/SoapSendServlet.java @@ -4,9 +4,8 @@ import lombok.extern.slf4j.Slf4j; import ru.javaops.masterjava.service.mail.GroupResult; import ru.javaops.masterjava.service.mail.MailWSClient; -import ru.javaops.masterjava.service.mail.util.Attachments; +import ru.javaops.masterjava.service.mail.util.MailUtils; -import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -20,7 +19,7 @@ @MultipartConfig public class SoapSendServlet extends HttpServlet { @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String result; try { log.info("Start sending"); @@ -30,9 +29,9 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String subject = req.getParameter("subject"); String body = req.getParameter("body"); Part filePart = req.getPart("attach"); - GroupResult groupResult = MailWSClient.sendBulk(MailWSClient.split(users), subject, body, + GroupResult groupResult = MailWSClient.sendBulk(MailUtils.split(users), subject, body, filePart == null ? null : - ImmutableList.of(Attachments.getAttachment(filePart.getSubmittedFileName(), filePart.getInputStream()))); + ImmutableList.of(MailUtils.getAttachment(filePart.getSubmittedFileName(), filePart.getInputStream()))); result = groupResult.toString(); log.info("Processing finished with result: {}", result); } catch (Exception e) { diff --git a/web/webapp/src/main/webapp/WEB-INF/templates/users.html b/web/webapp/src/main/webapp/WEB-INF/templates/users.html index 477fc54f6..6512fcf45 100644 --- a/web/webapp/src/main/webapp/WEB-INF/templates/users.html +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -43,9 +43,9 @@

- SOAP
- REST
- JMS
+ SOAP
+ REST
+ JMS

@@ -53,10 +53,10 @@