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/43] 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 10fc37ae66197eaa02760e92841731c9e42f098f Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Thu, 19 Oct 2017 17:21:26 +0300 Subject: [PATCH 02/43] 1 1 MailService --- .../masterjava/service/MailService.java | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/main/java/ru/javaops/masterjava/service/MailService.java b/src/main/java/ru/javaops/masterjava/service/MailService.java index 2b6aeb294..cef46e55e 100644 --- a/src/main/java/ru/javaops/masterjava/service/MailService.java +++ b/src/main/java/ru/javaops/masterjava/service/MailService.java @@ -1,8 +1,10 @@ package ru.javaops.masterjava.service; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.*; +import java.util.stream.Collectors; public class MailService { private static final String OK = "OK"; @@ -11,10 +13,74 @@ public class MailService { private static final String INTERRUPTED_BY_TIMEOUT = "+++ Interrupted by timeout"; private static final String INTERRUPTED_EXCEPTION = "+++ InterruptedException"; + private final ExecutorService mailExecutor = Executors.newFixedThreadPool(8); + public GroupResult sendToList(final String template, final Set emails) throws Exception { - return new GroupResult(0, Collections.emptyList(), null); - } + final CompletionService completionService = new ExecutorCompletionService<>(mailExecutor); + + List> futures = emails.stream() + .map(email -> completionService.submit(() -> sendToUser(template, email))) + .collect(Collectors.toList()); + return new Callable() { + private int success = 0; + private List failed = new ArrayList<>(); + + @Override + public GroupResult call() { + while (!futures.isEmpty()) { + try { + Future future = completionService.poll(10, TimeUnit.SECONDS); + if (future == null) { + return cancelWithFail(INTERRUPTED_BY_TIMEOUT); + } + futures.remove(future); + MailResult mailResult = future.get(); + if (mailResult.isOk()) { + success++; + } else { + failed.add(mailResult); + if (failed.size() >= 5) { + return cancelWithFail(INTERRUPTED_BY_FAULTS_NUMBER); + } + } + } catch (ExecutionException e) { + return cancelWithFail(e.getCause().toString()); + } catch (InterruptedException e) { + return cancelWithFail(INTERRUPTED_EXCEPTION); + } + } +/* + for (Future future : futures) { + MailResult mailResult; + try { + mailResult = future.get(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return cancelWithFail(INTERRUPTED_EXCEPTION); + } catch (ExecutionException e) { + return cancelWithFail(e.getCause().toString()); + } catch (TimeoutException e) { + return cancelWithFail(INTERRUPTED_BY_TIMEOUT); + } + if (mailResult.isOk()) { + success++; + } else { + failed.add(mailResult); + if (failed.size() >= 5) { + return cancelWithFail(INTERRUPTED_BY_FAULTS_NUMBER); + } + } + } +*/ + return new GroupResult(success, failed, null); + } + + private GroupResult cancelWithFail(String cause) { + futures.forEach(f -> f.cancel(true)); + return new GroupResult(success, failed, cause); + } + }.call(); + } // dummy realization public MailResult sendToUser(String template, String email) throws Exception { From bb669328e87f7e4a9e8d6b911c5ed643932deb01 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 24 Oct 2017 11:59:07 +0300 Subject: [PATCH 03/43] 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 046c715dd784db0133964bbb171127c8bcccd3a4 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 24 Oct 2017 12:14:01 +0300 Subject: [PATCH 04/43] 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 9518949fcd9a82824fe9a58ae522aee2a010230d Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 24 Oct 2017 13:18:37 +0300 Subject: [PATCH 05/43] 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 daa5a96657efc4f5096d25e5c73df5d10ff7cf72 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 24 Oct 2017 13:26:04 +0300 Subject: [PATCH 06/43] 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 d068a04e300d027eabf53997d202a82df7e312ac Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 24 Oct 2017 13:45:51 +0300 Subject: [PATCH 07/43] 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 97e01f3b21b37759da1493c8fc3054d71ddcf76c Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 25 Oct 2017 18:15:11 +0300 Subject: [PATCH 08/43] 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 b70bdd503aaa591385970a200eb4634b5bb94464 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Thu, 26 Oct 2017 16:51:16 +0300 Subject: [PATCH 09/43] 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 088ef4ac39a4b87d5e0cbc73e9c31a6ebbe7f8af Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 27 Oct 2017 11:27:40 +0300 Subject: [PATCH 10/43] 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 5137c8b9fbc6d360ad80a91490ccecc57e20f735 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 27 Oct 2017 11:43:13 +0300 Subject: [PATCH 11/43] 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 32e5fef7626911e2d2e3bb0753424696b81225f3 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 27 Oct 2017 12:06:54 +0300 Subject: [PATCH 12/43] 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 ae51c1c8751edb2b71c78bcfff826624fc22cc12 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 3 Nov 2017 15:13:34 +0300 Subject: [PATCH 13/43] 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..8288f5a7d 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 7ad39e10433d30b9e1706bb108bd187482e884b4 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 3 Nov 2017 17:14:17 +0300 Subject: [PATCH 14/43] 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 1ad9ff0bb8b20001eb8e1a16ebd2cb56d83bc808 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Fri, 3 Nov 2017 18:43:39 +0300 Subject: [PATCH 15/43] 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 ab882b64cb52035b91c7b1ffb2bfbb203ed148f7 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 7 Nov 2017 11:01:13 +0300 Subject: [PATCH 16/43] 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..cf18b4022 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 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 f0e6e463b56113113b4e5ff52eee8f4974a59df9 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 7 Nov 2017 11:13:41 +0300 Subject: [PATCH 17/43] 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 e894127bed4b08090f97431bef445618c2da781a Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 7 Nov 2017 17:00:22 +0300 Subject: [PATCH 18/43] 3 6 multimodule --- common/pom.xml | 21 +++++++++ import/pom.xml | 39 ++++++++++++++++ parent/pom.xml | 69 +++++++++++++++++++++++++++++ pom.xml | 118 ++++--------------------------------------------- 4 files changed, 138 insertions(+), 109 deletions(-) create mode 100644 common/pom.xml create mode 100644 import/pom.xml create mode 100644 parent/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/import/pom.xml b/import/pom.xml new file mode 100644 index 000000000..c1dfd8522 --- /dev/null +++ b/import/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + ru.javaops + parent + ../parent/pom.xml + 1.0-SNAPSHOT + + + export + 1.0-SNAPSHOT + war + Export + + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.0 + + false + + + + + + + + ru.javaops + common + ${project.version} + + + \ 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..053d377ab 100644 --- a/pom.xml +++ b/pom.xml @@ -4,119 +4,19 @@ 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 - - - - - - - - + + import + common + webapp + mail-service + mail-api + test + From e28fb7e4291af55527b74dfecb20158405deaab2 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 8 Nov 2017 09:38:18 +0300 Subject: [PATCH 19/43] 3 6 multimodule --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 053d377ab..ffd6d4dc0 100644 --- a/pom.xml +++ b/pom.xml @@ -14,9 +14,11 @@ import common + From 5185f4aea9f0cc20151ec31aa970c1a18e0fb4e6 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 8 Nov 2017 09:55:36 +0300 Subject: [PATCH 20/43] 4 0 rename upload --- pom.xml | 2 +- {import => upload}/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename {import => upload}/pom.xml (95%) diff --git a/pom.xml b/pom.xml index ffd6d4dc0..0085819cf 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ https://github.com/JavaOPs/masterjava - import + upload common + + web + services diff --git a/services/mail-api/pom.xml b/services/mail-api/pom.xml new file mode 100644 index 000000000..25c53388e --- /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} + + + + \ No newline at end of file diff --git a/services/mail-service/pom.xml b/services/mail-service/pom.xml new file mode 100644 index 000000000..2b4be6bad --- /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} + + + \ No newline at end of file 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 98% 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 cef46e55e..e26f302d2 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,4 +1,4 @@ -package ru.javaops.masterjava.service; +package ru.javaops.masterjava.service.mail; import java.util.ArrayList; import java.util.List; @@ -6,7 +6,7 @@ import java.util.concurrent.*; import java.util.stream.Collectors; -public class MailService { +public class MailServiceExecutor { private static final String OK = "OK"; private static final String INTERRUPTED_BY_FAULTS_NUMBER = "+++ Interrupted by faults number"; 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..f469908cf --- /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 + + + \ No newline at end of file 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..c306de359 --- /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 + + + import + + + + + com.j2html + j2html + 0.7 + + + + \ No newline at end of file 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 85% 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..6a83965e1 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,13 +1,8 @@ + 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; 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..60bb1c8c3 --- /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} + + + \ No newline at end of file From 6cb99506cbf9013c55043b93cce4ed83ec7ff06b Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 8 Nov 2017 13:35:42 +0300 Subject: [PATCH 22/43] 4 2 HW3 thymeleaf upload --- web/common-web/pom.xml | 42 +++++++++++++ .../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 | 56 +++++++++++++++++ .../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 +++++ 10 files changed, 307 insertions(+) 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/web/common-web/pom.xml b/web/common-web/pom.xml new file mode 100644 index 000000000..a72bf0040 --- /dev/null +++ b/web/common-web/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + + ru.javaops + parent + ../../parent/pom.xml + 1.0-SNAPSHOT + + + common-web + 1.0-SNAPSHOT + Common Web + + + + ${project.groupId} + common + ${project.version} + + + commons-fileupload + commons-fileupload + 1.3.3 + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + org.thymeleaf + thymeleaf + 3.0.8.RELEASE + + + \ No newline at end of file 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..86e82db66 --- /dev/null +++ b/web/upload/src/main/java/ru/javaops/masterjava/upload/UploadServlet.java @@ -0,0 +1,56 @@ +package ru.javaops.masterjava.upload; + +import org.apache.commons.fileupload.FileItemIterator; +import org.apache.commons.fileupload.FileItemStream; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.thymeleaf.context.WebContext; +import ru.javaops.masterjava.model.User; + +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.io.InputStream; +import java.util.List; + +import static ru.javaops.masterjava.common.web.ThymeleafListener.engine; + +@WebServlet("/") +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 ServletFileUpload upload = new ServletFileUpload(); + final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); + + try { +// https://commons.apache.org/proper/commons-fileupload/streaming.html + + final FileItemIterator itemIterator = upload.getItemIterator(req); + while (itemIterator.hasNext()) { //expect that it's only one file + FileItemStream fileItemStream = itemIterator.next(); + if (!fileItemStream.isFormField()) { + try (InputStream is = fileItemStream.openStream()) { + List users = userProcessor.process(is); + webContext.setVariable("users", users); + engine.process("result", webContext, resp.getWriter()); + } + break; + } + } + } 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..2b5e6fe5a --- /dev/null +++ b/web/upload/src/main/webapp/WEB-INF/templates/exception.html @@ -0,0 +1,20 @@ + + + + Application error + + + +
+
+
+

Application error:

+ +

exception.message

+
    +
  • +
+
+
+ + \ No newline at end of file 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..01ec88cfd --- /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
+ + \ No newline at end of file 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..d48c3123e --- /dev/null +++ b/web/upload/src/main/webapp/WEB-INF/templates/upload.html @@ -0,0 +1,16 @@ + + + Upload XML + + +
+

Select xml file to upload

+

+
+

+

+ +

+
+ + \ No newline at end of file From 9d8aac7950d6a9d23a1106b8a051f1d88be649fc Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 8 Nov 2017 13:36:38 +0300 Subject: [PATCH 23/43] 4 3 HW3 upload servlet3 --- web/common-web/pom.xml | 5 --- .../masterjava/upload/UploadServlet.java | 31 ++++++++----------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/web/common-web/pom.xml b/web/common-web/pom.xml index a72bf0040..98ba46590 100644 --- a/web/common-web/pom.xml +++ b/web/common-web/pom.xml @@ -21,11 +21,6 @@ common ${project.version}
- - commons-fileupload - commons-fileupload - 1.3.3 - javax.servlet javax.servlet-api 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 86e82db66..1566fa650 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,23 +1,23 @@ package ru.javaops.masterjava.upload; -import org.apache.commons.fileupload.FileItemIterator; -import org.apache.commons.fileupload.FileItemStream; -import org.apache.commons.fileupload.servlet.ServletFileUpload; 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("/") +@WebServlet(urlPatterns = "/", loadOnStartup = 1) +@MultipartConfig public class UploadServlet extends HttpServlet { private final UserProcessor userProcessor = new UserProcessor(); @@ -30,23 +30,18 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final ServletFileUpload upload = new ServletFileUpload(); final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale()); try { -// https://commons.apache.org/proper/commons-fileupload/streaming.html - - final FileItemIterator itemIterator = upload.getItemIterator(req); - while (itemIterator.hasNext()) { //expect that it's only one file - FileItemStream fileItemStream = itemIterator.next(); - if (!fileItemStream.isFormField()) { - try (InputStream is = fileItemStream.openStream()) { - List users = userProcessor.process(is); - webContext.setVariable("users", users); - engine.process("result", webContext, resp.getWriter()); - } - break; - } +// 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); From 5c56bfa3633f7e793b0c448162357524a786ac3f Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 13 Nov 2017 13:21:58 +0300 Subject: [PATCH 24/43] 4 4 dependencies --- parent/pom.xml | 2 ++ pom.xml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/parent/pom.xml b/parent/pom.xml index d7b056d1d..c32ae495a 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -14,6 +14,8 @@ 1.8 UTF-8 UTF-8 + + false 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 38ea7a190f6a2bbcf1c995aa628e745a0073264c Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 13 Nov 2017 13:23:16 +0300 Subject: [PATCH 25/43] 4 5 fix convergence --- parent/pom.xml | 2 +- test/pom.xml | 4 ++-- web/upload/pom.xml | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index c32ae495a..e69f61baf 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 f469908cf..5ad3d7da2 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 c306de359..7c28bf231 100644 --- a/web/upload/pom.xml +++ b/web/upload/pom.xml @@ -24,7 +24,13 @@ com.j2html j2html - 0.7 + 1.2.0 + + + com.google.guava + guava + + From 7b574495f735ce39257fc7a5c5639ce4ddf2daa4 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 13 Nov 2017 13:23:58 +0300 Subject: [PATCH 26/43] 4 6 enforcer --- parent/pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/parent/pom.xml b/parent/pom.xml index e69f61baf..464395e02 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 2312790b10d1aafdbcfb42643a02be6850008bbf Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 13 Nov 2017 14:31:06 +0300 Subject: [PATCH 27/43] 4 7 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..b6815ab8d --- /dev/null +++ b/config_templates/logback-test.xml @@ -0,0 +1,20 @@ + + + + true + + + + + UTF-8 + %-5level %logger{0} [%file:%line] %msg%n + + + + + + + + + + \ No newline at end of file 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 6083235c1..57c9ba570 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 464395e02..7511d4c7c 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 98ba46590..37ca1f642 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 + + \ No newline at end of file From 4e8a9dcf9383d6116cc85c5b6d2bab723337eff8 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 14 Nov 2017 11:47:18 +0300 Subject: [PATCH 28/43] 4 8 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 670c4a74db6096e7ed9f031a12581bce55c9629e Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 4 Dec 2017 17:53:44 +0300 Subject: [PATCH 29/43] 4 10 fix upload --- config_templates/context.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_templates/context.xml b/config_templates/context.xml index 40bb8aa0c..2cb0e614a 100644 --- a/config_templates/context.xml +++ b/config_templates/context.xml @@ -42,8 +42,8 @@ testOnBorrow="true" logAbandoned="true" driverClassName="org.postgresql.Driver" - username="user" - password="password" + username="postgres" + password="postgres" url="jdbc:postgresql://localhost:5432/masterjava"/> From ae261223217529dfcd9a21ab780e90218ff4138a Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 4 Dec 2017 18:59:20 +0300 Subject: [PATCH 30/43] 4 9 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 | 43 ++++++++++----- .../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, 342 insertions(+), 16 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 b6815ab8d..739dd6dcf 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..6efb20ca3 --- /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 + + + \ No newline at end of file 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..f4445cb50 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; @@ -60,4 +79,4 @@ public String toString() { ", flag=" + flag + ')'; } -} +} \ No newline at end of file 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..b194be03b --- /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", "postgres", "postgres"); + } + + 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); + }); + } +} \ No newline at end of file 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..774b3aa43 --- /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(UserTestData.FIST5_USERS, users); + } +} \ No newline at end of file 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 7c28bf231..83df869ed 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 1566fa650..dc5c9e0b1 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 4c81d3483..0e2874ced 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.util.StaxStreamProcessor; import javax.xml.stream.XMLStreamException; From 4be55a306f5c013ff700a5b4f99729ca03904c05 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 4 Dec 2017 19:06:58 +0300 Subject: [PATCH 31/43] 5 0 fix HW3 jaxb stax --- web/upload/pom.xml | 2 +- .../masterjava/upload/UploadServlet.java | 2 +- .../masterjava/upload/UserProcessor.java | 14 ++-- .../masterjava/xml/util/JaxbMarshaller.java | 15 ++-- .../masterjava/xml/util/JaxbParser.java | 71 ++++++++----------- .../masterjava/xml/util/JaxbUnmarshaller.java | 14 ++-- .../java/ru/javaops/masterjava/MainXml.java | 13 ++-- .../masterjava/xml/util/JaxbParserTest.java | 22 +++--- 8 files changed, 76 insertions(+), 77 deletions(-) diff --git a/web/upload/pom.xml b/web/upload/pom.xml index 83df869ed..b6521abf5 100644 --- a/web/upload/pom.xml +++ b/web/upload/pom.xml @@ -17,7 +17,7 @@ Upload - import + upload 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 dc5c9e0b1..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 @@ -17,7 +17,7 @@ import static ru.javaops.masterjava.common.web.ThymeleafListener.engine; @WebServlet(urlPatterns = "/", loadOnStartup = 1) -@MultipartConfig +@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit public class UploadServlet extends HttpServlet { private final UserProcessor userProcessor = new UserProcessor(); 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 0e2874ced..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 @@ -2,8 +2,12 @@ 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; 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..507825dda 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); } - -} +} \ No newline at end of file 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 cf18b4022..f9f29c2c9 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,20 +16,20 @@ 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 T unmarshal(XMLStreamReader reader, Class elementClass) throws JAXBException { 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..4754b17d6 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 2d7e45e0306d94f7d9200699022cb23a6e700af6 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Mon, 4 Dec 2017 20:01:50 +0300 Subject: [PATCH 32/43] 5 1 HW4 upload chunk --- parent-web/pom.xml | 41 +++++++++++++++++ .../masterjava/persist/dao/UserDao.java | 8 +++- .../masterjava/persist/dao/UserDaoTest.java | 7 +++ .../masterjava/upload/UploadServlet.java | 46 +++++++++++++------ .../masterjava/upload/UserProcessor.java | 6 ++- .../main/webapp/WEB-INF/templates/upload.html | 8 +++- 6 files changed, 98 insertions(+), 18 deletions(-) diff --git a/parent-web/pom.xml b/parent-web/pom.xml index 57c9ba570..bf211d9dc 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..ff760f7bc 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,11 @@ public User insert(User user) { return user; } - @SqlUpdate("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS user_flag)) ") + @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 +35,7 @@ public User insert(User user) { @SqlUpdate("TRUNCATE users") @Override public abstract void clean(); + + @SqlBatch("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG))") + public abstract void insertBatch(@BindBean List users, @BatchChunkSize int chunkSize); } 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 774b3aa43..b526168fe 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,11 @@ public void getWithLimit() { List users = dao.getWithLimit(5); Assert.assertEquals(UserTestData.FIST5_USERS, users); } + + @Test + public void insertBatch() throws Exception { + dao.clean(); + dao.insertBatch(FIST5_USERS, 3); + Assert.assertEquals(5, dao.getWithLimit(100).size()); + } } \ No newline at end of file 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..7591e7142 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,8 @@ package ru.javaops.masterjava.upload; +import com.google.common.collect.ImmutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.thymeleaf.context.WebContext; import ru.javaops.masterjava.persist.model.User; @@ -19,33 +22,48 @@ @WebServlet(urlPatterns = "/", loadOnStartup = 1) @MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit public class UploadServlet extends HttpServlet { + private static final Logger log = LoggerFactory.getLogger(UploadServlet.class); + 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 users = userProcessor.process(is, chunkSize); + log.info("Successfully uploaded " + users.size() + " users"); + final WebContext webContext = + new WebContext(req, resp, req.getServletContext(), req.getLocale(), + ImmutableMap.of("users", users)); + 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..31f4254a5 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,7 @@ package ru.javaops.masterjava.upload; +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,8 +18,9 @@ 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 { + public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { final StaxStreamProcessor processor = new StaxStreamProcessor(is); List users = new ArrayList<>(); @@ -27,6 +30,7 @@ public List process(final InputStream is) throws XMLStreamException, JAXBE final User user = new User(xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); users.add(user); } + userDao.insertBatch(users, chunkSize); return users; } } 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 d48c3123e..12351c91c 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 0e2ed1c110b5e51ec15cc47f8f82ba166639b9bc Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 10:09:25 +0300 Subject: [PATCH 33/43] 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 60bb1c8c3..de3a56fc2 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..0ae713167 --- /dev/null +++ b/web/webapp/src/main/webapp/WEB-INF/templates/users.html @@ -0,0 +1,27 @@ + + + + Users + + + + + + + + + + + + + + + + + + + + +
#Full NameEmailFlag
+ + \ No newline at end of file From e00faa30aa05fc54501cda3b29924ab77b302a7c Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 10:34:58 +0300 Subject: [PATCH 34/43] 5 3 HW4 already present --- config_templates/sql/initDB.sql | 1 + .../ru/javaops/masterjava/persist/dao/UserDao.java | 7 +++++-- .../ru/javaops/masterjava/upload/UploadServlet.java | 6 +++--- .../ru/javaops/masterjava/upload/UserProcessor.java | 11 +++++++++-- .../src/main/webapp/WEB-INF/templates/result.html | 4 ++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/config_templates/sql/initDB.sql b/config_templates/sql/initDB.sql index 888ba8141..09b463d69 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); \ No newline at end of file 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 ff760f7bc..50bf5bc0b 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 @@ -36,6 +36,9 @@ public User insert(User user) { @Override public abstract void clean(); - @SqlBatch("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG))") - public abstract void insertBatch(@BindBean List users, @BatchChunkSize int chunkSize); + // https://habrahabr.ru/post/264281/ + @SqlBatch("INSERT INTO users (full_name, email, flag) VALUES (: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); } 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 7591e7142..5ef87b0c5 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 @@ -44,11 +44,11 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } else { Part filePart = req.getPart("fileToUpload"); try (InputStream is = filePart.getInputStream()) { - List users = userProcessor.process(is, chunkSize); - log.info("Successfully uploaded " + users.size() + " users"); + List alreadyPresentUsers = userProcessor.process(is, chunkSize); + log.info("Already present in DB " + alreadyPresentUsers.size() + " users"); final WebContext webContext = new WebContext(req, resp, req.getServletContext(), req.getLocale(), - ImmutableMap.of("users", users)); + ImmutableMap.of("users", alreadyPresentUsers)); engine.process("result", webContext, resp.getWriter()); return; } 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 31f4254a5..ec7e05827 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,6 @@ package ru.javaops.masterjava.upload; +import one.util.streamex.IntStreamEx; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; @@ -20,6 +21,9 @@ public class UserProcessor { private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); private static UserDao userDao = DBIProvider.getDao(UserDao.class); + /* + * return users, already present in DB + */ public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { final StaxStreamProcessor processor = new StaxStreamProcessor(is); List users = new ArrayList<>(); @@ -30,7 +34,10 @@ public List process(final InputStream is, int chunkSize) throws XMLStreamE final User user = new User(xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); users.add(user); } - userDao.insertBatch(users, chunkSize); - return users; + int[] result = userDao.insertBatch(users, chunkSize); + return IntStreamEx.range(0, users.size()) + .filter(i -> result[i] == 0) + .mapToObj(users::get) + .toList(); } } 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 01ec88cfd..3d0e3ebcb 100644 --- a/web/upload/src/main/webapp/WEB-INF/templates/result.html +++ b/web/upload/src/main/webapp/WEB-INF/templates/result.html @@ -1,11 +1,11 @@ - Uploaded users + Already present users

Upload XML

-

Uploaded users

+

Already present users

From 1e34ae88c4ca7dab57600468eb02fbb114044b43 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 10:55:56 +0300 Subject: [PATCH 35/43] 5 4 HW4 parallel --- .../masterjava/persist/dao/UserDao.java | 23 +++- .../masterjava/persist/dao/UserDaoTest.java | 7 ++ .../masterjava/upload/UploadServlet.java | 8 +- .../masterjava/upload/UserProcessor.java | 107 +++++++++++++++--- .../main/webapp/WEB-INF/templates/result.html | 25 +--- 5 files changed, 129 insertions(+), 41 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 50bf5bc0b..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; @@ -21,6 +23,16 @@ public User insert(User user) { return user; } + @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); @@ -37,8 +49,17 @@ public User insert(User user) { public abstract void clean(); // https://habrahabr.ru/post/264281/ - @SqlBatch("INSERT INTO users (full_name, email, flag) VALUES (:fullName, :email, CAST(:flag AS USER_FLAG))" + + @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 b526168fe..14e84af2d 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 @@ -39,4 +39,11 @@ public void insertBatch() throws Exception { 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); + } } \ No newline at end of file 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 5ef87b0c5..9b726737e 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 @@ -4,7 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.context.WebContext; -import ru.javaops.masterjava.persist.model.User; + import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; @@ -44,11 +44,11 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } else { Part filePart = req.getPart("fileToUpload"); try (InputStream is = filePart.getInputStream()) { - List alreadyPresentUsers = userProcessor.process(is, chunkSize); - log.info("Already present in DB " + alreadyPresentUsers.size() + " users"); + 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", alreadyPresentUsers)); + ImmutableMap.of("users", failed)); engine.process("result", webContext, resp.getWriter()); return; } 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 ec7e05827..e2199205c 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,6 +1,8 @@ package ru.javaops.masterjava.upload; -import one.util.streamex.IntStreamEx; +import one.util.streamex.StreamEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ru.javaops.masterjava.persist.DBIProvider; import ru.javaops.masterjava.persist.dao.UserDao; import ru.javaops.masterjava.persist.model.User; @@ -16,28 +18,99 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; public class UserProcessor { + private static final Logger log = LoggerFactory.getLogger(UserProcessor.class); + 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); + + public static class FailedChunk { + public String emailOrRange; + public String reason; + + public FailedChunk(String emailOrRange, String reason) { + this.emailOrRange = emailOrRange; + this.reason = reason; + } + + @Override + public String toString() { + return emailOrRange + " : " + reason; + } + } + /* - * return users, already present in DB + * return failed users chunks */ - public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { - final StaxStreamProcessor processor = new StaxStreamProcessor(is); - List users = new ArrayList<>(); - - 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); - } - int[] result = userDao.insertBatch(users, chunkSize); - return IntStreamEx.range(0, users.size()) - .filter(i -> result[i] == 0) - .mapToObj(users::get) - .toList(); + public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { + log.info("Start processing with chunkSize=" + chunkSize); + + return new Callable>() { + class ChunkFuture { + String emailRange; + Future> future; + + public ChunkFuture(List chunk, Future> future) { + this.future = future; + this.emailRange = chunk.get(0).getEmail(); + if (chunk.size() > 1) { + this.emailRange += '-' + chunk.get(chunk.size() - 1).getEmail(); + } + } + } + + @Override + public List call() throws XMLStreamException, JAXBException { + List futures = new ArrayList<>(); + + int id = userDao.getSeqAndSkip(chunkSize); + List chunk = new ArrayList<>(chunkSize); + final StaxStreamProcessor processor = new StaxStreamProcessor(is); + 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(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); + chunk.add(user); + if (chunk.size() == chunkSize) { + futures.add(submit(chunk)); + chunk = new ArrayList<>(chunkSize); + id = userDao.getSeqAndSkip(chunkSize); + } + } + + if (!chunk.isEmpty()) { + futures.add(submit(chunk)); + } + + List failed = new ArrayList<>(); + futures.forEach(cf -> { + try { + failed.addAll(StreamEx.of(cf.future.get()).map(email -> new FailedChunk(email, "already present")).toList()); + log.info(cf.emailRange + " successfully executed"); + } catch (Exception e) { + log.error(cf.emailRange + " failed", e); + failed.add(new FailedChunk(cf.emailRange, e.toString())); + } + }); + return failed; + } + + private ChunkFuture submit(List chunk) { + ChunkFuture chunkFuture = new ChunkFuture(chunk, + executorService.submit(() -> userDao.insertAndGetConflictEmails(chunk)) + ); + log.info("Submit chunk: " + chunkFuture.emailRange); + return chunkFuture; + } + }.call(); } } 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 3d0e3ebcb..57d01fd24 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 @@ - Already present users + Failed users

Upload XML

-

Already present users

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

Failed users

+
    + +
  • +
\ No newline at end of file From c443b96f55eee208187b10dac7a557f0adf3e209 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 11:12:20 +0300 Subject: [PATCH 36/43] 5 5 HW4 parallel2 --- .../masterjava/upload/UploadServlet.java | 2 +- .../masterjava/upload/UserProcessor.java | 111 ++++++++---------- .../main/webapp/WEB-INF/templates/result.html | 2 +- 3 files changed, 51 insertions(+), 64 deletions(-) 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 9b726737e..0c56353df 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 @@ -44,7 +44,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 = userProcessor.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 e2199205c..3a351d71f 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,6 +1,5 @@ package ru.javaops.masterjava.upload; -import one.util.streamex.StreamEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.javaops.masterjava.persist.DBIProvider; @@ -17,8 +16,10 @@ 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.concurrent.Callable; +import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -32,85 +33,71 @@ public class UserProcessor { private ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_THREADS); - public static class FailedChunk { - public String emailOrRange; + public static class FailedEmails { + public String emailsOrRange; public String reason; - public FailedChunk(String emailOrRange, String reason) { - this.emailOrRange = emailOrRange; + public FailedEmails(String emailsOrRange, String reason) { + this.emailsOrRange = emailsOrRange; this.reason = reason; } @Override public String toString() { - return emailOrRange + " : " + reason; + return emailsOrRange + " : " + reason; } } /* * return failed users chunks */ - public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { + public List process(final InputStream is, int chunkSize) throws XMLStreamException, JAXBException { log.info("Start processing with chunkSize=" + chunkSize); - return new Callable>() { - class ChunkFuture { - String emailRange; - Future> future; - - public ChunkFuture(List chunk, Future> future) { - this.future = future; - this.emailRange = chunk.get(0).getEmail(); - if (chunk.size() > 1) { - this.emailRange += '-' + chunk.get(chunk.size() - 1).getEmail(); - } - } + Map>> chunkFutures = new LinkedHashMap<>(); // ordered map (emailRange -> chunk future) + + int id = userDao.getSeqAndSkip(chunkSize); + List chunk = new ArrayList<>(chunkSize); + final StaxStreamProcessor processor = new StaxStreamProcessor(is); + 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(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); } + } - @Override - public List call() throws XMLStreamException, JAXBException { - List futures = new ArrayList<>(); - - int id = userDao.getSeqAndSkip(chunkSize); - List chunk = new ArrayList<>(chunkSize); - final StaxStreamProcessor processor = new StaxStreamProcessor(is); - 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(id++, xmlUser.getValue(), xmlUser.getEmail(), UserFlag.valueOf(xmlUser.getFlag().value())); - chunk.add(user); - if (chunk.size() == chunkSize) { - futures.add(submit(chunk)); - chunk = new ArrayList<>(chunkSize); - id = userDao.getSeqAndSkip(chunkSize); - } - } - - if (!chunk.isEmpty()) { - futures.add(submit(chunk)); - } + if (!chunk.isEmpty()) { + addChunkFutures(chunkFutures, chunk); + } - List failed = new ArrayList<>(); - futures.forEach(cf -> { - try { - failed.addAll(StreamEx.of(cf.future.get()).map(email -> new FailedChunk(email, "already present")).toList()); - log.info(cf.emailRange + " successfully executed"); - } catch (Exception e) { - log.error(cf.emailRange + " failed", e); - failed.add(new FailedChunk(cf.emailRange, e.toString())); - } - }); - return failed; + 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 ChunkFuture submit(List chunk) { - ChunkFuture chunkFuture = new ChunkFuture(chunk, - executorService.submit(() -> userDao.insertAndGetConflictEmails(chunk)) - ); - log.info("Submit chunk: " + chunkFuture.emailRange); - return chunkFuture; - } - }.call(); + 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/result.html b/web/upload/src/main/webapp/WEB-INF/templates/result.html index 57d01fd24..497dffe3f 100644 --- a/web/upload/src/main/webapp/WEB-INF/templates/result.html +++ b/web/upload/src/main/webapp/WEB-INF/templates/result.html @@ -7,7 +7,7 @@

Upload XML

Failed users

    - +
From e6f571065e317786a7b543e983cbcb68326426cc Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 12:12:03 +0300 Subject: [PATCH 37/43] 5 6 typesafe config --- common/pom.xml | 30 +++++++++++++++++++ .../ru/javaops/masterjava/config/Configs.java | 19 ++++++++++++ parent/pom.xml | 25 ---------------- persist/src/main/resources/persist.conf | 7 +++++ .../masterjava/persist/DBITestProvider.java | 6 +++- 5 files changed, 61 insertions(+), 26 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..6e769be52 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 7511d4c7c..f50e4636b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -76,31 +76,6 @@ - - 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} - - junit 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 b194be03b..5091a52a8 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", "postgres", "postgres"); + 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 667a3509e61090920a2f39c23f4589c071cca079 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 12:43:31 +0300 Subject: [PATCH 38/43] 5 7 lombok --- parent/pom.xml | 7 ++ .../masterjava/persist/DBIProvider.java | 6 +- .../masterjava/persist/model/BaseEntity.java | 21 ++--- .../masterjava/persist/model/User.java | 82 +++---------------- .../masterjava/upload/UploadServlet.java | 5 +- .../masterjava/upload/UserProcessor.java | 18 ++-- 6 files changed, 35 insertions(+), 104 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index f50e4636b..ad642c479 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -76,6 +76,13 @@ + + org.projectlombok + lombok + 1.16.18 + provided + + junit 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 f4445cb50..927a299f3 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; } } \ No newline at end of file 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 0c56353df..0e5406721 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,8 +1,7 @@ package ru.javaops.masterjava.upload; import com.google.common.collect.ImmutableMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.thymeleaf.context.WebContext; @@ -21,8 +20,8 @@ @WebServlet(urlPatterns = "/", loadOnStartup = 1) @MultipartConfig(fileSizeThreshold = 1024 * 1024 * 10) //10 MB in memory limit +@Slf4j public class UploadServlet extends HttpServlet { - private static final Logger log = LoggerFactory.getLogger(UploadServlet.class); private static final int CHUNK_SIZE = 2000; private final UserProcessor userProcessor = new UserProcessor(); 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 3a351d71f..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 @@ -1,14 +1,14 @@ package ru.javaops.masterjava.upload; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +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; 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; @@ -24,8 +24,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; +@Slf4j public class UserProcessor { - private static final Logger log = LoggerFactory.getLogger(UserProcessor.class); private static final int NUMBER_THREADS = 4; private static final JaxbParser jaxbParser = new JaxbParser(ObjectFactory.class); @@ -33,15 +33,11 @@ public class UserProcessor { private ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_THREADS); + @AllArgsConstructor public static class FailedEmails { public String emailsOrRange; public String reason; - public FailedEmails(String emailsOrRange, String reason) { - this.emailsOrRange = emailsOrRange; - this.reason = reason; - } - @Override public String toString() { return emailsOrRange + " : " + reason; @@ -58,8 +54,8 @@ public List process(final InputStream is, int chunkSize) throws XM int id = userDao.getSeqAndSkip(chunkSize); List chunk = new ArrayList<>(chunkSize); - final StaxStreamProcessor processor = new StaxStreamProcessor(is); - JaxbUnmarshaller unmarshaller = jaxbParser.createUnmarshaller(); + val processor = new StaxStreamProcessor(is); + val 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); From 8007746c6da1a83d7418fdd33d7d91e010d242d0 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 13:58:30 +0300 Subject: [PATCH 39/43] 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..974f9868a --- /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; +} \ No newline at end of file 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 927a299f3..c34f3020b 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; } } \ No newline at end of file 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..f647dfcff --- /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; +} \ No newline at end of file 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..80f23598b --- /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 \ No newline at end of file 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 1bd5451c365dda2435eaef519e595bb998ffa363 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 14:18:36 +0300 Subject: [PATCH 40/43] 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 ++++++++++++++ sql/lb_apply.bat | 6 +-- 15 files changed, 487 insertions(+), 9 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..2306a85e4 --- /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(); + } +} \ No newline at end of file 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..d4dbf19ab --- /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()); + } +} \ No newline at end of file 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..467fad4b0 --- /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()); + } +} \ No newline at end of file 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..b364e3f02 --- /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()); + } +} \ No newline at end of file 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..a46f6dbd3 --- /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); + } +} \ No newline at end of file diff --git a/sql/lb_apply.bat b/sql/lb_apply.bat index 80f23598b..03f457f75 100644 --- a/sql/lb_apply.bat +++ b/sql/lb_apply.bat @@ -1,8 +1,8 @@ -set LB_HOME=c:\java\liquibase-3.5.3 +set LB_HOME=e:\JavaTools\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 ^ +--username=postgres ^ +--password=postgres ^ migrate \ No newline at end of file From 93d5cc0d496a6e6f8f949f74df67a664f7d34b87 Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 14:20:59 +0300 Subject: [PATCH 41/43] 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..74bc595f0 --- /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); + } +} \ No newline at end of file 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 0e5406721..894e3a006 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 @@ -24,7 +24,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 { @@ -43,7 +43,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 5c7351fea003f9d99ca64ad194d0315ce77ece6e Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Tue, 5 Dec 2017 19:14:05 +0300 Subject: [PATCH 42/43] 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 74bc595f0..38af0f35a 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); } } \ No newline at end of file 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..72a2e0422 --- /dev/null +++ b/web/upload/src/test/resources/payload_bad.xml @@ -0,0 +1,31 @@ + + + + + Topjava + + + + + + Masterjava + + + + + Санкт-Петербург + Москва + Киев + Минск + + + Full Name + Admin + Deleted + User1 + User2 + User3 + + \ No newline at end of file From bf6cd3a2e3198c25e149eb927d261d1fbfd5b8bc Mon Sep 17 00:00:00 2001 From: SlavaSim Date: Wed, 13 Dec 2017 15:55:58 +0300 Subject: [PATCH 43/43] 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..2ebaf8d76 --- /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); +} \ No newline at end of file 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..fd649330a --- /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); + } +} \ No newline at end of file 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()); + } +}