diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java new file mode 100644 index 0000000000..dc2a324c12 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/BacktrackingAlgorithm.java @@ -0,0 +1,104 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.stream.IntStream; + +public class BacktrackingAlgorithm { + + private static final int BOARD_SIZE = 9; + private static final int SUBSECTION_SIZE = 3; + private static final int BOARD_START_INDEX = 0; + + private static final int NO_VALUE = 0; + private static final int MIN_VALUE = 1; + private static final int MAX_VALUE = 9; + + private static int[][] board = { + {8, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 3, 6, 0, 0, 0, 0, 0}, + {0, 7, 0, 0, 9, 0, 2, 0, 0}, + {0, 5, 0, 0, 0, 7, 0, 0, 0}, + {0, 0, 0, 0, 4, 5, 7, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 3, 0}, + {0, 0, 1, 0, 0, 0, 0, 6, 8}, + {0, 0, 8, 5, 0, 0, 0, 1, 0}, + {0, 9, 0, 0, 0, 0, 4, 0, 0} + }; + + public static void main(String[] args) { + BacktrackingAlgorithm solver = new BacktrackingAlgorithm(); + solver.solve(board); + solver.printBoard(); + } + + private void printBoard() { + for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) { + for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) { + System.out.print(board[row][column] + " "); + } + System.out.println(); + } + } + + private boolean solve(int[][] board) { + for (int r = BOARD_START_INDEX; r < BOARD_SIZE; r++) { + for (int c = BOARD_START_INDEX; c < BOARD_SIZE; c++) { + if (board[r][c] == NO_VALUE) { + for (int k = MIN_VALUE; k <= MAX_VALUE; k++) { + board[r][c] = k; + if (isValid(board, r, c) && solve(board)) { + return true; + } + board[r][c] = NO_VALUE; + } + return false; + } + } + } + return true; + } + + private boolean isValid(int[][] board, int r, int c) { + return rowConstraint(board, r) && + columnConstraint(board, c) && + subsectionConstraint(board, r, c); + } + + private boolean subsectionConstraint(int[][] board, int r, int c) { + boolean[] constraint = new boolean[BOARD_SIZE]; + int subsectionRowStart = (r / SUBSECTION_SIZE) * SUBSECTION_SIZE; + int subsectionRowEnd = subsectionRowStart + SUBSECTION_SIZE; + + int subsectionColumnStart = (c / SUBSECTION_SIZE) * SUBSECTION_SIZE; + int subsectionColumnEnd = subsectionColumnStart + SUBSECTION_SIZE; + + for (int i = subsectionRowStart; i < subsectionRowEnd; i++) { + for (int j = subsectionColumnStart; j < subsectionColumnEnd; j++) { + if (!checkConstraint(board, i, constraint, j)) return false; + } + } + return true; + } + + private boolean columnConstraint(int[][] board, int c) { + boolean[] constraint = new boolean[BOARD_SIZE]; + return IntStream.range(BOARD_START_INDEX, BOARD_SIZE) + .allMatch(i -> checkConstraint(board, i, constraint, c)); + } + + private boolean rowConstraint(int[][] board, int r) { + boolean[] constraint = new boolean[BOARD_SIZE]; + return IntStream.range(BOARD_START_INDEX, BOARD_SIZE) + .allMatch(i -> checkConstraint(board, r, constraint, i)); + } + + private boolean checkConstraint(int[][] board, int r, boolean[] constraint, int c) { + if (board[r][c] != NO_VALUE) { + if (!constraint[board[r][c] - 1]) { + constraint[board[r][c] - 1] = true; + } else { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java new file mode 100644 index 0000000000..46995ca42f --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/ColumnNode.java @@ -0,0 +1,33 @@ +package com.baeldung.algorithms.sudoku; + +class ColumnNode extends DancingNode { + int size; + String name; + + ColumnNode(String n) { + super(); + size = 0; + name = n; + C = this; + } + + void cover() { + unlinkLR(); + for (DancingNode i = this.D; i != this; i = i.D) { + for (DancingNode j = i.R; j != i; j = j.R) { + j.unlinkUD(); + j.C.size--; + } + } + } + + void uncover() { + for (DancingNode i = this.U; i != this; i = i.U) { + for (DancingNode j = i.L; j != i; j = j.L) { + j.C.size++; + j.relinkUD(); + } + } + relinkLR(); + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java new file mode 100644 index 0000000000..e5a02b7c91 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinks.java @@ -0,0 +1,133 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class DancingLinks { + + private ColumnNode header; + private List answer; + + private void search(int k) { + if (header.R == header) { + handleSolution(answer); + } else { + ColumnNode c = selectColumnNodeHeuristic(); + c.cover(); + + for (DancingNode r = c.D; r != c; r = r.D) { + answer.add(r); + + for (DancingNode j = r.R; j != r; j = j.R) { + j.C.cover(); + } + + search(k + 1); + + r = answer.remove(answer.size() - 1); + c = r.C; + + for (DancingNode j = r.L; j != r; j = j.L) { + j.C.uncover(); + } + } + c.uncover(); + } + } + + private ColumnNode selectColumnNodeHeuristic() { + int min = Integer.MAX_VALUE; + ColumnNode ret = null; + for (ColumnNode c = (ColumnNode) header.R; c != header; c = (ColumnNode) c.R) { + if (c.size < min) { + min = c.size; + ret = c; + } + } + return ret; + } + + private ColumnNode makeDLXBoard(boolean[][] grid) { + final int COLS = grid[0].length; + + ColumnNode headerNode = new ColumnNode("header"); + List columnNodes = new ArrayList<>(); + + for (int i = 0; i < COLS; i++) { + ColumnNode n = new ColumnNode(Integer.toString(i)); + columnNodes.add(n); + headerNode = (ColumnNode) headerNode.hookRight(n); + } + headerNode = headerNode.R.C; + + for (boolean[] aGrid : grid) { + DancingNode prev = null; + for (int j = 0; j < COLS; j++) { + if (aGrid[j]) { + ColumnNode col = columnNodes.get(j); + DancingNode newNode = new DancingNode(col); + if (prev == null) + prev = newNode; + col.U.hookDown(newNode); + prev = prev.hookRight(newNode); + col.size++; + } + } + } + + headerNode.size = COLS; + + return headerNode; + } + + DancingLinks(boolean[][] cover) { + header = makeDLXBoard(cover); + } + + public void runSolver() { + answer = new LinkedList<>(); + search(0); + } + + private void handleSolution(List answer) { + int[][] result = parseBoard(answer); + printSolution(result); + } + + private int size = 9; + + private int[][] parseBoard(List answer) { + int[][] result = new int[size][size]; + for (DancingNode n : answer) { + DancingNode rcNode = n; + int min = Integer.parseInt(rcNode.C.name); + for (DancingNode tmp = n.R; tmp != n; tmp = tmp.R) { + int val = Integer.parseInt(tmp.C.name); + if (val < min) { + min = val; + rcNode = tmp; + } + } + int ans1 = Integer.parseInt(rcNode.C.name); + int ans2 = Integer.parseInt(rcNode.R.C.name); + int r = ans1 / size; + int c = ans1 % size; + int num = (ans2 % size) + 1; + result[r][c] = num; + } + return result; + } + + private static void printSolution(int[][] result) { + int N = result.length; + for (int[] aResult : result) { + StringBuilder ret = new StringBuilder(); + for (int j = 0; j < N; j++) { + ret.append(aResult[j]).append(" "); + } + System.out.println(ret); + } + System.out.println(); + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java new file mode 100644 index 0000000000..6b0f57a075 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingLinksAlgorithm.java @@ -0,0 +1,108 @@ +package com.baeldung.algorithms.sudoku; + +import java.util.Arrays; + +public class DancingLinksAlgorithm { + private static final int BOARD_SIZE = 9; + private static final int SUBSECTION_SIZE = 3; + private static final int NO_VALUE = 0; + private static final int CONSTRAINTS = 4; + private static final int MIN_VALUE = 1; + private static final int MAX_VALUE = 9; + private static final int COVER_START_INDEX = 1; + + private static int[][] board = { + {8, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 3, 6, 0, 0, 0, 0, 0}, + {0, 7, 0, 0, 9, 0, 2, 0, 0}, + {0, 5, 0, 0, 0, 7, 0, 0, 0}, + {0, 0, 0, 0, 4, 5, 7, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 3, 0}, + {0, 0, 1, 0, 0, 0, 0, 6, 8}, + {0, 0, 8, 5, 0, 0, 0, 1, 0}, + {0, 9, 0, 0, 0, 0, 4, 0, 0} + }; + + public static void main(String[] args) { + DancingLinksAlgorithm solver = new DancingLinksAlgorithm(); + solver.solve(board); + } + + private void solve(int[][] board) { + boolean[][] cover = initializeExactCoverBoard(board); + DancingLinks dlx = new DancingLinks(cover); + dlx.runSolver(); + } + + private int getIndex(int row, int col, int num) { + return (row - 1) * BOARD_SIZE * BOARD_SIZE + (col - 1) * BOARD_SIZE + (num - 1); + } + + private boolean[][] createExactCoverBoard() { + boolean[][] R = new boolean[BOARD_SIZE * BOARD_SIZE * MAX_VALUE][BOARD_SIZE * BOARD_SIZE * CONSTRAINTS]; + + int hBase = 0; + + // Cell constraint. + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++, hBase++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++) { + int index = getIndex(r, c, n); + R[index][hBase] = true; + } + } + } + + // Row constrain. + for (int r = COVER_START_INDEX; r <= BOARD_SIZE; r++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int c1 = COVER_START_INDEX; c1 <= BOARD_SIZE; c1++) { + int index = getIndex(r, c1, n); + R[index][hBase] = true; + } + } + } + + // Column constraint. + for (int c = COVER_START_INDEX; c <= BOARD_SIZE; c++) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int r1 = COVER_START_INDEX; r1 <= BOARD_SIZE; r1++) { + int index = getIndex(r1, c, n); + R[index][hBase] = true; + } + } + } + + // Subsection constraint + for (int br = COVER_START_INDEX; br <= BOARD_SIZE; br += SUBSECTION_SIZE) { + for (int bc = COVER_START_INDEX; bc <= BOARD_SIZE; bc += SUBSECTION_SIZE) { + for (int n = COVER_START_INDEX; n <= BOARD_SIZE; n++, hBase++) { + for (int rDelta = 0; rDelta < SUBSECTION_SIZE; rDelta++) { + for (int cDelta = 0; cDelta < SUBSECTION_SIZE; cDelta++) { + int index = getIndex(br + rDelta, bc + cDelta, n); + R[index][hBase] = true; + } + } + } + } + } + return R; + } + + private boolean[][] initializeExactCoverBoard(int[][] board) { + boolean[][] R = createExactCoverBoard(); + for (int i = COVER_START_INDEX; i <= BOARD_SIZE; i++) { + for (int j = COVER_START_INDEX; j <= BOARD_SIZE; j++) { + int n = board[i - 1][j - 1]; + if (n != NO_VALUE) { + for (int num = MIN_VALUE; num <= MAX_VALUE; num++) { + if (num != n) { + Arrays.fill(R[getIndex(i, j, num)], false); + } + } + } + } + } + return R; + } +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java new file mode 100644 index 0000000000..b494eba9ef --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/sudoku/DancingNode.java @@ -0,0 +1,50 @@ +package com.baeldung.algorithms.sudoku; + +class DancingNode { + DancingNode L, R, U, D; + ColumnNode C; + + DancingNode hookDown(DancingNode n1) { + assert (this.C == n1.C); + n1.D = this.D; + n1.D.U = n1; + n1.U = this; + this.D = n1; + return n1; + } + + DancingNode hookRight(DancingNode n1) { + n1.R = this.R; + n1.R.L = n1; + n1.L = this; + this.R = n1; + return n1; + } + + void unlinkLR() { + this.L.R = this.R; + this.R.L = this.L; + } + + void relinkLR() { + this.L.R = this.R.L = this; + } + + void unlinkUD() { + this.U.D = this.D; + this.D.U = this.U; + } + + void relinkUD() { + this.U.D = this.D.U = this; + } + + DancingNode() { + L = R = U = D = this; + } + + DancingNode(ColumnNode c) { + this(); + C = c; + } +} \ No newline at end of file diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java index ae91c6fe6c..0a7190964e 100644 --- a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java @@ -2,46 +2,48 @@ package com.baeldung.spliteratorAPI; import java.util.List; import java.util.Spliterator; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class RelatedAuthorSpliterator implements Spliterator { - private final List list; - private int current = 0; + private final List list; + AtomicInteger current = new AtomicInteger(); - public RelatedAuthorSpliterator(List list) { - this.list = list; - } + public RelatedAuthorSpliterator(List list) { + this.list = list; + } - @Override - public boolean tryAdvance(Consumer action) { - action.accept(list.get(current++)); - return current < list.size(); - } + @Override + public boolean tryAdvance(Consumer action) { - @Override - public Spliterator trySplit() { - int currentSize = list.size() - current; - if (currentSize < 10) { - return null; - } - for (int splitPos = currentSize / 2 + current; splitPos < list.size(); splitPos++) { - if (list.get(splitPos) - .getRelatedArticleId() == 0) { - Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current, splitPos)); - current = splitPos; - return spliterator; - } - } - return null; - } + action.accept(list.get(current.getAndIncrement())); + return current.get() < list.size(); + } - @Override - public long estimateSize() { - return list.size() - current; - } + @Override + public Spliterator trySplit() { + int currentSize = list.size() - current.get(); + if (currentSize < 10) { + return null; + } + for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) { + if (list.get(splitPos).getRelatedArticleId() == 0) { + Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos)); + current.set(splitPos); + return spliterator; + } + } + return null; + } + + @Override + public long estimateSize() { + return list.size() - current.get(); + } + + @Override + public int characteristics() { + return CONCURRENT; + } - @Override - public int characteristics() { - return SIZED + CONCURRENT; - } } diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java new file mode 100644 index 0000000000..5cf3b9098f --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java @@ -0,0 +1,218 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.IOException; +import java.net.*; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpClientTest { + + @Test + public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample body")) + .build(); + + HttpResponse response = HttpClient.newBuilder() + .proxy(ProxySelector.getDefault()) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample body")); + } + + @Test + public void shouldNotFollowRedirectWhenSetToDefaultNever() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM)); + assertThat(response.body(), containsString("https://stackoverflow.com/")); + } + + @Test + public void shouldFollowRedirectWhenSetToAlways() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.finalRequest() + .uri() + .toString(), equalTo("https://stackoverflow.com/")); + } + + @Test + public void shouldReturnOKStatusForAuthenticatedAccess() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/basic-auth")) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("postman", "password".toCharArray()); + } + }) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldSendRequestAsync() throws URISyntaxException, InterruptedException, ExecutionException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample body")) + .build(); + CompletableFuture> response = HttpClient.newBuilder() + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldUseJustTwoThreadWhenProcessingSendAsyncRequest() throws URISyntaxException, InterruptedException, ExecutionException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + ExecutorService executorService = Executors.newFixedThreadPool(2); + + CompletableFuture> response1 = HttpClient.newBuilder() + .executor(executorService) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture> response2 = HttpClient.newBuilder() + .executor(executorService) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture> response3 = HttpClient.newBuilder() + .executor(executorService) + .build() + .sendAsync(request, HttpResponse.BodyHandler.asString()); + + CompletableFuture.allOf(response1, response2, response3) + .join(); + + assertThat(response1.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response2.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response3.get() + .statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldNotStoreCookieWhenPolicyAcceptNone() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpClient httpClient = HttpClient.newBuilder() + .cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_NONE)) + .build(); + + httpClient.send(request, HttpResponse.BodyHandler.asString()); + + assertThat(httpClient.cookieManager() + .get() + .getCookieStore() + .getCookies(), empty()); + } + + @Test + public void shouldStoreCookieWhenPolicyAcceptAll() throws URISyntaxException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpClient httpClient = HttpClient.newBuilder() + .cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_ALL)) + .build(); + + httpClient.send(request, HttpResponse.BodyHandler.asString()); + + assertThat(httpClient.cookieManager() + .get() + .getCookieStore() + .getCookies(), not(empty())); + } + + @Test + public void shouldProcessMultipleRequestViaStream() throws URISyntaxException, ExecutionException, InterruptedException { + List targets = Arrays.asList(new URI("https://postman-echo.com/get?foo1=bar1"), new URI("https://postman-echo.com/get?foo2=bar2")); + + HttpClient client = HttpClient.newHttpClient(); + + List> futures = targets.stream() + .map(target -> client.sendAsync(HttpRequest.newBuilder(target) + .GET() + .build(), HttpResponse.BodyHandler.asString()) + .thenApply(response -> response.body())) + .collect(Collectors.toList()); + + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .join(); + + if (futures.get(0) + .get() + .contains("foo1")) { + assertThat(futures.get(0) + .get(), containsString("bar1")); + assertThat(futures.get(1) + .get(), containsString("bar2")); + } else { + assertThat(futures.get(1) + .get(), containsString("bar2")); + assertThat(futures.get(1) + .get(), containsString("bar1")); + } + + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java new file mode 100644 index 0000000000..7c0e9a90e0 --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java @@ -0,0 +1,171 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; + +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpRequestTest { + + @Test + public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldUseHttp2WhenWebsiteUsesHttp2() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://stackoverflow.com")) + .version(HttpClient.Version.HTTP_2) + .GET() + .build(); + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.version(), equalTo(HttpClient.Version.HTTP_2)); + } + + @Test + public void shouldFallbackToHttp1_1WhenWebsiteDoesNotUseHttp2() throws IOException, InterruptedException, URISyntaxException, NoSuchAlgorithmException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .version(HttpClient.Version.HTTP_2) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1)); + } + + @Test + public void shouldReturnStatusOKWhenSendGetRequestWithDummyHeaders() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .headers("key1", "value1", "key2", "value2") + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnStatusOKWhenSendGetRequestTimeoutSet() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .timeout(Duration.of(10, SECONDS)) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnNoContentWhenPostWithNoBody() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .POST(HttpRequest.noBody()) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithBodyText() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromString("Sample request body")) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithInputStream() throws IOException, InterruptedException, URISyntaxException { + byte[] sampleData = "Sample request body".getBytes(); + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromInputStream(() -> new ByteArrayInputStream(sampleData))) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithByteArrayProcessorStream() throws IOException, InterruptedException, URISyntaxException { + byte[] sampleData = "Sample request body".getBytes(); + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromByteArray(sampleData)) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample request body")); + } + + @Test + public void shouldReturnSampleDataContentWhenPostWithFileProcessorStream() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/post")) + .headers("Content-Type", "text/plain;charset=UTF-8") + .POST(HttpRequest.BodyProcessor.fromFile(Paths.get("src/test/resources/sample.txt"))) + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), containsString("Sample file content")); + } + +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java new file mode 100644 index 0000000000..80295ff34c --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java @@ -0,0 +1,55 @@ +package com.baeldung.java9.httpclient; + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.junit.Test; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertThat; + +/** + * Created by adam. + */ +public class HttpResponseTest { + + @Test + public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("https://postman-echo.com/get")) + .GET() + .build(); + + HttpResponse response = HttpClient.newHttpClient() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_OK)); + assertThat(response.body(), not(isEmptyString())); + } + + @Test + public void shouldResponseURIDifferentThanRequestUIRWhenRedirect() throws IOException, InterruptedException, URISyntaxException { + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI("http://stackoverflow.com")) + .version(HttpClient.Version.HTTP_1_1) + .GET() + .build(); + HttpResponse response = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build() + .send(request, HttpResponse.BodyHandler.asString()); + + assertThat(request.uri() + .toString(), equalTo("http://stackoverflow.com")); + assertThat(response.uri() + .toString(), equalTo("https://stackoverflow.com/")); + } + +} diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java new file mode 100644 index 0000000000..a70041ed7d --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/Job.java @@ -0,0 +1,24 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +public class Job implements Runnable { + private String jobName; + private JobPriority jobPriority; + + public Job(String jobName, JobPriority jobPriority) { + this.jobName = jobName; + this.jobPriority = jobPriority != null ? jobPriority : JobPriority.MEDIUM; + } + + public JobPriority getJobPriority() { + return jobPriority; + } + + @Override + public void run() { + try { + System.out.println("Job:" + jobName + " Priority:" + jobPriority); + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } + } +} \ No newline at end of file diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java new file mode 100644 index 0000000000..d8092a9ce7 --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/JobPriority.java @@ -0,0 +1,7 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +public enum JobPriority { + HIGH, + MEDIUM, + LOW +} diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java new file mode 100644 index 0000000000..70fd1710c0 --- /dev/null +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobScheduler.java @@ -0,0 +1,56 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +import java.util.Comparator; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class PriorityJobScheduler { + + private ExecutorService priorityJobPoolExecutor; + private ExecutorService priorityJobScheduler; + private PriorityBlockingQueue priorityQueue; + + public PriorityJobScheduler(Integer poolSize, Integer queueSize) { + priorityJobPoolExecutor = Executors.newFixedThreadPool(poolSize); + Comparator jobComparator = Comparator.comparing(Job::getJobPriority); + priorityQueue = new PriorityBlockingQueue(queueSize, + (Comparator) jobComparator); + + priorityJobScheduler = Executors.newSingleThreadExecutor(); + priorityJobScheduler.execute(()->{ + while (true) { + try { + priorityJobPoolExecutor.execute(priorityQueue.take()); + } catch (InterruptedException e) { + break; + } + } + }); + } + + public void scheduleJob(Job job) { + priorityQueue.add(job); + } + + public int getQueuedTaskCount() { + return priorityQueue.size(); + } + + protected void close(ExecutorService scheduler) { + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + } + } + + public void closeScheduler() { + close(priorityJobPoolExecutor); + close(priorityJobScheduler); + } +} diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java index 9b850c4153..9d76c1fcd1 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Data.java @@ -11,7 +11,10 @@ public class Data { while (transfer) { try { wait(); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); + } } transfer = true; @@ -23,7 +26,10 @@ public class Data { while (!transfer) { try { wait(); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); + } } transfer = false; diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java index 63f48b8031..21ba822bfd 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Receiver.java @@ -19,7 +19,10 @@ public class Receiver implements Runnable { //Thread.sleep() to mimic heavy server-side processing try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); + } } } } \ No newline at end of file diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java index b7d782c3f5..c365294cdd 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/waitandnotify/Sender.java @@ -11,11 +11,11 @@ public class Sender implements Runnable { public void run() { String packets[] = { - "First packet", - "Second packet", - "Third packet", - "Fourth packet", - "End" + "First packet", + "Second packet", + "Third packet", + "Fourth packet", + "End" }; for (String packet : packets) { @@ -24,7 +24,10 @@ public class Sender implements Runnable { //Thread.sleep() to mimic heavy server-side processing try { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000)); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); + } } } } \ No newline at end of file diff --git a/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java b/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java new file mode 100644 index 0000000000..902bada3a2 --- /dev/null +++ b/core-java-concurrency/src/test/java/com/baeldung/concurrent/prioritytaskexecution/PriorityJobSchedulerUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.concurrent.prioritytaskexecution; + +import org.junit.Test; + +public class PriorityJobSchedulerUnitTest { + private static int POOL_SIZE = 1; + private static int QUEUE_SIZE = 10; + + @Test + public void whenMultiplePriorityJobsQueued_thenHighestPriorityJobIsPicked() { + Job job1 = new Job("Job1", JobPriority.LOW); + Job job2 = new Job("Job2", JobPriority.MEDIUM); + Job job3 = new Job("Job3", JobPriority.HIGH); + Job job4 = new Job("Job4", JobPriority.MEDIUM); + Job job5 = new Job("Job5", JobPriority.LOW); + Job job6 = new Job("Job6", JobPriority.HIGH); + + PriorityJobScheduler pjs = new PriorityJobScheduler(POOL_SIZE, QUEUE_SIZE); + + pjs.scheduleJob(job1); + pjs.scheduleJob(job2); + pjs.scheduleJob(job3); + pjs.scheduleJob(job4); + pjs.scheduleJob(job5); + pjs.scheduleJob(job6); + + // ensure no tasks is pending before closing the scheduler + while (pjs.getQueuedTaskCount() != 0); + + // delay to avoid job sleep (added for demo) being interrupted + try { + Thread.sleep(2000); + } catch (InterruptedException ignored) { + } + + pjs.closeScheduler(); + } +} diff --git a/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java b/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java index 49f4313e9d..e2bc328df3 100644 --- a/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java +++ b/core-java-concurrency/src/test/java/com/baeldung/concurrent/waitandnotify/NetworkIntegrationTest.java @@ -13,38 +13,38 @@ import org.junit.Test; public class NetworkIntegrationTest { - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); - private String expected; - - @Before - public void setUpStreams() { - System.setOut(new PrintStream(outContent)); - System.setErr(new PrintStream(errContent)); - } - - @Before - public void setUpExpectedOutput() { - StringWriter expectedStringWriter = new StringWriter(); - - PrintWriter printWriter = new PrintWriter(expectedStringWriter); - printWriter.println("First packet"); - printWriter.println("Second packet"); - printWriter.println("Third packet"); - printWriter.println("Fourth packet"); - printWriter.close(); - - expected = expectedStringWriter.toString(); - } + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private String expected; + + @Before + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @Before + public void setUpExpectedOutput() { + StringWriter expectedStringWriter = new StringWriter(); + + PrintWriter printWriter = new PrintWriter(expectedStringWriter); + printWriter.println("First packet"); + printWriter.println("Second packet"); + printWriter.println("Third packet"); + printWriter.println("Fourth packet"); + printWriter.close(); + + expected = expectedStringWriter.toString(); + } - @After - public void cleanUpStreams() { - System.setOut(null); - System.setErr(null); - } - - @Test - public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() { + @After + public void cleanUpStreams() { + System.setOut(null); + System.setErr(null); + } + + @Test + public void givenSenderAndReceiver_whenSendingPackets_thenNetworkSynchronized() { Data data = new Data(); Thread sender = new Thread(new Sender(data)); Thread receiver = new Thread(new Receiver(data)); @@ -54,12 +54,13 @@ public class NetworkIntegrationTest { //wait for sender and receiver to finish before we test against expected try { - sender.join(); - receiver.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - assertEquals(expected, outContent.toString()); - } + sender.join(); + receiver.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread Interrupted"); + } + + assertEquals(expected, outContent.toString()); + } } diff --git a/core-java/README.md b/core-java/README.md index 6a4a87de0c..ed60db855c 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -126,3 +126,4 @@ - [A Guide to Inner Interfaces in Java](http://www.baeldung.com/java-inner-interfaces) - [Polymorphism in Java](http://www.baeldung.com/java-polymorphism) - [Recursion In Java](http://www.baeldung.com/java-recursion) +- [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize) diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java new file mode 100644 index 0000000000..9ca2edac38 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/Channel.java @@ -0,0 +1,5 @@ +package com.baeldung.designpatterns.observer; + +public interface Channel { + public void update(Object o); +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java new file mode 100644 index 0000000000..0330fbdcdc --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsAgency.java @@ -0,0 +1,24 @@ +package com.baeldung.designpatterns.observer; + +import java.util.ArrayList; +import java.util.List; + +public class NewsAgency { + private String news; + private List channels = new ArrayList<>(); + + public void addObserver(Channel channel) { + this.channels.add(channel); + } + + public void removeObserver(Channel channel) { + this.channels.remove(channel); + } + + public void setNews(String news) { + this.news = news; + for (Channel channel : this.channels) { + channel.update(this.news); + } + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java new file mode 100644 index 0000000000..09c22e0ad8 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/NewsChannel.java @@ -0,0 +1,20 @@ +package com.baeldung.designpatterns.observer; + +public class NewsChannel implements Channel { + + private String news; + + @Override + public void update(Object news) { + this.setNews((String) news); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } + +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java new file mode 100644 index 0000000000..2849820663 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsAgency.java @@ -0,0 +1,13 @@ +package com.baeldung.designpatterns.observer; + +import java.util.Observable; + +public class ONewsAgency extends Observable { + private String news; + + public void setNews(String news) { + this.news = news; + setChanged(); + notifyObservers(news); + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java new file mode 100644 index 0000000000..3989fe0286 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/ONewsChannel.java @@ -0,0 +1,22 @@ +package com.baeldung.designpatterns.observer; + +import java.util.Observable; +import java.util.Observer; + +public class ONewsChannel implements Observer { + + private String news; + + @Override + public void update(Observable o, Object news) { + this.setNews((String) news); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java new file mode 100644 index 0000000000..b05b97ab0b --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsAgency.java @@ -0,0 +1,28 @@ +package com.baeldung.designpatterns.observer; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class PCLNewsAgency { + private String news; + + private PropertyChangeSupport support; + + public PCLNewsAgency() { + support = new PropertyChangeSupport(this); + } + + public void addPropertyChangeListener(PropertyChangeListener pcl) { + support.addPropertyChangeListener(pcl); + } + + public void removePropertyChangeListener(PropertyChangeListener pcl) { + support.removePropertyChangeListener(pcl); + } + + public void setNews(String value) { + support.firePropertyChange("news", this.news, value); + this.news = value; + + } +} diff --git a/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java new file mode 100644 index 0000000000..ff8d35463c --- /dev/null +++ b/core-java/src/main/java/com/baeldung/designpatterns/observer/PCLNewsChannel.java @@ -0,0 +1,21 @@ +package com.baeldung.designpatterns.observer; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class PCLNewsChannel implements PropertyChangeListener { + + private String news; + + public void propertyChange(PropertyChangeEvent evt) { + this.setNews((String) evt.getNewValue()); + } + + public String getNews() { + return news; + } + + public void setNews(String news) { + this.news = news; + } +} diff --git a/core-java/src/main/java/com/baeldung/javac/Data.java b/core-java/src/main/java/com/baeldung/javac/Data.java new file mode 100644 index 0000000000..feef0c4f1e --- /dev/null +++ b/core-java/src/main/java/com/baeldung/javac/Data.java @@ -0,0 +1,16 @@ +package com.baeldung.javac; + +import java.util.ArrayList; +import java.util.List; + +public class Data { + List textList = new ArrayList(); + + public void addText(String text) { + textList.add(text); + } + + public List getTextList() { + return this.textList; + } +} \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/arguments b/core-java/src/main/java/javac-args/arguments new file mode 100644 index 0000000000..51639800a7 --- /dev/null +++ b/core-java/src/main/java/javac-args/arguments @@ -0,0 +1,2 @@ +-d javac-target -verbose +com/baeldung/javac/Data.java \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/options b/core-java/src/main/java/javac-args/options new file mode 100644 index 0000000000..f02f2344ff --- /dev/null +++ b/core-java/src/main/java/javac-args/options @@ -0,0 +1,2 @@ +-d javac-target +-verbose \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/types b/core-java/src/main/java/javac-args/types new file mode 100644 index 0000000000..ef2d861f84 --- /dev/null +++ b/core-java/src/main/java/javac-args/types @@ -0,0 +1 @@ +com/baeldung/javac/Data.java \ No newline at end of file diff --git a/core-java/src/main/java/javac-args/xlint-ops b/core-java/src/main/java/javac-args/xlint-ops new file mode 100644 index 0000000000..cdccbc0cce --- /dev/null +++ b/core-java/src/main/java/javac-args/xlint-ops @@ -0,0 +1,3 @@ +-d javac-target +-Xlint:rawtypes,unchecked,static,cast,serial,fallthrough +com/baeldung/javac/Data.java \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java b/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java new file mode 100644 index 0000000000..a8a0e29990 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/designpatterns/observer/ObserverIntegrationTest.java @@ -0,0 +1,47 @@ +package com.baeldung.designpatterns.observer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.baeldung.designpatterns.observer.NewsAgency; +import com.baeldung.designpatterns.observer.NewsChannel; + +public class ObserverIntegrationTest { + + @Test + public void whenChangingNewsAgencyState_thenNewsChannelNotified() { + + NewsAgency observable = new NewsAgency(); + NewsChannel observer = new NewsChannel(); + + observable.addObserver(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } + + @Test + public void whenChangingONewsAgencyState_thenONewsChannelNotified() { + + ONewsAgency observable = new ONewsAgency(); + ONewsChannel observer = new ONewsChannel(); + + observable.addObserver(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } + + @Test + public void whenChangingPCLNewsAgencyState_thenONewsChannelNotified() { + + PCLNewsAgency observable = new PCLNewsAgency(); + PCLNewsChannel observer = new PCLNewsChannel(); + + observable.addPropertyChangeListener(observer); + + observable.setNews("news"); + assertEquals(observer.getNews(), "news"); + } +} diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java b/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java new file mode 100644 index 0000000000..63b3fbd438 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/CostlySupplier.java @@ -0,0 +1,17 @@ +package org.baeldung.guava.memoizer; + +import java.math.BigInteger; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class CostlySupplier { + + public static BigInteger generateBigNumber() { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + } + return new BigInteger("12345"); + } + +} diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java b/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java new file mode 100644 index 0000000000..74fcbdcc14 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/Factorial.java @@ -0,0 +1,22 @@ +package org.baeldung.guava.memoizer; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.math.BigInteger; + +public class Factorial { + + private static LoadingCache memo = CacheBuilder.newBuilder() + .build(CacheLoader.from(Factorial::getFactorial)); + + public static BigInteger getFactorial(int n) { + if (n == 0) { + return BigInteger.ONE; + } else { + return BigInteger.valueOf(n).multiply(memo.getUnchecked(n - 1)); + } + } + +} diff --git a/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java b/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java new file mode 100644 index 0000000000..0c70f08c23 --- /dev/null +++ b/guava/src/main/java/org/baeldung/guava/memoizer/FibonacciSequence.java @@ -0,0 +1,26 @@ +package org.baeldung.guava.memoizer; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +public class FibonacciSequence { + + private static LoadingCache memo = CacheBuilder.newBuilder() + .maximumSize(100) + .build(CacheLoader.from(FibonacciSequence::getFibonacciNumber)); + + public static BigInteger getFibonacciNumber(int n) { + if (n == 0) { + return BigInteger.ZERO; + } else if (n == 1) { + return BigInteger.ONE; + } else { + return memo.getUnchecked(n - 1).add(memo.getUnchecked(n - 2)); + } + } + +} diff --git a/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java new file mode 100644 index 0000000000..1f934347b4 --- /dev/null +++ b/guava/src/test/java/org/baeldung/guava/GuavaMemoizerUnitTest.java @@ -0,0 +1,92 @@ +package org.baeldung.guava; + +import com.google.common.base.Suppliers; +import org.baeldung.guava.memoizer.CostlySupplier; +import org.baeldung.guava.memoizer.Factorial; +import org.baeldung.guava.memoizer.FibonacciSequence; +import org.junit.Test; + +import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.number.IsCloseTo.closeTo; +import static org.junit.Assert.assertThat; + +public class GuavaMemoizerUnitTest { + + @Test + public void givenInteger_whenGetFibonacciNumber_thenShouldCalculateFibonacciNumber() { + // given + int n = 95; + + // when + BigInteger fibonacciNumber = FibonacciSequence.getFibonacciNumber(n); + + // then + BigInteger expectedFibonacciNumber = new BigInteger("31940434634990099905"); + assertThat(fibonacciNumber, is(equalTo(expectedFibonacciNumber))); + } + + @Test + public void givenInteger_whenGetFactorial_thenShouldCalculateFactorial() { + // given + int n = 95; + + // when + BigInteger factorial = Factorial.getFactorial(n); + + // then + BigInteger expectedFactorial = new BigInteger("10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000"); + assertThat(factorial, is(equalTo(expectedFactorial))); + } + + @Test + public void givenMemoizedSupplier_whenGet_thenSubsequentGetsAreFast() { + // given + Supplier memoizedSupplier; + memoizedSupplier = Suppliers.memoize(CostlySupplier::generateBigNumber); + + // when + BigInteger expectedValue = new BigInteger("12345"); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); + + // then + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); + } + + @Test + public void givenMemoizedSupplierWithExpiration_whenGet_thenSubsequentGetsBeforeExpiredAreFast() throws InterruptedException { + // given + Supplier memoizedSupplier; + memoizedSupplier = Suppliers.memoizeWithExpiration(CostlySupplier::generateBigNumber, 3, TimeUnit.SECONDS); + + // when + BigInteger expectedValue = new BigInteger("12345"); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); + + // then + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 0D); + // add one more second until memoized Supplier is evicted from memory + TimeUnit.SECONDS.sleep(1); + assertSupplierGetExecutionResultAndDuration(memoizedSupplier, expectedValue, 2000D); + } + + private void assertSupplierGetExecutionResultAndDuration(Supplier supplier, + T expectedValue, + double expectedDurationInMs) { + Instant start = Instant.now(); + T value = supplier.get(); + Long durationInMs = Duration.between(start, Instant.now()).toMillis(); + double marginOfErrorInMs = 100D; + + assertThat(value, is(equalTo(expectedValue))); + assertThat(durationInMs.doubleValue(), is(closeTo(expectedDurationInMs, marginOfErrorInMs))); + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java index 25fc0d7b02..130edec8cc 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java @@ -81,6 +81,7 @@ public class HibernateUtil { metadataSources.addAnnotatedClass(Bag.class); metadataSources.addAnnotatedClass(PointEntity.class); metadataSources.addAnnotatedClass(PolygonEntity.class); + metadataSources.addAnnotatedClass(com.baeldung.hibernate.pojo.Person.class); Metadata metadata = metadataSources.buildMetadata(); return metadata.getSessionFactoryBuilder() diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java b/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java new file mode 100644 index 0000000000..c8b3397b09 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/converters/PersonNameConverter.java @@ -0,0 +1,43 @@ +package com.baeldung.hibernate.converters; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import com.baeldung.hibernate.pojo.PersonName; + +@Converter +public class PersonNameConverter implements AttributeConverter { + + private static final String SEPARATOR = ", "; + + @Override + public String convertToDatabaseColumn(PersonName person) { + StringBuilder sb = new StringBuilder(); + if (person.getSurname() != null) { + sb.append(person.getSurname()); + sb.append(SEPARATOR); + } + + if (person.getName() != null) { + sb.append(person.getName()); + } + + return sb.toString(); + } + + @Override + public PersonName convertToEntityAttribute(String dbPerson) { + String[] pieces = dbPerson.split(SEPARATOR); + + if (pieces == null || pieces.length != 2) { + return null; + } + + PersonName personName = new PersonName(); + personName.setSurname(pieces[0]); + personName.setName(pieces[1]); + + return personName; + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java index a84a981f7f..6736b39b64 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/interceptors/CustomInterceptorImpl.java @@ -9,7 +9,7 @@ import org.hibernate.Interceptor; import org.hibernate.Transaction; import org.hibernate.type.Type; -public class CustomInterceptorImpl implements Interceptor { +public class CustomInterceptorImpl implements Interceptor, Serializable { @Override public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException { diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java new file mode 100644 index 0000000000..390a5954ed --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/Person.java @@ -0,0 +1,32 @@ +package com.baeldung.hibernate.pojo; + +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import com.baeldung.hibernate.converters.PersonNameConverter; + +@Entity(name = "PersonTable") +public class Person { + + @Id + @GeneratedValue + private Long id; + + @Convert(converter = PersonNameConverter.class) + private PersonName personName; + + public PersonName getPersonName() { + return personName; + } + + public void setPersonName(PersonName personName) { + this.personName = personName; + } + + public Long getId() { + return id; + } + +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java new file mode 100644 index 0000000000..335fe73f75 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pojo/PersonName.java @@ -0,0 +1,29 @@ +package com.baeldung.hibernate.pojo; + +import java.io.Serializable; + +public class PersonName implements Serializable { + + private static final long serialVersionUID = 7883094644631050150L; + + private String name; + + private String surname; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + +} diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java new file mode 100644 index 0000000000..aec2311294 --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/converter/PersonNameConverterTest.java @@ -0,0 +1,74 @@ +package com.baeldung.hibernate.converter; + +import java.io.IOException; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.hibernate.HibernateUtil; +import com.baeldung.hibernate.pojo.Person; +import com.baeldung.hibernate.pojo.PersonName; + +import static org.junit.Assert.assertEquals; + +public class PersonNameConverterTest { + + private Session session; + private Transaction transaction; + + @Before + public void setUp() throws IOException { + session = HibernateUtil.getSessionFactory() + .openSession(); + transaction = session.beginTransaction(); + + session.createNativeQuery("delete from personTable") + .executeUpdate(); + + transaction.commit(); + transaction = session.beginTransaction(); + } + + @After + public void tearDown() { + transaction.rollback(); + session.close(); + } + + @Test + public void givenPersonName_WhenSaving_ThenNameAndSurnameConcat() { + final String name = "name"; + final String surname = "surname"; + + PersonName personName = new PersonName(); + personName.setName(name); + personName.setSurname(surname); + + Person person = new Person(); + person.setPersonName(personName); + + Long id = (Long) session.save(person); + + session.flush(); + session.clear(); + + String dbPersonName = (String) session.createNativeQuery("select p.personName from PersonTable p where p.id = :id") + .setParameter("id", id) + .getSingleResult(); + + assertEquals(surname + ", " + name, dbPersonName); + + Person dbPerson = session.createNativeQuery("select * from PersonTable p where p.id = :id", Person.class) + .setParameter("id", id) + .getSingleResult(); + + assertEquals(dbPerson.getPersonName() + .getName(), name); + assertEquals(dbPerson.getPersonName() + .getSurname(), surname); + } + +} diff --git a/influxdb/README.md b/influxdb/README.md index 7d1684688d..f2c421580e 100644 --- a/influxdb/README.md +++ b/influxdb/README.md @@ -2,7 +2,6 @@ ### Relevant Article: - [Introduction to using InfluxDB with Java](http://www.baeldung.com/using-influxdb-with-java/) -- [Using InfluxDB with Java](http://www.baeldung.com/java-influxdb) ### Overview This Maven project contains the Java code for the article linked above. diff --git a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java index 6c7a03cb70..858903a676 100644 --- a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java +++ b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java @@ -8,7 +8,6 @@ import org.influxdb.dto.*; import org.influxdb.impl.InfluxDBResultMapper; import org.junit.Test; -import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -103,7 +102,7 @@ public class InfluxDBConnectionLiveTest { // another brief pause. Thread.sleep(10); - List memoryPointList = getPoints(connection, "Select * from memory", "baeldung"); + List memoryPointList = getPoints(connection, "Select * from memory", "baeldung"); assertEquals(10, memoryPointList.size()); diff --git a/java-rmi/pom.xml b/java-rmi/pom.xml new file mode 100644 index 0000000000..7c08968cbf --- /dev/null +++ b/java-rmi/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + com.baeldung.rmi + java-rmi + 1.0-SNAPSHOT + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + UTF-8 + + + diff --git a/java-rmi/src/main/java/com/baeldung/rmi/Message.java b/java-rmi/src/main/java/com/baeldung/rmi/Message.java new file mode 100644 index 0000000000..6d21a45fe3 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/Message.java @@ -0,0 +1,36 @@ +package com.baeldung.rmi; + +import java.io.Serializable; + +public class Message implements Serializable { + + private String messageText; + + private String contentType; + + public Message() { + } + + public Message(String messageText, String contentType) { + + this.messageText = messageText; + this.contentType = contentType; + } + + public String getMessageText() { + return messageText; + } + + public void setMessageText(String messageText) { + this.messageText = messageText; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + +} diff --git a/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java b/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java new file mode 100644 index 0000000000..f962e4ad07 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/MessengerService.java @@ -0,0 +1,11 @@ +package com.baeldung.rmi; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface MessengerService extends Remote { + + public String sendMessage(String clientMessage) throws RemoteException; + + public Message sendMessage(Message clientMessage) throws RemoteException; +} \ No newline at end of file diff --git a/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java b/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java new file mode 100644 index 0000000000..ebf03d4b67 --- /dev/null +++ b/java-rmi/src/main/java/com/baeldung/rmi/MessengerServiceImpl.java @@ -0,0 +1,37 @@ +package com.baeldung.rmi; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; + +public class MessengerServiceImpl implements MessengerService { + + public String sendMessage(String clientMessage) { + + String serverMessage = null; + if (clientMessage.equals("Client Message")) { + serverMessage = "Server Message"; + } + + return serverMessage; + } + + public void createStubAndBind() throws RemoteException { + + MessengerService stub = (MessengerService) UnicastRemoteObject.exportObject((MessengerService) this, 0); + Registry registry = LocateRegistry.createRegistry(1099); + registry.rebind("MessengerService", stub); + } + + public Message sendMessage(Message clientMessage) throws RemoteException { + + Message serverMessage = null; + if (clientMessage.getMessageText().equals("Client Message")) { + serverMessage = new Message("Server Message", "text/plain"); + } + + return serverMessage; + } + +} \ No newline at end of file diff --git a/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java b/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java new file mode 100644 index 0000000000..66bfbe49eb --- /dev/null +++ b/java-rmi/src/test/java/com/baeldung/rmi/JavaRMIIntegrationTest.java @@ -0,0 +1,44 @@ +package com.baeldung.rmi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +import org.junit.BeforeClass; +import org.junit.Test; + +public class JavaRMIIntegrationTest { + + @BeforeClass + public static void whenRunServer_thenServerStarts() { + + try { + MessengerServiceImpl server = new MessengerServiceImpl(); + server.createStubAndBind(); + } catch (RemoteException e) { + fail("Exception Occurred"); + } + } + + @Test + public void whenClientSendsMessageToServer_thenServerSendsResponseMessage() { + + try { + Registry registry = LocateRegistry.getRegistry(); + MessengerService server = (MessengerService) registry.lookup("MessengerService"); + String responseMessage = server.sendMessage("Client Message"); + + String expectedMessage = "Server Message"; + assertEquals(responseMessage, expectedMessage); + } catch (RemoteException e) { + fail("Exception Occurred"); + } catch (NotBoundException nb) { + fail("Exception Occurred"); + } + } + +} \ No newline at end of file diff --git a/jgroups/README.md b/jgroups/README.md new file mode 100644 index 0000000000..bb2813c3d6 --- /dev/null +++ b/jgroups/README.md @@ -0,0 +1,15 @@ +## Reliable Messaging with JGroups Tutorial Project + +### Relevant Article: +- [Reliable Messaging with JGroups](http://www.baeldung.com/reliable-messaging-with-jgroups/) + +### Overview +This Maven project contains the Java code for the article linked above. + +### Package Organization +Java classes for the intro tutorial are in the org.baeldung.jgroups package. + + +### Running the tests + +``` diff --git a/jgroups/pom.xml b/jgroups/pom.xml new file mode 100644 index 0000000000..0e5971875e --- /dev/null +++ b/jgroups/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + jgroups + 0.1-SNAPSHOT + jar + jgroups + Reliable Messaging with JGroups Tutorial + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.jgroups + jgroups + 4.0.10.Final + + + + commons-cli + commons-cli + 1.4 + + + + + 1.8 + UTF-8 + + + diff --git a/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java new file mode 100644 index 0000000000..2a8df8a9ab --- /dev/null +++ b/jgroups/src/main/java/com/baeldung/jgroups/JGroupsMessenger.java @@ -0,0 +1,222 @@ +package com.baeldung.jgroups; + +import org.apache.commons.cli.*; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.List; +import java.util.Optional; + +public class JGroupsMessenger extends ReceiverAdapter { + + private JChannel channel; + private String userName; + private String clusterName; + private View lastView; + private boolean running = true; + + // Our shared state + private Integer messageCount = 0; + + /** + * Connect to a JGroups cluster using command line options + * @param args command line arguments + * @throws Exception + */ + private void start(String[] args) throws Exception { + processCommandline(args); + + // Create the channel + // This file could be moved, or made a command line option. + channel = new JChannel("src/main/resources/udp.xml"); + + // Set a name + channel.name(userName); + + // Register for callbacks + channel.setReceiver(this); + + // Ignore our messages + channel.setDiscardOwnMessages(true); + + // Connect + channel.connect(clusterName); + + // Start state transfer + channel.getState(null, 0); + + // Do the things + processInput(); + + // Clean up + channel.close(); + + } + + /** + * Quick and dirty implementaton of commons cli for command line args + * @param args the command line args + * @throws ParseException + */ + private void processCommandline(String[] args) throws ParseException { + + // Options, parser, friendly help + Options options = new Options(); + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + + options.addOption("u", "user", true, "User name") + .addOption("c", "cluster", true, "Cluster name"); + + CommandLine line = parser.parse(options, args); + + if (line.hasOption("user")) { + userName = line.getOptionValue("user"); + } else { + formatter.printHelp("JGroupsMessenger: need a user name.\n", options); + System.exit(-1); + } + + if (line.hasOption("cluster")) { + clusterName = line.getOptionValue("cluster"); + } else { + formatter.printHelp("JGroupsMessenger: need a cluster name.\n", options); + System.exit(-1); + } + } + + // Start it up + public static void main(String[] args) throws Exception { + new JGroupsMessenger().start(args); + } + + + @Override + public void viewAccepted(View newView) { + + // Save view if this is the first + if (lastView == null) { + System.out.println("Received initial view:"); + newView.forEach(System.out::println); + } else { + + // Compare to last view + System.out.println("Received new view."); + + List
newMembers = View.newMembers(lastView, newView); + if (newMembers.size() > 0) { + System.out.println("New members: "); + newMembers.forEach(System.out::println); + } + + List
exMembers = View.leftMembers(lastView, newView); + if (exMembers.size() > 0) { + System.out.println("Exited members:"); + exMembers.forEach(System.out::println); + } + } + lastView = newView; + } + + /** + * Loop on console input until we see 'x' to exit + */ + private void processInput() { + + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + while (running) { + try { + + // Get a destination, means broadcast + Address destination = null; + System.out.print("Enter a destination: "); + System.out.flush(); + String destinationName = in.readLine().toLowerCase(); + + if (destinationName.equals("x")) { + running = false; + continue; + } else if (!destinationName.isEmpty()) { + Optional
optDestination = getAddress(destinationName); + if (optDestination.isPresent()) { + destination = optDestination.get(); + } else { + System.out.println("Destination not found, try again."); + continue; + } + } + + // Accept a string to send + System.out.print("Enter a message: "); + System.out.flush(); + String line = in.readLine().toLowerCase(); + sendMessage(destination, line); + } catch (IOException ioe) { + running = false; + } + } + System.out.println("Exiting."); + } + + /** + * Send message from here + * @param destination the destination + * @param messageString the message + */ + private void sendMessage(Address destination, String messageString) { + try { + System.out.println("Sending " + messageString + " to " + destination); + Message message = new Message(destination, messageString); + channel.send(message); + } catch (Exception exception) { + System.err.println("Exception sending message: " + exception.getMessage()); + running = false; + } + } + + @Override + public void receive(Message message) { + // Print source and dest with message + String line = "Message received from: " + message.getSrc() + " to: " + message.getDest() + " -> " + message.getObject(); + + // Only track the count of broadcast messages + // Tracking direct message would make for a pointless state + if (message.getDest() == null) { + messageCount++; + System.out.println("Message count: " + messageCount); + } + + System.out.println(line); + } + + @Override + public void getState(OutputStream output) throws Exception { + // Serialize into the stream + Util.objectToStream(messageCount, new DataOutputStream(output)); + } + + @Override + public void setState(InputStream input) { + + // NOTE: since we know that incrementing the count and transferring the state + // is done inside the JChannel's thread, we don't have to worry about synchronizing + // messageCount. For production code it should be synchronized! + try { + // Deserialize + messageCount = Util.objectFromStream(new DataInputStream(input)); + } catch (Exception e) { + System.out.println("Error deserialing state!"); + } + System.out.println(messageCount + " is the current messagecount."); + } + + + private Optional
getAddress(String name) { + View view = channel.view(); + return view.getMembers().stream().filter(address -> name.equals(address.toString())).findFirst(); + } + +} + + diff --git a/jgroups/src/main/resources/udp.xml b/jgroups/src/main/resources/udp.xml new file mode 100644 index 0000000000..5a9bfd0851 --- /dev/null +++ b/jgroups/src/main/resources/udp.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + diff --git a/json/pom.xml b/json/pom.xml index 958dd32f53..7c74d425af 100644 --- a/json/pom.xml +++ b/json/pom.xml @@ -31,6 +31,11 @@ ${fastjson.version} + + org.json + json + 20171018 + diff --git a/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java b/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java new file mode 100644 index 0000000000..f5fee0c4a9 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/CDLDemo.java @@ -0,0 +1,59 @@ +package com.baeldung.jsonjava; + +import org.json.CDL; +import org.json.JSONArray; +import org.json.JSONTokener; + +public class CDLDemo { + public static void main(String[] args) { + System.out.println("7.1. Producing JSONArray Directly from Comma Delimited Text: "); + jsonArrayFromCDT(); + + System.out.println("\n7.2. Producing Comma Delimited Text from JSONArray: "); + cDTfromJSONArray(); + + System.out.println("\n7.3.1. Producing JSONArray of JSONObjects Using Comma Delimited Text: "); + jaOfJOFromCDT2(); + + System.out.println("\n7.3.2. Producing JSONArray of JSONObjects Using Comma Delimited Text: "); + jaOfJOFromCDT2(); + } + + public static void jsonArrayFromCDT() { + JSONArray ja = CDL.rowToJSONArray(new JSONTokener("England, USA, Canada")); + System.out.println(ja); + } + + public static void cDTfromJSONArray() { + JSONArray ja = new JSONArray("[\"England\",\"USA\",\"Canada\"]"); + String cdt = CDL.rowToString(ja); + System.out.println(cdt); + } + + public static void jaOfJOFromCDT() { + String string = + "name, city, age \n" + + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(string); + System.out.println(result.toString()); + } + + public static void jaOfJOFromCDT2() { + JSONArray ja = new JSONArray(); + ja.put("name"); + ja.put("city"); + ja.put("age"); + + String string = + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(ja, string); + System.out.println(result.toString()); + } + +} diff --git a/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java b/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java new file mode 100644 index 0000000000..bc39d13642 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/CookieDemo.java @@ -0,0 +1,30 @@ +package com.baeldung.jsonjava; + +import org.json.Cookie; +import org.json.JSONObject; + +public class CookieDemo { + public static void main(String[] args) { + System.out.println("8.1. Converting a Cookie String into a JSONObject"); + cookieStringToJSONObject(); + + System.out.println("\n8.2. Converting a JSONObject into Cookie String"); + jSONObjectToCookieString(); + } + + public static void cookieStringToJSONObject() { + String cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"; + JSONObject cookieJO = Cookie.toJSONObject(cookie); + System.out.println(cookieJO); + } + + public static void jSONObjectToCookieString() { + JSONObject cookieJO = new JSONObject(); + cookieJO.put("name", "username"); + cookieJO.put("value", "John Doe"); + cookieJO.put("expires", "Thu, 18 Dec 2013 12:00:00 UTC"); + cookieJO.put("path", "/"); + String cookie = Cookie.toString(cookieJO); + System.out.println(cookie); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/DemoBean.java b/json/src/main/java/com/baeldung/jsonjava/DemoBean.java new file mode 100644 index 0000000000..6d27b329c2 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/DemoBean.java @@ -0,0 +1,26 @@ +package com.baeldung.jsonjava; + +public class DemoBean { + private int id; + private String name; + private boolean active; + + public int getId() { + return id; + } + public void setId(int id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public boolean isActive() { + return active; + } + public void setActive(boolean active) { + this.active = active; + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java b/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java new file mode 100644 index 0000000000..800018005c --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/HTTPDemo.java @@ -0,0 +1,27 @@ +package com.baeldung.jsonjava; + +import org.json.HTTP; +import org.json.JSONObject; + +public class HTTPDemo { + public static void main(String[] args) { + System.out.println("9.1. Converting JSONObject to HTTP Header: "); + jSONObjectToHTTPHeader(); + + System.out.println("\n9.2. Converting HTTP Header String Back to JSONObject: "); + hTTPHeaderToJSONObject(); + } + + public static void jSONObjectToHTTPHeader() { + JSONObject jo = new JSONObject(); + jo.put("Method", "POST"); + jo.put("Request-URI", "http://www.example.com/"); + jo.put("HTTP-Version", "HTTP/1.1"); + System.out.println(HTTP.toString(jo)); + } + + public static void hTTPHeaderToJSONObject() { + JSONObject obj = HTTP.toJSONObject("POST \"http://www.example.com/\" HTTP/1.1"); + System.out.println(obj); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java new file mode 100644 index 0000000000..2a4fab2eab --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONArrayDemo.java @@ -0,0 +1,52 @@ +package com.baeldung.jsonjava; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class JSONArrayDemo { + public static void main(String[] args) { + System.out.println("5.1. Creating JSON Array: "); + creatingJSONArray(); + + System.out.println("\n5.2. Creating JSON Array from JSON string: "); + jsonArrayFromJSONString(); + + System.out.println("\n5.3. Creating JSON Array from Collection Object: "); + jsonArrayFromCollectionObj(); + } + + public static void creatingJSONArray() { + JSONArray ja = new JSONArray(); + ja.put(Boolean.TRUE); + ja.put("lorem ipsum"); + + // We can also put a JSONObject in JSONArray + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + ja.put(jo); + + System.out.println(ja.toString()); + } + + public static void jsonArrayFromJSONString() { + JSONArray ja = new JSONArray("[true, \"lorem ipsum\", 215]"); + System.out.println(ja); + } + + public static void jsonArrayFromCollectionObj() { + List list = new ArrayList<>(); + list.add("California"); + list.add("Texas"); + list.add("Hawaii"); + list.add("Alaska"); + + JSONArray ja = new JSONArray(list); + System.out.println(ja); + } +} \ No newline at end of file diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java new file mode 100644 index 0000000000..cfe8467c30 --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONObjectDemo.java @@ -0,0 +1,59 @@ +package com.baeldung.jsonjava; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; + +public class JSONObjectDemo { + public static void main(String[] args) { + System.out.println("4.1. Creating JSONObject: "); + jsonFromJSONObject(); + + System.out.println("\n4.2. Creating JSONObject from Map: "); + jsonFromMap(); + + System.out.println("\n4.3. Creating JSONObject from JSON string: "); + jsonFromJSONString(); + + System.out.println("\n4.4. Creating JSONObject from Java Bean: "); + jsonFromDemoBean(); + } + + public static void jsonFromJSONObject() { + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + System.out.println(jo.toString()); + } + + public static void jsonFromMap() { + Map map = new HashMap<>(); + map.put("name", "jon doe"); + map.put("age", "22"); + map.put("city", "chicago"); + JSONObject jo = new JSONObject(map); + + System.out.println(jo.toString()); + } + + public static void jsonFromJSONString() { + JSONObject jo = new JSONObject( + "{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}" + ); + + System.out.println(jo.toString()); + } + + public static void jsonFromDemoBean() { + DemoBean demo = new DemoBean(); + demo.setId(1); + demo.setName("lorem ipsum"); + demo.setActive(true); + + JSONObject jo = new JSONObject(demo); + System.out.println(jo); + } +} diff --git a/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java b/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java new file mode 100644 index 0000000000..fb9fea959c --- /dev/null +++ b/json/src/main/java/com/baeldung/jsonjava/JSONTokenerDemo.java @@ -0,0 +1,13 @@ +package com.baeldung.jsonjava; + +import org.json.JSONTokener; + +public class JSONTokenerDemo { + public static void main(String[] args) { + JSONTokener jt = new JSONTokener("Sample String"); + + while(jt.more()) { + System.out.println(jt.next()); + } + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java new file mode 100644 index 0000000000..441c71e78e --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/CDLIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.CDL; +import org.json.JSONArray; +import org.json.JSONTokener; +import org.junit.Test; + +public class CDLIntegrationTest { + @Test + public void givenCommaDelimitedText_thenConvertToJSONArray() { + JSONArray ja = CDL.rowToJSONArray(new JSONTokener("England, USA, Canada")); + assertEquals("[\"England\",\"USA\",\"Canada\"]", ja.toString()); + } + + @Test + public void givenJSONArray_thenConvertToCommaDelimitedText() { + JSONArray ja = new JSONArray("[\"England\",\"USA\",\"Canada\"]"); + String cdt = CDL.rowToString(ja); + assertEquals("England,USA,Canada", cdt.toString().trim()); + } + + @Test + public void givenCommaDelimitedText_thenGetJSONArrayOfJSONObjects() { + String string = + "name, city, age \n" + + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(string); + assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + } + + @Test + public void givenCommaDelimitedText_thenGetJSONArrayOfJSONObjects2() { + JSONArray ja = new JSONArray(); + ja.put("name"); + ja.put("city"); + ja.put("age"); + + String string = + "john, chicago, 22 \n" + + "gary, florida, 35 \n" + + "sal, vegas, 18"; + + JSONArray result = CDL.toJSONArray(ja, string); + assertEquals("[{\"name\":\"john\",\"city\":\"chicago\",\"age\":\"22\"},{\"name\":\"gary\",\"city\":\"florida\",\"age\":\"35\"},{\"name\":\"sal\",\"city\":\"vegas\",\"age\":\"18\"}]", result.toString()); + } + +} diff --git a/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java new file mode 100644 index 0000000000..c1a3505bbc --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/CookieIntegrationTest.java @@ -0,0 +1,29 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.Cookie; +import org.json.JSONObject; +import org.junit.Test; + +public class CookieIntegrationTest { + @Test + public void givenCookieString_thenConvertToJSONObject() { + String cookie = "username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"; + JSONObject cookieJO = Cookie.toJSONObject(cookie); + + assertEquals("{\"path\":\"/\",\"expires\":\"Thu, 18 Dec 2013 12:00:00 UTC\",\"name\":\"username\",\"value\":\"John Doe\"}", cookieJO.toString()); + } + + @Test + public void givenJSONObject_thenConvertToCookieString() { + JSONObject cookieJO = new JSONObject(); + cookieJO.put("name", "username"); + cookieJO.put("value", "John Doe"); + cookieJO.put("expires", "Thu, 18 Dec 2013 12:00:00 UTC"); + cookieJO.put("path", "/"); + String cookie = Cookie.toString(cookieJO); + + assertEquals("username=John Doe;expires=Thu, 18 Dec 2013 12:00:00 UTC;path=/", cookie.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java new file mode 100644 index 0000000000..1aa0427c7e --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/HTTPIntegrationTest.java @@ -0,0 +1,25 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; +import org.json.HTTP; +import org.json.JSONObject; +import org.junit.Test; + +public class HTTPIntegrationTest { + @Test + public void givenJSONObject_thenConvertToHTTPHeader() { + JSONObject jo = new JSONObject(); + jo.put("Method", "POST"); + jo.put("Request-URI", "http://www.example.com/"); + jo.put("HTTP-Version", "HTTP/1.1"); + + assertEquals("POST \"http://www.example.com/\" HTTP/1.1"+HTTP.CRLF+HTTP.CRLF, HTTP.toString(jo)); + } + + @Test + public void givenHTTPHeader_thenConvertToJSONObject() { + JSONObject obj = HTTP.toJSONObject("POST \"http://www.example.com/\" HTTP/1.1"); + + assertEquals("{\"Request-URI\":\"http://www.example.com/\",\"Method\":\"POST\",\"HTTP-Version\":\"HTTP/1.1\"}", obj.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java new file mode 100644 index 0000000000..c956232abe --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONArrayIntegrationTest.java @@ -0,0 +1,47 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; + +public class JSONArrayIntegrationTest { + @Test + public void givenJSONJava_thenCreateNewJSONArrayFromScratch() { + JSONArray ja = new JSONArray(); + ja.put(Boolean.TRUE); + ja.put("lorem ipsum"); + + // We can also put a JSONObject in JSONArray + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + ja.put(jo); + + assertEquals("[true,\"lorem ipsum\",{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}]", ja.toString()); + } + + @Test + public void givenJsonString_thenCreateNewJSONArray() { + JSONArray ja = new JSONArray("[true, \"lorem ipsum\", 215]"); + assertEquals("[true,\"lorem ipsum\",215]", ja.toString()); + } + + @Test + public void givenListObject_thenConvertItToJSONArray() { + List list = new ArrayList<>(); + list.add("California"); + list.add("Texas"); + list.add("Hawaii"); + list.add("Alaska"); + + JSONArray ja = new JSONArray(list); + assertEquals("[\"California\",\"Texas\",\"Hawaii\",\"Alaska\"]", ja.toString()); + } +} \ No newline at end of file diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java new file mode 100644 index 0000000000..70f7921797 --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONObjectIntegrationTest.java @@ -0,0 +1,53 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; +import org.junit.Test; + +public class JSONObjectIntegrationTest { + @Test + public void givenJSONJava_thenCreateNewJSONObject() { + JSONObject jo = new JSONObject(); + jo.put("name", "jon doe"); + jo.put("age", "22"); + jo.put("city", "chicago"); + + assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); + + } + + @Test + public void givenMapObject_thenCreateJSONObject() { + Map map = new HashMap<>(); + map.put("name", "jon doe"); + map.put("age", "22"); + map.put("city", "chicago"); + JSONObject jo = new JSONObject(map); + + assertEquals("{\"name\":\"jon doe\",\"city\":\"chicago\",\"age\":\"22\"}", jo.toString()); + } + + @Test + public void givenJsonString_thenCreateJSONObject() { + JSONObject jo = new JSONObject( + "{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}" + ); + + assertEquals("{\"city\":\"chicago\",\"name\":\"jon doe\",\"age\":\"22\"}", jo.toString()); + } + + @Test + public void givenDemoBean_thenCreateJSONObject() { + DemoBean demo = new DemoBean(); + demo.setId(1); + demo.setName("lorem ipsum"); + demo.setActive(true); + + JSONObject jo = new JSONObject(demo); + assertEquals("{\"name\":\"lorem ipsum\",\"active\":true,\"id\":1}", jo.toString()); + } +} diff --git a/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java new file mode 100644 index 0000000000..4fe8f27231 --- /dev/null +++ b/json/src/test/java/com/baeldung/jsonjava/JSONTokenerIntegrationTest.java @@ -0,0 +1,21 @@ +package com.baeldung.jsonjava; + +import static org.junit.Assert.assertEquals; + +import org.json.JSONTokener; +import org.junit.Test; + +public class JSONTokenerIntegrationTest { + @Test + public void givenString_convertItToJSONTokens() { + String str = "Sample String"; + JSONTokener jt = new JSONTokener(str); + + char[] expectedTokens = str.toCharArray(); + int index = 0; + + while(jt.more()) { + assertEquals(expectedTokens[index++], jt.next()); + } + } +} diff --git a/libraries/README.md b/libraries/README.md index d001f13698..fbf2b4e876 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -59,6 +59,7 @@ - [Intro to JDO Queries 2/2](http://www.baeldung.com/jdo-queries) - [Guide to google-http-client](http://www.baeldung.com/google-http-client) - [Interact with Google Sheets from Java](http://www.baeldung.com/google-sheets-java-client) +- [Programatically Create, Configure, and Run a Tomcat Server] (http://www.baeldung.com/tomcat-programmatic-setup) diff --git a/libraries/pom.xml b/libraries/pom.xml index 09c8cb8335..fa1839010c 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -116,6 +116,12 @@ + + + org.asynchttpclient + async-http-client + ${async.http.client.version} + org.beykery @@ -194,6 +200,11 @@ jetty-servlet ${jetty.version} + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + rome rome @@ -705,6 +716,18 @@ test test + + + + org.apache.tomcat + tomcat-catalina + ${tomcat.version} + + + org.milyn + milyn-smooks-all + ${smooks.version} + @@ -741,12 +764,11 @@ 3.6.2 1.5.0 3.1.0 - 9.4.3.v20170317 4.5.3 2.5 1.6 1.4.196 - 9.4.2.v20170220 + 9.4.8.v20171121 4.5.3 2.5 1.2.0 @@ -757,7 +779,7 @@ 1.1.3-rc.5 1.4.0 1.1.0 - 4.1.15.Final + 4.1.20.Final 4.1 4.12 0.10 @@ -785,6 +807,9 @@ 1.23.0 v4-rev493-1.21.0 1.0.0 + 1.7.0 3.0.14 + 8.5.24 + 2.2.0 \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java b/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java new file mode 100644 index 0000000000..00ba84368a --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/JettyServerFactory.java @@ -0,0 +1,94 @@ +package com.baeldung.jetty; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.webapp.WebAppContext; + +/** + * Simple factory for creating Jetty basic instances. + * + * @author Donato Rimenti + * + */ +public class JettyServerFactory { + + /** + * Exposed context of the app. + */ + public final static String APP_PATH = "/myApp"; + + /** + * The server port. + */ + public final static int SERVER_PORT = 13133; + + /** + * Private constructor to avoid instantiation. + */ + private JettyServerFactory() { + } + + /** + * Returns a simple server listening on port 80 with a timeout of 30 seconds + * for connections and no handlers. + * + * @return a server + */ + public static Server createBaseServer() { + Server server = new Server(); + + // Adds a connector for port 80 with a timeout of 30 seconds. + ServerConnector connector = new ServerConnector(server); + connector.setPort(SERVER_PORT); + connector.setHost("127.0.0.1"); + connector.setIdleTimeout(30000); + server.addConnector(connector); + + return server; + } + + /** + * Creates a server which delegates the request handling to a web + * application. + * + * @return a server + */ + public static Server createWebAppServer() { + // Adds an handler to a server and returns it. + Server server = createBaseServer(); + String webAppFolderPath = JettyServerFactory.class.getClassLoader().getResource("jetty-embedded-demo-app.war") + .getPath(); + Handler webAppHandler = new WebAppContext(webAppFolderPath, APP_PATH); + server.setHandler(webAppHandler); + + return server; + } + + /** + * Creates a server which delegates the request handling to both a logging + * handler and to a web application, in this order. + * + * @return a server + */ + public static Server createMultiHandlerServer() { + Server server = createBaseServer(); + + // Creates the handlers and adds them to the server. + HandlerCollection handlers = new HandlerCollection(); + + String webAppFolderPath = JettyServerFactory.class.getClassLoader().getResource("jetty-embedded-demo-app.war") + .getPath(); + Handler customRequestHandler = new WebAppContext(webAppFolderPath, APP_PATH); + handlers.addHandler(customRequestHandler); + + Handler loggingRequestHandler = new LoggingRequestHandler(); + handlers.addHandler(loggingRequestHandler); + + server.setHandler(handlers); + + return server; + } + +} \ No newline at end of file diff --git a/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java b/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java new file mode 100644 index 0000000000..a38759c903 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/jetty/LoggingRequestHandler.java @@ -0,0 +1,168 @@ +package com.baeldung.jetty; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handler implementation which simply logs that a request has been received. + * + * @author Donato Rimenti + */ +public class LoggingRequestHandler implements Handler { + + /** + * Logger. + */ + private final static Logger LOG = LoggerFactory.getLogger(LoggingRequestHandler.class); + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#addLifeCycleListener(org. + * eclipse.jetty.util.component.LifeCycle.Listener) + */ + @Override + public void addLifeCycleListener(Listener arg0) { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isFailed() + */ + @Override + public boolean isFailed() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isRunning() + */ + @Override + public boolean isRunning() { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isStarted() + */ + @Override + public boolean isStarted() { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isStarting() + */ + @Override + public boolean isStarting() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isStopped() + */ + @Override + public boolean isStopped() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#isStopping() + */ + @Override + public boolean isStopping() { + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jetty.util.component.LifeCycle#removeLifeCycleListener(org. + * eclipse.jetty.util.component.LifeCycle.Listener) + */ + @Override + public void removeLifeCycleListener(Listener arg0) { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#start() + */ + @Override + public void start() throws Exception { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.util.component.LifeCycle#stop() + */ + @Override + public void stop() throws Exception { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.server.Handler#destroy() + */ + @Override + public void destroy() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.server.Handler#getServer() + */ + @Override + public Server getServer() { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.server.Handler#handle(java.lang.String, + * org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3) + throws IOException, ServletException { + LOG.info("Received a new request"); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jetty.server.Handler#setServer(org.eclipse.jetty.server. + * Server) + */ + @Override + public void setServer(Server server) { + } + +} diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java new file mode 100644 index 0000000000..d11f5a29b2 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java @@ -0,0 +1,45 @@ +package com.baeldung.smooks.converter; + +import com.baeldung.smooks.model.Order; +import org.milyn.Smooks; +import org.milyn.payload.JavaResult; +import org.milyn.payload.StringResult; +import org.xml.sax.SAXException; + +import javax.xml.transform.stream.StreamSource; +import java.io.IOException; + +public class OrderConverter { + + public Order convertOrderXMLToOrderObject(String path) throws IOException, SAXException { + Smooks smooks = new Smooks(OrderConverter.class.getResourceAsStream("/smooks/smooks-mapping.xml")); + try { + JavaResult javaResult = new JavaResult(); + smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), javaResult); + return (Order) javaResult.getBean("order"); + } finally { + smooks.close(); + } + } + + + public String convertOrderXMLtoEDIFACT(String path) throws IOException, SAXException { + return convertDocumentWithTempalte(path, "/smooks/smooks-transform-edi.xml"); + } + + public String convertOrderXMLtoEmailMessage(String path) throws IOException, SAXException { + return convertDocumentWithTempalte(path, "/smooks/smooks-transform-email.xml"); + } + + private String convertDocumentWithTempalte(String path, String config) throws IOException, SAXException { + Smooks smooks = new Smooks(config); + + try { + StringResult stringResult = new StringResult(); + smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), stringResult); + return stringResult.toString(); + } finally { + smooks.close(); + } + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java new file mode 100644 index 0000000000..3975921da0 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java @@ -0,0 +1,27 @@ +package com.baeldung.smooks.converter; + +import org.milyn.Smooks; +import org.milyn.payload.JavaResult; +import org.milyn.payload.StringResult; +import org.milyn.validation.ValidationResult; +import org.xml.sax.SAXException; + +import javax.xml.transform.stream.StreamSource; +import java.io.IOException; + +public class OrderValidator { + + public ValidationResult validate(String path) throws IOException, SAXException { + Smooks smooks = new Smooks(OrderValidator.class.getResourceAsStream("/smooks/smooks-validation.xml")); + + try { + StringResult xmlResult = new StringResult(); + JavaResult javaResult = new JavaResult(); + ValidationResult validationResult = new ValidationResult(); + smooks.filterSource(new StreamSource(OrderValidator.class.getResourceAsStream(path)), xmlResult, javaResult, validationResult); + return validationResult; + } finally { + smooks.close(); + } + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Item.java b/libraries/src/main/java/com/baeldung/smooks/model/Item.java new file mode 100644 index 0000000000..a7f7783b3f --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Item.java @@ -0,0 +1,71 @@ +package com.baeldung.smooks.model; + +public class Item { + + public Item() { + } + + public Item(String code, Double price, Integer quantity) { + this.code = code; + this.price = price; + this.quantity = quantity; + } + + private String code; + private Double price; + private Integer quantity; + + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Item item = (Item) o; + + if (code != null ? !code.equals(item.code) : item.code != null) return false; + if (price != null ? !price.equals(item.price) : item.price != null) return false; + return quantity != null ? quantity.equals(item.quantity) : item.quantity == null; + } + + @Override + public int hashCode() { + int result = code != null ? code.hashCode() : 0; + result = 31 * result + (price != null ? price.hashCode() : 0); + result = 31 * result + (quantity != null ? quantity.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Item{" + + "code='" + code + '\'' + + ", price=" + price + + ", quantity=" + quantity + + '}'; + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Order.java b/libraries/src/main/java/com/baeldung/smooks/model/Order.java new file mode 100644 index 0000000000..047e1fe8a3 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Order.java @@ -0,0 +1,52 @@ +package com.baeldung.smooks.model; + +import java.util.Date; +import java.util.List; + +public class Order { + private Date creationDate; + private Long number; + private Status status; + private Supplier supplier; + private List items; + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public Long getNumber() { + return number; + } + + public void setNumber(Long number) { + this.number = number; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public Supplier getSupplier() { + return supplier; + } + + public void setSupplier(Supplier supplier) { + this.supplier = supplier; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Status.java b/libraries/src/main/java/com/baeldung/smooks/model/Status.java new file mode 100644 index 0000000000..53c50bdf46 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Status.java @@ -0,0 +1,5 @@ +package com.baeldung.smooks.model; + +public enum Status { + NEW, IN_PROGRESS, FINISHED +} diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java new file mode 100644 index 0000000000..31a9e1f43f --- /dev/null +++ b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java @@ -0,0 +1,49 @@ +package com.baeldung.smooks.model; + +public class Supplier { + + private String name; + private String phoneNumber; + + public Supplier() { + } + + public Supplier(String name, String phoneNumber) { + this.name = name; + this.phoneNumber = phoneNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Supplier supplier = (Supplier) o; + + if (name != null ? !name.equals(supplier.name) : supplier.name != null) return false; + return phoneNumber != null ? phoneNumber.equals(supplier.phoneNumber) : supplier.phoneNumber == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0); + return result; + } +} diff --git a/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java b/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java new file mode 100644 index 0000000000..9cf4a0ed95 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/MyFilter.java @@ -0,0 +1,31 @@ +package com.baeldung.tomcat; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by adi on 1/14/18. + */ +@WebFilter(urlPatterns = "/my-servlet/*") +public class MyFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + System.out.println("Filtering stuff..."); + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.addHeader("myHeader", "myHeaderValue"); + chain.doFilter(request, httpResponse); + } + + @Override + public void destroy() { + + } +} diff --git a/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java b/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java new file mode 100644 index 0000000000..4bbf3c03a7 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/MyServlet.java @@ -0,0 +1,25 @@ +package com.baeldung.tomcat; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Created by adi on 1/10/18. + */ +@WebServlet( + name = "com.baeldung.tomcat.programmatic.MyServlet", + urlPatterns = {"/my-servlet"} +) +public class MyServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.setStatus(HttpServletResponse.SC_OK); + resp.getWriter().write("test"); + resp.getWriter().flush(); + resp.getWriter().close(); + } +} diff --git a/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java b/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java new file mode 100644 index 0000000000..b84b6b5c6d --- /dev/null +++ b/libraries/src/main/java/com/baeldung/tomcat/ProgrammaticTomcat.java @@ -0,0 +1,63 @@ +package com.baeldung.tomcat; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; + +import java.io.File; + +/** + * Created by adi on 1/10/18. + */ +public class ProgrammaticTomcat { + + private Tomcat tomcat = null; + + //uncomment for live test + // public static void main(String[] args) throws LifecycleException, ServletException, URISyntaxException, IOException { + // startTomcat(); + // } + + public void startTomcat() throws LifecycleException { + tomcat = new Tomcat(); + tomcat.setPort(8080); + tomcat.setHostname("localhost"); + String appBase = "."; + tomcat + .getHost() + .setAppBase(appBase); + + File docBase = new File(System.getProperty("java.io.tmpdir")); + Context context = tomcat.addContext("", docBase.getAbsolutePath()); + + //add a servlet + Class servletClass = MyServlet.class; + Tomcat.addServlet(context, servletClass.getSimpleName(), servletClass.getName()); + context.addServletMappingDecoded("/my-servlet/*", servletClass.getSimpleName()); + + //add a filter and filterMapping + Class filterClass = MyFilter.class; + FilterDef myFilterDef = new FilterDef(); + myFilterDef.setFilterClass(filterClass.getName()); + myFilterDef.setFilterName(filterClass.getSimpleName()); + context.addFilterDef(myFilterDef); + + FilterMap myFilterMap = new FilterMap(); + myFilterMap.setFilterName(filterClass.getSimpleName()); + myFilterMap.addURLPattern("/my-servlet/*"); + context.addFilterMap(myFilterMap); + + tomcat.start(); + //uncomment for live test + // tomcat + // .getServer() + // .await(); + } + + public void stopTomcat() throws LifecycleException { + tomcat.stop(); + tomcat.destroy(); + } +} diff --git a/libraries/src/main/resources/smooks/email.ftl b/libraries/src/main/resources/smooks/email.ftl new file mode 100644 index 0000000000..8413046508 --- /dev/null +++ b/libraries/src/main/resources/smooks/email.ftl @@ -0,0 +1,8 @@ +<#setting locale="en_US"> +Hi, +Order number #${order.number} created on ${order.creationDate?string["yyyy-MM-dd"]} is currently in ${order.status} status. +Consider contact supplier "${supplier.name}" with phone number: "${supplier.phoneNumber}". +Order items: +<#list items as item> +${item.quantity} X ${item.code} (total price ${item.price * item.quantity}) + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/item-rules.csv b/libraries/src/main/resources/smooks/item-rules.csv new file mode 100644 index 0000000000..c93c453f25 --- /dev/null +++ b/libraries/src/main/resources/smooks/item-rules.csv @@ -0,0 +1 @@ +"max_total","item.quantity * item.price < 300.00" diff --git a/libraries/src/main/resources/smooks/order.ftl b/libraries/src/main/resources/smooks/order.ftl new file mode 100644 index 0000000000..9d40eb55c7 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.ftl @@ -0,0 +1,7 @@ +<#setting locale="en_US"> +UNA:+.? ' +UNH+${order.number}+${order.status}+${order.creationDate?string["yyyy-MM-dd"]}' +CTA+${supplier.name}+${supplier.phoneNumber}' +<#list items as item> +LIN+${item.quantity}+${item.code}+${item.price}' + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/order.json b/libraries/src/main/resources/smooks/order.json new file mode 100644 index 0000000000..bf6bc5fe93 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.json @@ -0,0 +1,21 @@ +{ + "creationDate":"2018-01-14", + "orderNumber":771, + "orderStatus":"IN_PROGRESS", + "supplier":{ + "name":"CompanyX", + "phone":"1234567" + }, + "orderItems":[ + { + "quantity":1, + "code":"PX1234", + "price":9.99 + }, + { + "quantity":2, + "code":"RX1990", + "price":120.32 + } + ] +} \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/order.xml b/libraries/src/main/resources/smooks/order.xml new file mode 100644 index 0000000000..343c5cab38 --- /dev/null +++ b/libraries/src/main/resources/smooks/order.xml @@ -0,0 +1,20 @@ + + 771 + IN_PROGRESS + + CompanyX + 1234567 + + + + 1 + PX1234 + 9.99 + + + 2 + RX990 + 120.32 + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-mapping.xml b/libraries/src/main/resources/smooks/smooks-mapping.xml new file mode 100644 index 0000000000..7996834e38 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-mapping.xml @@ -0,0 +1,29 @@ + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-transform-edi.xml b/libraries/src/main/resources/smooks/smooks-transform-edi.xml new file mode 100644 index 0000000000..1dae4055a8 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-transform-edi.xml @@ -0,0 +1,11 @@ + + + + + + + /smooks/order.ftl + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-transform-email.xml b/libraries/src/main/resources/smooks/smooks-transform-email.xml new file mode 100644 index 0000000000..101aa67f0d --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-transform-email.xml @@ -0,0 +1,12 @@ + + + + + + + + /smooks/email.ftl + + + \ No newline at end of file diff --git a/libraries/src/main/resources/smooks/smooks-validation.xml b/libraries/src/main/resources/smooks/smooks-validation.xml new file mode 100644 index 0000000000..b66722ffc5 --- /dev/null +++ b/libraries/src/main/resources/smooks/smooks-validation.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/libraries/src/main/resources/smooks/supplier.properties b/libraries/src/main/resources/smooks/supplier.properties new file mode 100644 index 0000000000..cc17e45eb4 --- /dev/null +++ b/libraries/src/main/resources/smooks/supplier.properties @@ -0,0 +1,2 @@ +supplierName=[A-Za-z0-9]* +supplierPhone=^[0-9\\-\\+]{9,15}$ diff --git a/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java new file mode 100644 index 0000000000..1398c2ba41 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/asynchttpclient/AsyncHttpClientTestCase.java @@ -0,0 +1,209 @@ +package com.baeldung.asynchttpclient; + +import io.netty.handler.codec.http.HttpHeaders; +import org.asynchttpclient.AsyncCompletionHandler; +import org.asynchttpclient.AsyncHandler; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.Dsl; +import org.asynchttpclient.HttpResponseBodyPart; +import org.asynchttpclient.HttpResponseStatus; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Request; +import org.asynchttpclient.Response; +import org.asynchttpclient.ws.WebSocket; +import org.asynchttpclient.ws.WebSocketListener; +import org.asynchttpclient.ws.WebSocketUpgradeHandler; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AsyncHttpClientTestCase { + + private static AsyncHttpClient HTTP_CLIENT; + + @Before + public void setup() { + AsyncHttpClientConfig clientConfig = Dsl.config().setConnectTimeout(15000).setRequestTimeout(15000).build(); + HTTP_CLIENT = Dsl.asyncHttpClient(clientConfig); + } + + @Test + public void givenHttpClient_executeSyncGetRequest() { + + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + + Future responseFuture = boundGetRequest.execute(); + try { + Response response = responseFuture.get(5000, TimeUnit.MILLISECONDS); + assertNotNull(response); + assertEquals(200, response.getStatusCode()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequest() { + + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) { + + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); + + // execute a bound GET request + BoundRequestBuilder boundGetRequest = HTTP_CLIENT.prepareGet("http://www.baeldung.com"); + + boundGetRequest.execute(new AsyncCompletionHandler() { + @Override + public Integer onCompleted(Response response) { + int resposeStatusCode = response.getStatusCode(); + assertEquals(200, resposeStatusCode); + return resposeStatusCode; + } + }); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequestWithAsyncHandler() { + + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + HTTP_CLIENT.executeRequest(unboundGetRequest, new AsyncHandler() { + + int responseStatusCode = -1; + + @Override + public State onStatusReceived(HttpResponseStatus responseStatus) { + responseStatusCode = responseStatus.getStatusCode(); + return State.CONTINUE; + } + + @Override + public State onHeadersReceived(HttpHeaders headers) { + return State.CONTINUE; + } + + @Override + public State onBodyPartReceived(HttpResponseBodyPart bodyPart) { + return State.CONTINUE; + } + + @Override + public void onThrowable(Throwable t) { + + } + + @Override + public Integer onCompleted() { + assertEquals(200, responseStatusCode); + return responseStatusCode; + } + }); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenHttpClient_executeAsyncGetRequestWithListanableFuture() { + // execute an unbound GET request + Request unboundGetRequest = Dsl.get("http://www.baeldung.com").build(); + + ListenableFuture listenableFuture = HTTP_CLIENT.executeRequest(unboundGetRequest); + listenableFuture.addListener(() -> { + Response response; + try { + response = listenableFuture.get(5000, TimeUnit.MILLISECONDS); + assertEquals(200, response.getStatusCode()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + }, Executors.newCachedThreadPool()); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void givenWebSocketClient_tryToConnect() { + + WebSocketUpgradeHandler.Builder upgradeHandlerBuilder = new WebSocketUpgradeHandler.Builder(); + WebSocketUpgradeHandler wsHandler = upgradeHandlerBuilder.addWebSocketListener(new WebSocketListener() { + @Override + public void onOpen(WebSocket websocket) { + // WebSocket connection opened + } + + @Override + public void onClose(WebSocket websocket, int code, String reason) { + // WebSocket connection closed + } + + @Override + public void onError(Throwable t) { + // WebSocket connection error + assertTrue(t.getMessage().contains("Request timeout")); + } + }).build(); + + WebSocket WEBSOCKET_CLIENT = null; + try { + WEBSOCKET_CLIENT = Dsl.asyncHttpClient() + .prepareGet("ws://localhost:5590/websocket") + .addHeader("header_name", "header_value") + .addQueryParam("key", "value") + .setRequestTimeout(5000) + .execute(wsHandler).get(); + + if (WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendPingFrame(); + WEBSOCKET_CLIENT.sendTextFrame("test message"); + WEBSOCKET_CLIENT.sendBinaryFrame(new byte[]{'t', 'e', 's', 't'}); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } finally { + if (WEBSOCKET_CLIENT != null && WEBSOCKET_CLIENT.isOpen()) { + WEBSOCKET_CLIENT.sendCloseFrame(200, "OK"); + } + } + } +} diff --git a/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java b/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java new file mode 100644 index 0000000000..849ba24f55 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/jetty/JettyServerFactoryUnitTest.java @@ -0,0 +1,86 @@ +package com.baeldung.jetty; + +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.eclipse.jetty.server.Server; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test for {@link JettyServerFactory}. + * + * @author Donato Rimenti + * + */ +public class JettyServerFactoryUnitTest { + + /** + * Tests that when a base server is provided a request returns a status 404. + * + * @throws Exception + */ + @Test + public void givenBaseServer_whenHttpRequest_thenStatus404() throws Exception { + Server server = JettyServerFactory.createBaseServer(); + server.start(); + + int statusCode = sendGetRequest(); + + Assert.assertEquals(404, statusCode); + server.stop(); + } + + /** + * Tests that when a web app server is provided a request returns a status + * 200. + * + * @throws Exception + */ + @Test + public void givenWebAppServer_whenHttpRequest_thenStatus200() throws Exception { + Server server = JettyServerFactory.createWebAppServer(); + server.start(); + + int statusCode = sendGetRequest(); + + Assert.assertEquals(200, statusCode); + server.stop(); + } + + /** + * Tests that when a multi handler server is provided a request returns a + * status 200. + * + * @throws Exception + */ + @Test + public void givenMultiHandlerServerServer_whenHttpRequest_thenStatus200() throws Exception { + Server server = JettyServerFactory.createMultiHandlerServer(); + server.start(); + + int statusCode = sendGetRequest(); + + Assert.assertEquals(200, statusCode); + server.stop(); + } + + /** + * Sends a default HTTP GET request to the server and returns the response + * status code. + * + * @return the status code of the response + * @throws Exception + */ + private int sendGetRequest() throws Exception { + HttpHost target = new HttpHost("localhost", JettyServerFactory.SERVER_PORT); + HttpRequest request = new HttpGet(JettyServerFactory.APP_PATH); + HttpClient client = HttpClientBuilder.create().build(); + HttpResponse response = client.execute(target, request); + return response.getStatusLine().getStatusCode(); + } + +} diff --git a/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java new file mode 100644 index 0000000000..4d2cb71329 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java @@ -0,0 +1,70 @@ +package com.baeldung.smooks.converter; + +import com.baeldung.smooks.model.Item; +import com.baeldung.smooks.model.Order; +import com.baeldung.smooks.model.Status; +import com.baeldung.smooks.model.Supplier; +import org.junit.Test; +import org.milyn.validation.ValidationResult; +import java.text.SimpleDateFormat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + + +public class SmooksIntegrationTest { + + private static final String EDIFACT_MESSAGE = + "UNA:+.? '\r\n" + + "UNH+771+IN_PROGRESS+2018-01-14'\r\n" + + "CTA+CompanyX+1234567'\r\n" + + "LIN+1+PX1234+9.99'\r\n" + + "LIN+2+RX990+120.32'\r\n"; + private static final String EMAIL_MESSAGE = + "Hi,\r\n" + + "Order number #771 created on 2018-01-14 is currently in IN_PROGRESS status.\r\n" + + "Consider contact supplier \"CompanyX\" with phone number: \"1234567\".\r\n" + + "Order items:\r\n" + + "1 X PX1234 (total price 9.99)\r\n" + + "2 X RX990 (total price 240.64)\r\n"; + + @Test + public void givenOrderXML_whenConvert_thenPOJOsConstructedCorrectly() throws Exception { + + OrderConverter xmlToJavaOrderConverter = new OrderConverter(); + Order order = xmlToJavaOrderConverter.convertOrderXMLToOrderObject("/smooks/order.xml"); + + assertThat(order.getNumber(),is(771L)); + assertThat(order.getStatus(),is(Status.IN_PROGRESS)); + assertThat(order.getCreationDate(),is(new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-14"))); + assertThat(order.getSupplier(),is(new Supplier("CompanyX","1234567"))); + assertThat(order.getItems(),containsInAnyOrder( + new Item("PX1234",9.99,1), + new Item("RX990",120.32,2)) + ); + + } + + @Test + public void givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors() throws Exception { + OrderValidator orderValidator = new OrderValidator(); + ValidationResult validationResult = orderValidator.validate("/smooks/order.xml"); + + assertThat(validationResult.getErrors(), hasSize(1)); + // 1234567 didn't match ^[0-9\\-\\+]{9,15}$ + assertThat(validationResult.getErrors().get(0).getFailRuleResult().getRuleName(),is("supplierPhone")); + } + + @Test + public void givenOrderXML_whenApplyEDITemplate_thenConvertedToEDIFACT() throws Exception { + OrderConverter orderConverter = new OrderConverter(); + String edifact = orderConverter.convertOrderXMLtoEDIFACT("/smooks/order.xml"); + assertThat(edifact,is(EDIFACT_MESSAGE)); + } + + @Test + public void givenOrderXML_whenApplyEmailTemplate_thenConvertedToEmailMessage() throws Exception { + OrderConverter orderConverter = new OrderConverter(); + String emailMessage = orderConverter.convertOrderXMLtoEmailMessage("/smooks/order.xml"); + assertThat(emailMessage,is(EMAIL_MESSAGE)); + } +} \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java b/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java new file mode 100644 index 0000000000..d559c3d408 --- /dev/null +++ b/libraries/src/test/java/com/baeldung/tomcat/ProgrammaticTomcatTest.java @@ -0,0 +1,62 @@ +package com.baeldung.tomcat; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Created by adi on 1/14/18. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class ProgrammaticTomcatTest { + + private ProgrammaticTomcat tomcat = new ProgrammaticTomcat(); + + @Before + public void setUp() throws Exception { + tomcat.startTomcat(); + } + + @After + public void tearDown() throws Exception { + tomcat.stopTomcat(); + } + + @Test + public void givenTomcatStarted_whenAccessServlet_responseIsTestAndResponseHeaderIsSet() throws Exception { + CloseableHttpClient httpClient = HttpClientBuilder + .create() + .build(); + HttpGet getServlet = new HttpGet("http://localhost:8080/my-servlet"); + + HttpResponse response = httpClient.execute(getServlet); + assertEquals(HttpStatus.SC_OK, response + .getStatusLine() + .getStatusCode()); + + String myHeaderValue = response + .getFirstHeader("myHeader") + .getValue(); + assertEquals("myHeaderValue", myHeaderValue); + + HttpEntity responseEntity = response.getEntity(); + assertNotNull(responseEntity); + + String responseString = EntityUtils.toString(responseEntity, "UTF-8"); + assertEquals("test", responseString); + } + +} diff --git a/libraries/src/test/resources/jetty-embedded-demo-app.war b/libraries/src/test/resources/jetty-embedded-demo-app.war new file mode 100644 index 0000000000..7f8bc37df0 Binary files /dev/null and b/libraries/src/test/resources/jetty-embedded-demo-app.war differ diff --git a/logging-modules/README.md b/logging-modules/README.md index 8ae7316047..6de71adb43 100644 --- a/logging-modules/README.md +++ b/logging-modules/README.md @@ -4,3 +4,4 @@ ### Relevant Articles: - [Creating a Custom Logback Appender](http://www.baeldung.com/custom-logback-appender) +- [Get Log Output in JSON Format](http://www.baeldung.com/java-log-json-output) diff --git a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java index ffe82b7ced..c41423643a 100644 --- a/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java +++ b/persistence-modules/spring-hibernate-5/src/test/java/com/baeldung/hibernate/bootstrap/HibernateBootstrapIntegrationTest.java @@ -8,11 +8,16 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Commit; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { HibernateConf.class }) @Transactional @@ -35,4 +40,146 @@ public class HibernateBootstrapIntegrationTest { Assert.assertNotNull(searchEntity); } + @Test + public void whenProgrammaticTransactionCommit_thenEntityIsInDatabase() { + assertTrue(TestTransaction.isActive()); + + //Save an entity and commit. + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + assertTrue(TestTransaction.isFlaggedForRollback()); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it, but don't commit. + TestTransaction.start(); + + assertTrue(TestTransaction.isFlaggedForRollback()); + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it and commit. + TestTransaction.start(); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + assertTrue(TestTransaction.isActive()); + + TestTransaction.flagForCommit(); + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is no longer there in a new transaction. + TestTransaction.start(); + + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNull(searchEntity); + } + + @Test + @Commit + public void givenTransactionCommitDefault_whenProgrammaticTransactionCommit_thenEntityIsInDatabase() { + assertTrue(TestTransaction.isActive()); + + //Save an entity and commit. + Session session = sessionFactory.getCurrentSession(); + + TestEntity newEntity = new TestEntity(); + newEntity.setId(1); + session.save(newEntity); + + TestEntity searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + assertFalse(TestTransaction.isFlaggedForRollback()); + + TestTransaction.end(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it, but don't commit. + TestTransaction.start(); + + assertFalse(TestTransaction.isFlaggedForRollback()); + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + TestTransaction.flagForRollback(); + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is still there in a new transaction, + //then delete it and commit. + TestTransaction.start(); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNotNull(searchEntity); + + session.delete(searchEntity); + session.flush(); + + assertTrue(TestTransaction.isActive()); + + TestTransaction.end(); + + assertFalse(TestTransaction.isActive()); + + //Check that the entity is no longer there in a new transaction. + TestTransaction.start(); + + assertTrue(TestTransaction.isActive()); + + session = sessionFactory.getCurrentSession(); + searchEntity = session.find(TestEntity.class, 1); + + Assert.assertNull(searchEntity); + } + } diff --git a/pom.xml b/pom.xml index 6321b0333e..ca6d4afe82 100644 --- a/pom.xml +++ b/pom.xml @@ -87,11 +87,13 @@ jackson vavr - java-lite - java-vavr-stream + java-lite + java-rmi + java-vavr-stream javax-servlets javaxval jaxb + jgroups jee-7 jjwt @@ -245,6 +247,7 @@ spring-zuul spring-reactor spring-vertx + spring-jinq spring-rest-embedded-tomcat diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java index 74e2d7daca..c9a333c044 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java @@ -2,22 +2,25 @@ package com.baeldung.reactive.websocket; import java.net.URI; import java.time.Duration; -import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.reactive.socket.WebSocketMessage; import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; import org.springframework.web.reactive.socket.client.WebSocketClient; import reactor.core.publisher.Mono; -@SpringBootApplication public class ReactiveJavaClientWebSocket { - public static void main(String[] args) throws InterruptedException { + public static void main(String[] args) throws InterruptedException { + WebSocketClient client = new ReactorNettyWebSocketClient(); - client.execute(URI.create("ws://localhost:8080/event-emitter"), session -> session.send(Mono.just(session.textMessage("event-me-from-spring-reactive-client"))) + client.execute( + URI.create("ws://localhost:8080/event-emitter"), + session -> session.send( + Mono.just(session.textMessage("event-spring-reactive-client-websocket"))) .thenMany(session.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()) + .map(WebSocketMessage::getPayloadAsText) + .log()) .then()) .block(Duration.ofSeconds(10L)); } + } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java index 7f74e714f6..2e93c0c0dc 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java @@ -1,71 +1,41 @@ package com.baeldung.reactive.websocket; -import org.springframework.web.reactive.socket.WebSocketSession; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - import org.springframework.stereotype.Component; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketMessage; - +import org.springframework.web.reactive.socket.WebSocketSession; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import javax.annotation.PostConstruct; import java.time.Duration; -import java.time.LocalDateTime; -import java.util.UUID; + +import static java.time.LocalDateTime.now; +import static java.util.UUID.randomUUID; @Component public class ReactiveWebSocketHandler implements WebSocketHandler { - private Flux eventFlux; - private Flux intervalFlux; + private static final ObjectMapper json = new ObjectMapper(); - /** - * Here we prepare a Flux that will emit a message every second - */ - @PostConstruct - private void init() throws InterruptedException { + private Flux eventFlux = Flux.generate(sink -> { + Event event = new Event(randomUUID().toString(), now().toString()); + try { + sink.next(json.writeValueAsString(event)); + } catch (JsonProcessingException e) { + sink.error(e); + } + }); - eventFlux = Flux.generate(e -> { - Event event = new Event(UUID.randomUUID() - .toString(), - LocalDateTime.now() - .toString()); - e.next(event); - }); + private Flux intervalFlux = Flux.interval(Duration.ofMillis(1000L)) + .zipWith(eventFlux, (time, event) -> event); - intervalFlux = Flux.interval(Duration.ofMillis(1000L)) - .zipWith(eventFlux, (time, event) -> event); - - } - - /** - * On each new client session, send the message flux to the client. - * Spring subscribes to the flux and send every new flux event to the WebSocketSession object - * @param session - * @return Mono - */ @Override public Mono handle(WebSocketSession webSocketSession) { - ObjectMapper json = new ObjectMapper(); - return webSocketSession.send(intervalFlux.map(event -> { - try { - String jsonEvent = json.writeValueAsString(event); - System.out.println(jsonEvent); - return jsonEvent; - } catch (JsonProcessingException e) { - e.printStackTrace(); - return ""; - } - }) - .map(webSocketSession::textMessage)) - - .and(webSocketSession.receive() - .map(WebSocketMessage::getPayloadAsText) - .log()); + return webSocketSession.send(intervalFlux + .map(webSocketSession::textMessage)) + .and(webSocketSession.receive() + .map(WebSocketMessage::getPayloadAsText).log()); } - } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..2a5c5f0368 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java @@ -0,0 +1,50 @@ +package com.baeldung.loginextrafieldscustom; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException { + + if (!request.getMethod().equals("POST")) { + throw new AuthenticationServiceException("Authentication method not supported: " + + request.getMethod()); + } + + CustomAuthenticationToken authRequest = getAuthRequest(request); + setDetails(request, authRequest); + return this.getAuthenticationManager().authenticate(authRequest); + } + + private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) { + String username = obtainUsername(request); + String password = obtainPassword(request); + String domain = obtainDomain(request); + + if (username == null) { + username = ""; + } + if (password == null) { + password = ""; + } + if (domain == null) { + domain = ""; + } + + return new CustomAuthenticationToken(username, password, domain); + } + + private String obtainDomain(HttpServletRequest request) { + return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java new file mode 100644 index 0000000000..50995169a1 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java @@ -0,0 +1,28 @@ +package com.baeldung.loginextrafieldscustom; + +import java.util.Collection; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken { + + private String domain; + + public CustomAuthenticationToken(Object principal, Object credentials, String domain) { + super(principal, credentials); + this.domain = domain; + super.setAuthenticated(false); + } + + public CustomAuthenticationToken(Object principal, Object credentials, String domain, + Collection authorities) { + super(principal, credentials, authorities); + this.domain = domain; + super.setAuthenticated(true); // must use super, as we override + } + + public String getDomain() { + return this.domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java new file mode 100644 index 0000000000..693900d843 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java @@ -0,0 +1,92 @@ +package com.baeldung.loginextrafieldscustom; + +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.Assert; + +public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + + /** + * The plaintext password used to perform + * PasswordEncoder#matches(CharSequence, String)} on when the user is + * not found to avoid SEC-2056. + */ + private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; + + private PasswordEncoder passwordEncoder; + private CustomUserDetailsService userDetailsService; + + /** + * The password used to perform + * {@link PasswordEncoder#matches(CharSequence, String)} on when the user is + * not found to avoid SEC-2056. This is necessary, because some + * {@link PasswordEncoder} implementations will short circuit if the password is not + * in a valid format. + */ + private String userNotFoundEncodedPassword; + + public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) { + this.passwordEncoder = passwordEncoder; + this.userDetailsService = userDetailsService; + } + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + + if (authentication.getCredentials() == null) { + logger.debug("Authentication failed: no credentials provided"); + throw new BadCredentialsException( + messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + + String presentedPassword = authentication.getCredentials() + .toString(); + + if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { + logger.debug("Authentication failed: password does not match stored value"); + throw new BadCredentialsException( + messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + } + + @Override + protected void doAfterPropertiesSet() throws Exception { + Assert.notNull(this.userDetailsService, "A UserDetailsService must be set"); + this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); + } + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication; + UserDetails loadedUser; + + try { + loadedUser = this.userDetailsService.loadUserByUsernameAndDomain(auth.getPrincipal() + .toString(), auth.getDomain()); + } catch (UsernameNotFoundException notFound) { + if (authentication.getCredentials() != null) { + String presentedPassword = authentication.getCredentials() + .toString(); + passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword); + } + throw notFound; + } catch (Exception repositoryProblem) { + throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); + } + + if (loadedUser == null) { + throw new InternalAuthenticationServiceException("UserDetailsService returned null, " + + "which is an interface contract violation"); + } + return loadedUser; + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java new file mode 100644 index 0000000000..358129173d --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java @@ -0,0 +1,10 @@ +package com.baeldung.loginextrafieldscustom; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public interface CustomUserDetailsService { + + UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException; + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java new file mode 100644 index 0000000000..ea979e2fab --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java @@ -0,0 +1,30 @@ +package com.baeldung.loginextrafieldscustom; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service("userDetailsService") +public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { + + private UserRepository userRepository; + + public CustomUserDetailsServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException { + if (StringUtils.isAnyBlank(username, domain)) { + throw new UsernameNotFoundException("Username and domain must be provided"); + } + User user = userRepository.findUser(username, domain); + if (user == null) { + throw new UsernameNotFoundException( + String.format("Username not found for domain, username=%s, domain=%s", + username, domain)); + } + return user; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java index c86769b016..428c8bf532 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.ArrayList; import java.util.Collection; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java similarity index 54% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java index a779acc75e..0cf934f288 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java @@ -1,13 +1,13 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class SpringExtraLoginFieldsApplication { +public class ExtraLoginFieldsApplication { public static void main(String[] args) { - SpringApplication.run(SpringExtraLoginFieldsApplication.class, args); + SpringApplication.run(ExtraLoginFieldsApplication.class, args); } } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java new file mode 100644 index 0000000000..def85ab978 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java @@ -0,0 +1,62 @@ +package com.baeldung.loginextrafieldscustom; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +@PropertySource("classpath:/application-extrafields.properties") +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private CustomUserDetailsService userDetailsService; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http + .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class) + .authorizeRequests() + .antMatchers("/css/**", "/index").permitAll() + .antMatchers("/user/**").authenticated() + .and() + .formLogin().loginPage("/login") + .and() + .logout() + .logoutUrl("/logout"); + } + + public CustomAuthenticationFilter authenticationFilter() throws Exception { + CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManagerBean()); + filter.setAuthenticationFailureHandler(failureHandler()); + return filter; + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authProvider()); + } + + public AuthenticationProvider authProvider() { + CustomUserDetailsAuthenticationProvider provider + = new CustomUserDetailsAuthenticationProvider(passwordEncoder(), userDetailsService); + return provider; + } + + public SimpleUrlAuthenticationFailureHandler failureHandler() { + return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); + } + + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java similarity index 87% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java index a5b3a434ae..aa03f15b6a 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Collection; @@ -8,7 +8,7 @@ public class User extends org.springframework.security.core.userdetails.User { private static final long serialVersionUID = 1L; - private final String domain; + private String domain; public User(String username, String domain, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java similarity index 66% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java index 4ca65b13d5..e2358e055b 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; public interface UserRepository { diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java similarity index 93% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java index 4a8abb4a83..b5e0b511ac 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Optional; diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..c82a13de1a --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.loginextrafieldssimple; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ExtraLoginFieldsApplication { + + public static void main(String[] args) { + SpringApplication.run(ExtraLoginFieldsApplication.class, args); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java index 429f6df972..d8c5ea8147 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.PropertySource; @@ -36,8 +36,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .logoutUrl("/logout"); } - public CustomAuthenticationFilter authenticationFilter() throws Exception { - CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); + public SimpleAuthenticationFilter authenticationFilter() throws Exception { + SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setAuthenticationFailureHandler(failureHandler()); return filter; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java similarity index 92% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java index b5d628628d..9dcb524157 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -9,7 +9,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { +public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java similarity index 82% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java index be02834852..2fad50ad01 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.userdetails.UserDetails; @@ -7,11 +7,11 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service("userDetailsService") -public class CustomUserDetailsService implements UserDetailsService { +public class SimpleUserDetailsService implements UserDetailsService { - private final UserRepository userRepository; + private UserRepository userRepository; - public CustomUserDetailsService(UserRepository userRepository) { + public SimpleUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java new file mode 100644 index 0000000000..e8aaa774a1 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Repository; + +@Repository("userRepository") +public class SimpleUserRepository implements UserRepository { + + @Override + public User findUser(String username, String domain) { + if (StringUtils.isAnyBlank(username, domain)) { + return null; + } else { + Collection authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java new file mode 100644 index 0000000000..b76da65638 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java @@ -0,0 +1,21 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class User extends org.springframework.security.core.userdetails.User { + + private String domain; + + public User(String username, String domain, String password, boolean enabled, + boolean accountNonExpired, boolean credentialsNonExpired, + boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java new file mode 100644 index 0000000000..919e611b9c --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.loginextrafieldssimple; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java new file mode 100644 index 0000000000..1b17de7bec --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.Optional; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class WebController { + + @RequestMapping("/") + public String root() { + return "redirect:/index"; + } + + @RequestMapping("/index") + public String index(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "index"; + } + + @RequestMapping("/user/index") + public String userIndex(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "user/index"; + } + + @RequestMapping("/login") + public String login() { + return "login"; + } + + private Optional getDomain() { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + String domain = null; + if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) { + User user = (User) auth.getPrincipal(); + domain = user.getDomain(); + } + return Optional.ofNullable(domain); + } +} diff --git a/spring-5-security/src/main/resources/static/css/main.css b/spring-5-security/src/main/resources/static/css/main.css index 9299ee6158..febc353af7 100644 --- a/spring-5-security/src/main/resources/static/css/main.css +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -1,18 +1,8 @@ -body { - font-family: sans; - font-size: 1em; -} - p.error { font-weight: bold; color: red; } div.logout { - float: right; + margin-right: 2em;; } - -.formfield { - margin: 0.5em; - padding: 0.3em; -} \ No newline at end of file diff --git a/spring-5-security/src/main/resources/templatesextrafields/index.html b/spring-5-security/src/main/resources/templatesextrafields/index.html index 52f6224dfb..37833ff0d2 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -1,24 +1,32 @@ - + - Spring Security - Login With Extra Fields + Spring Security with Extra Fields + + + + + -
- Logged in user: | - domain: Some Domain +
+
+

Logged in: | Some Domain +

- +
-

Hello Spring Security

+ +

Hello Spring Security

This is an unsecured page, but you can access the secured pages after authenticating.

+
diff --git a/spring-5-security/src/main/resources/templatesextrafields/login.html b/spring-5-security/src/main/resources/templatesextrafields/login.html index cafec89c15..5c51ea3b2c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/login.html +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -1,23 +1,36 @@ - - - Login page - - - - -

Login page

+ + + Login page + + + + + + + + + +
+ +

+ + +

+

+ + +

+

+ + +

+

Back to home page

- + +
+ diff --git a/spring-5-security/src/main/resources/templatesextrafields/user/index.html b/spring-5-security/src/main/resources/templatesextrafields/user/index.html index a4c1535100..9c41f0e78c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/user/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -1,13 +1,20 @@ - - - Spring Security - Login With Extra Fields - - - - -
-

This is a secured page!

-

Back to home page

- + + + Secured Page + + + + + + + + + +
+
+

This is a secured page!

+

Back to home page

+
+ diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java new file mode 100644 index 0000000000..30b869714f --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java @@ -0,0 +1,46 @@ +package com.baeldung.loginextrafields; + +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +public abstract class AbstractExtraLoginFieldsTest { + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + @Autowired + private WebApplicationContext wac; + + protected MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) + .apply(springSecurity(springSecurityFilterChain)) + .build(); + } + + @Test + public void givenRootPathAccess_thenRedirectToIndex() throws Exception { + this.mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("/index*")); + } + + @Test + public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { + this.mockMvc.perform(get("/user/index")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/login")); + } +} diff --git a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java similarity index 65% rename from spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java rename to spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java index cf0701708d..38c219cb5e 100644 --- a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java @@ -1,8 +1,7 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafields; import static org.junit.Assert.assertEquals; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; @@ -11,57 +10,26 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.ArrayList; import java.util.Collection; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.loginextrafieldscustom.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldscustom.User; @RunWith(SpringRunner.class) @SpringJUnitWebConfig -@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class) -public class SecurityExtraFieldsTest { - - @Autowired - private FilterChainProxy springSecurityFilterChain; - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) - .apply(springSecurity(springSecurityFilterChain)).build(); - } - - @Test - public void givenRootPathAccess_thenRedirectToIndex() throws Exception { - this.mockMvc.perform(get("/")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("/index*")); - } - - @Test - public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { - this.mockMvc.perform(get("/user/index")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("**/login")); - } +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsFullTest extends AbstractExtraLoginFieldsTest { @Test public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { @@ -100,4 +68,5 @@ public class SecurityExtraFieldsTest { Collection authorities = new ArrayList<>(); return new User("myusername", "mydomain", "password", true, true, true, true, authorities); } + } diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java new file mode 100644 index 0000000000..5c0d462772 --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java @@ -0,0 +1,72 @@ +package com.baeldung.loginextrafields; + +import static org.junit.Assert.assertEquals; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import com.baeldung.loginextrafieldssimple.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldssimple.User; + +@RunWith(SpringRunner.class) +@SpringJUnitWebConfig +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsSimpleTest extends AbstractExtraLoginFieldsTest { + + @Test + public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { + MockHttpServletRequestBuilder securedResourceAccess = get("/user/index"); + MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess) + .andExpect(status().is3xxRedirection()) + .andReturn(); + + MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest() + .getSession(); + String loginUrl = unauthenticatedResult.getResponse() + .getRedirectedUrl(); + + User user = getUser(); + + mockMvc.perform(post(loginUrl) + .param("username", user.getUsername()) + .param("password", user.getPassword()) + .param("domain", user.getDomain()) + .session(session) + .with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/user/index")) + .andReturn(); + + mockMvc.perform(securedResourceAccess.session(session)) + .andExpect(status().isOk()); + + SecurityContext securityContext + = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); + Authentication auth = securityContext.getAuthentication(); + assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain()); + } + + private User getUser() { + Collection authorities = new ArrayList<>(); + return new User("myusername", "mydomain", "password", true, true, true, true, authorities); + } + +} diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java index 4e4244abb7..32c3fbdef4 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java @@ -37,6 +37,7 @@ public class BasicAuthConfigurationIntegrationTest { @Test public void whenLoggedUserRequestsHomePage_ThenSuccess() throws IllegalStateException, IOException { ResponseEntity response = restTemplate.getForEntity(base.toString(), String.class); + assertEquals(HttpStatus.OK, response.getStatusCode()); assertTrue(response .getBody() @@ -47,6 +48,7 @@ public class BasicAuthConfigurationIntegrationTest { public void whenUserWithWrongCredentialsRequestsHomePage_ThenUnauthorizedPage() throws IllegalStateException, IOException { restTemplate = new TestRestTemplate("user", "wrongpassword"); ResponseEntity response = restTemplate.getForEntity(base.toString(), String.class); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); assertTrue(response .getBody() diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java index 09df9ce645..cc953eef5a 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java @@ -2,10 +2,7 @@ package com.baeldung.springbootsecurity.oauth2server; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; @@ -13,7 +10,6 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import static java.lang.String.format; import static java.util.Collections.singletonList; import static org.junit.Assert.assertNotNull; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -21,54 +17,35 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class) @ActiveProfiles("authz") -public class CustomConfigAuthorizationServerIntegrationTest { - - @Value("${local.server.port}") protected int port; +public class CustomConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport { @Test - public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("read")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("read")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); } @Test(expected = OAuth2AccessDeniedException.class) - public void whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung", singletonList("write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + restTemplate.getAccessToken(); } @Test - public void whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); - resourceDetails.setClientId("baeldung-admin"); - resourceDetails.setClientSecret("baeldung"); - resourceDetails.setScope(singletonList("write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); - OAuth2AccessToken accessToken = restTemplate.getAccessToken(); - assertNotNull(accessToken); - } + public void givenOAuth2Context_whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("baeldung-admin", singletonList("write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); - private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() { - ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); - resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); - resourceDetails.setGrantType("client_credentials"); - return resourceDetails; + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + + assertNotNull(accessToken); } } diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java index c7b1b4ef6c..4d7b449380 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java @@ -2,40 +2,28 @@ package com.baeldung.springbootsecurity.oauth2server; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.test.context.junit4.SpringRunner; -import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.junit.Assert.assertNotNull; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class, - properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=secret" }) -public class DefaultConfigAuthorizationServerIntegrationTest { - - @Value("${local.server.port}") protected int port; + properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=baeldung" }) +public class DefaultConfigAuthorizationServerIntegrationTest extends OAuth2IntegrationTestSupport { @Test - public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { - ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); - resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); - resourceDetails.setClientId("client"); - resourceDetails.setClientSecret("secret"); - resourceDetails.setGrantType("client_credentials"); - resourceDetails.setScope(asList("read", "write")); - DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); - restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + public void givenOAuth2Context_whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails("client", asList("read", "write")); + OAuth2RestTemplate restTemplate = getOAuth2RestTemplate(resourceDetails); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); } diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java new file mode 100644 index 0000000000..3eef206c7d --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/OAuth2IntegrationTestSupport.java @@ -0,0 +1,34 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; + +import java.util.List; + +import static java.lang.String.format; +import static java.util.Collections.singletonList; + +public class OAuth2IntegrationTestSupport { + + @Value("${local.server.port}") protected int port; + + protected ClientCredentialsResourceDetails getClientCredentialsResourceDetails(final String clientId, final List scopes) { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setClientId(clientId); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(scopes); + resourceDetails.setGrantType("client_credentials"); + return resourceDetails; + } + + protected OAuth2RestTemplate getOAuth2RestTemplate(final ClientCredentialsResourceDetails resourceDetails) { + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + return restTemplate; + } +} diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index d6ee022522..a557604bf3 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -167,6 +167,12 @@ org.apache.activemq artemis-server + + + com.rometools + rome + ${rome.version} + @@ -276,6 +282,7 @@ 8.5.11 1.4.194 2.4.1.Final + 1.9.0 \ No newline at end of file diff --git a/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java index 59f7fd3ba5..478e8d393a 100644 --- a/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java +++ b/spring-boot/src/main/java/com/baeldung/internationalization/config/MvcConfig.java @@ -6,14 +6,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @Configuration -@EnableWebMvc @ComponentScan(basePackages = "com.baeldung.internationalization.config") public class MvcConfig extends WebMvcConfigurerAdapter { @@ -33,6 +31,6 @@ public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(localeChangeInterceptor()); + registry.addInterceptor(localeChangeInterceptor()); } } diff --git a/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java b/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java new file mode 100644 index 0000000000..3efa619409 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/ArticleFeedView.java @@ -0,0 +1,54 @@ +package com.baeldung.rss; + +import com.rometools.rome.feed.rss.Channel; +import com.rometools.rome.feed.rss.Description; +import com.rometools.rome.feed.rss.Item; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.view.feed.AbstractRssFeedView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service("articleFeedView") +public class ArticleFeedView extends AbstractRssFeedView { + + protected Channel newFeed() { + Channel channel = new Channel("rss_2.0"); + channel.setLink("http://localhost:8080/rss"); + channel.setTitle("Article Feed"); + channel.setDescription("Article Feed Description"); + channel.setPubDate(new Date()); + return channel; + } + + @Override + protected List buildFeedItems(Map map, HttpServletRequest httpStRequest, HttpServletResponse httpStResponse) throws Exception { + List list = new ArrayList(); + + Item item1 = new Item(); + item1.setLink("http://www.baeldung.com/netty-exception-handling"); + item1.setTitle("Exceptions in Netty"); + Description description1 = new Description(); + description1.setValue("In this quick article, we’ll be looking at exception handling in Netty."); + item1.setDescription(description1); + item1.setPubDate(new Date()); + item1.setAuthor("Carlos"); + + Item item2 = new Item(); + item2.setLink("http://www.baeldung.com/cockroachdb-java"); + item2.setTitle("Guide to CockroachDB in Java"); + Description description2 = new Description(); + description2.setValue("This tutorial is an introductory guide to using CockroachDB with Java."); + item2.setDescription(description2); + item2.setPubDate(new Date()); + item2.setAuthor("Baeldung"); + + list.add(item1); + list.add(item2); + return list; + } +} diff --git a/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java b/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java new file mode 100644 index 0000000000..a3fbc4a37e --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/ArticleRssController.java @@ -0,0 +1,16 @@ +package com.baeldung.rss; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +@RequestMapping(value = "/rss", produces = "application/*") +public class ArticleRssController { + + @RequestMapping(method = RequestMethod.GET) + public String articleFeed() { + return "articleFeedView"; + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java b/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java new file mode 100644 index 0000000000..ee36ecdc51 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/CustomContainer.java @@ -0,0 +1,16 @@ +package com.baeldung.rss; + +import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class CustomContainer implements EmbeddedServletContainerCustomizer { + + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + container.setPort(8080); + container.setContextPath(""); + } + +} \ No newline at end of file diff --git a/spring-boot/src/main/java/com/baeldung/rss/RssApp.java b/spring-boot/src/main/java/com/baeldung/rss/RssApp.java new file mode 100644 index 0000000000..2add7ed421 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/rss/RssApp.java @@ -0,0 +1,20 @@ +package com.baeldung.rss; + +import com.baeldung.autoconfiguration.MySQLAutoconfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.security.RolesAllowed; + +@SpringBootApplication(exclude = MySQLAutoconfiguration.class) +@ComponentScan(basePackages = "com.baeldung.rss") +public class RssApp { + + @RolesAllowed("*") + public static void main(String[] args) { + System.setProperty("security.basic.enabled", "false"); + SpringApplication.run(RssApp.class, args); + } + +} diff --git a/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java b/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java index 7c66391bd2..8c7f2f932a 100644 --- a/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java +++ b/spring-boot/src/main/java/com/baeldung/utils/controller/UtilsController.java @@ -4,7 +4,6 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.ServletRequestUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; diff --git a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java index 8704b42166..5038c7e5f7 100644 --- a/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java +++ b/spring-boot/src/main/java/com/baeldung/webjar/WebjarsdemoApplication.java @@ -2,7 +2,6 @@ package com.baeldung.webjar; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; import com.baeldung.autoconfiguration.MySQLAutoconfiguration; diff --git a/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java b/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java index decd8ac5db..7bcbfee45b 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java +++ b/spring-boot/src/main/java/org/baeldung/boot/converter/GenericBigDecimalConverter.java @@ -5,7 +5,6 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import java.math.BigDecimal; -import java.math.MathContext; import java.util.Set; public class GenericBigDecimalConverter implements GenericConverter { diff --git a/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java b/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java index ad921c2c43..a6e0400845 100644 --- a/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java +++ b/spring-boot/src/main/java/org/baeldung/boot/converter/controller/StringToEmployeeConverterController.java @@ -1,7 +1,6 @@ package org.baeldung.boot.converter.controller; import com.baeldung.toggle.Employee; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java index 0ab4ecb128..f9bdb67a10 100644 --- a/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java +++ b/spring-boot/src/main/java/org/baeldung/main/SpringBootApplication.java @@ -11,7 +11,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/spring-boot/src/main/resources/static/internationalization.js b/spring-boot/src/main/resources/static/internationalization.js new file mode 100644 index 0000000000..ae416f083d --- /dev/null +++ b/spring-boot/src/main/resources/static/internationalization.js @@ -0,0 +1,8 @@ +$(document).ready(function() { + $("#locales").change(function () { + var selectedOption = $('#locales').val(); + if (selectedOption != ''){ + window.location.replace('international?lang=' + selectedOption); + } + }); +}); \ No newline at end of file diff --git a/spring-boot/src/main/resources/templates/international.html b/spring-boot/src/main/resources/templates/international.html index a2a5fbb591..e0cfb5143b 100644 --- a/spring-boot/src/main/resources/templates/international.html +++ b/spring-boot/src/main/resources/templates/international.html @@ -4,16 +4,7 @@ Home - +

diff --git a/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java index 17e7d2d9e0..5dd6ab8e17 100644 --- a/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java +++ b/spring-boot/src/test/java/org/baeldung/SpringBootMailIntegrationTest.java @@ -10,8 +10,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.web.context.WebApplicationContext; import org.subethamail.wiser.Wiser; import org.subethamail.wiser.WiserMessage; diff --git a/spring-cloud/README.md b/spring-cloud/README.md index 53b7606563..adaf3052e6 100644 --- a/spring-cloud/README.md +++ b/spring-cloud/README.md @@ -12,13 +12,11 @@ Client-side service discovery allows services to find and communicate with each other without hardcoding hostname and port. The only ‘fixed point’ in such an architecture consists of a service registry with which each service has to register. +### Relevant Articles: - [Intro to Spring Cloud Netflix - Hystrix](http://www.baeldung.com/spring-cloud-netflix-hystrix) - [Dockerizing a Spring Boot Application](http://www.baeldung.com/dockerizing-spring-boot-application) - [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon) - [A Quick Guide to Spring Cloud Consul](http://www.baeldung.com/spring-cloud-consul) - -### Relevant Articles: -- [Introduction to Spring Cloud Rest Client with Netflix Ribbon](http://www.baeldung.com/spring-cloud-rest-client-with-netflix-ribbon) - [An Introduction to Spring Cloud Zookeeper](http://www.baeldung.com/spring-cloud-zookeeper) +- [Using a Spring Cloud App Starter](http://www.baeldung.com/using-a-spring-cloud-app-starter) - [Spring Cloud Connectors and Heroku](http://www.baeldung.com/spring-cloud-heroku) - diff --git a/spring-cloud/spring-cloud-aws/README.md b/spring-cloud/spring-cloud-aws/README.md index c5f58159c8..1340712c7a 100644 --- a/spring-cloud/spring-cloud-aws/README.md +++ b/spring-cloud/spring-cloud-aws/README.md @@ -19,3 +19,8 @@ to write the following in `application.properties`: cloud.aws.rds.spring-cloud-test-db cloud.aws.rds.spring-cloud-test-db.password=se3retpass ``` +Multiple application classes are available under this project. To launch InstanceProfileAwsApplication application, replace `start-class` under `pom.xml`: + +``` +com.baeldung.spring.cloud.aws.InstanceProfileAwsApplication +``` \ No newline at end of file diff --git a/spring-cloud/spring-cloud-aws/pom.xml b/spring-cloud/spring-cloud-aws/pom.xml index 632e050d92..b27b6c0d18 100644 --- a/spring-cloud/spring-cloud-aws/pom.xml +++ b/spring-cloud/spring-cloud-aws/pom.xml @@ -19,6 +19,7 @@ + com.baeldung.spring.cloud.aws.SpringCloudAwsApplication UTF-8 UTF-8 1.8 diff --git a/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java new file mode 100644 index 0000000000..80c0851a6f --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/InstanceProfileAwsApplication.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.cloud.aws; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.spring.cloud.aws.s3.SpringCloudS3Service; + +@Configuration +@EnableAutoConfiguration +@ComponentScan("com.baeldung.spring.cloud.aws.s3") +public class InstanceProfileAwsApplication { + + private static final Logger logger = LoggerFactory.getLogger(InstanceProfileAwsApplication.class); + private static final String applicationConfig = "spring.config.name:application-instance-profile"; + + private static String bucketName; + private static String fileName = "sample-file.txt"; + + private static void setupResources() { + bucketName = "baeldung-test-" + UUID.randomUUID() + .toString(); + try { + Files.write(Paths.get(fileName), "Hello World!".getBytes()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + + public static void main(String[] args) { + setupResources(); + if (!new File(fileName).exists()) { + logger.warn("Not able to create {} file. Check your folder permissions.", fileName); + System.exit(1); + } + + SpringApplication application = new SpringApplicationBuilder(InstanceProfileAwsApplication.class).properties(applicationConfig) + .build(); + ConfigurableApplicationContext context = application.run(args); + SpringCloudS3Service service = context.getBean(SpringCloudS3Service.class); + + // S3 bucket operations + service.createBucket(bucketName); + service.uploadObject(bucketName, fileName); + service.downloadObject(bucketName, fileName); + service.deleteBucket(bucketName); + } + +} diff --git a/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java new file mode 100644 index 0000000000..a0fdce2143 --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/java/com/baeldung/spring/cloud/aws/s3/SpringCloudS3Service.java @@ -0,0 +1,64 @@ +package com.baeldung.spring.cloud.aws.s3; + +import java.io.File; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ListObjectsV2Result; +import com.amazonaws.services.s3.model.S3ObjectSummary; + +@Component +public class SpringCloudS3Service { + + private static final Logger logger = LoggerFactory.getLogger(SpringCloudS3Service.class); + + @Autowired + AmazonS3 amazonS3; + + @Autowired + SpringCloudS3 springCloudS3; + + public void createBucket(String bucketName) { + logger.debug("Creating S3 bucket: {}", bucketName); + amazonS3.createBucket(bucketName); + logger.info("{} bucket created successfully", bucketName); + } + + public void downloadObject(String bucketName, String objectName) { + String s3Url = "s3://" + bucketName + "/" + objectName; + try { + springCloudS3.downloadS3Object(s3Url); + logger.info("{} file download result: {}", objectName, new File(objectName).exists()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + + public void uploadObject(String bucketName, String objectName) { + String s3Url = "s3://" + bucketName + "/" + objectName; + File file = new File(objectName); + try { + springCloudS3.uploadFileToS3(file, s3Url); + logger.info("{} file uploaded to S3", objectName); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + + public void deleteBucket(String bucketName) { + logger.trace("Deleting S3 objects under {} bucket...", bucketName); + ListObjectsV2Result listObjectsV2Result = amazonS3.listObjectsV2(bucketName); + for (S3ObjectSummary objectSummary : listObjectsV2Result.getObjectSummaries()) { + logger.info("Deleting S3 object: {}", objectSummary.getKey()); + amazonS3.deleteObject(bucketName, objectSummary.getKey()); + } + logger.info("Deleting S3 bucket: {}", bucketName); + amazonS3.deleteBucket(bucketName); + } + +} diff --git a/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml b/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml new file mode 100644 index 0000000000..f8c3d3c915 --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/resources/InstanceProfileFormation.yaml @@ -0,0 +1,78 @@ +AWSTemplateFormatVersion: 2010-09-09 +Metadata: + 'AWS::CloudFormation::Designer': + 157e7d5f-5cb3-4a23-a50c-97e7f6c57173: + size: + width: 60 + height: 60 + position: + x: 450 + 'y': 90 + z: 0 + embeds: [] + 9bbaaa55-9cba-4555-a7c6-fb6ac248fd3a: + size: + width: 60 + height: 60 + position: + x: 260 + 'y': 90 + z: 0 + embeds: [] + isassociatedwith: + - 157e7d5f-5cb3-4a23-a50c-97e7f6c57173 + a7348729-a594-4dca-9b0a-e1c8d777dc3b: + size: + width: 60 + height: 60 + position: + x: 70 + 'y': 90 + z: 0 + embeds: [] +Resources: + IAMRoleBaeldung: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - 'sts:AssumeRole' + ManagedPolicyArns: + - 'arn:aws:iam::aws:policy/AmazonS3FullAccess' + Metadata: + 'AWS::CloudFormation::Designer': + id: 157e7d5f-5cb3-4a23-a50c-97e7f6c57173 + InstanceProfileBaeldung: + Type: 'AWS::IAM::InstanceProfile' + Properties: + Roles: + - !Ref IAMRoleBaeldung + Metadata: + 'AWS::CloudFormation::Designer': + id: 9bbaaa55-9cba-4555-a7c6-fb6ac248fd3a + EC2Instance: + Type: 'AWS::EC2::Instance' + Properties: + ImageId: ami-2581aa40 + InstanceType: t2.micro + IamInstanceProfile: !Ref InstanceProfileBaeldung + KeyName: Satish-Ohio + UserData: !Base64 + 'Fn::Join': + - '' + - - | + #!/bin/bash + - | + apt -y install openjdk-8-jre-headless + Metadata: + 'AWS::CloudFormation::Designer': + id: a7348729-a594-4dca-9b0a-e1c8d777dc3b + DependsOn: + - InstanceProfileBaeldung + diff --git a/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties b/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties new file mode 100644 index 0000000000..23ca09c85c --- /dev/null +++ b/spring-cloud/spring-cloud-aws/src/main/resources/application-instance-profile.properties @@ -0,0 +1,14 @@ +# Don't try to create DataSouce when running tests which don't need a DataSource +spring.autoconfigure.exclude=\ + org.springframework.cloud.aws.autoconfigure.jdbc.AmazonRdsDatabaseAutoConfiguration,\ + org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration +cloud.aws.region.auto=true + +# Load instance profile credentials +cloud.aws.credentials.instanceProfile=true + +# Disable auto cloud formation +cloud.aws.stack.auto=false + +# Disable web environment +spring.main.web-environment=false \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh new file mode 100644 index 0000000000..ca8298430b --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/bash/hadoop.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# For Ubuntu 14.04 +# Inspired from: https://github.com/curran/setupHadoop/blob/master/setupHadoop.sh +# Use from the user directory + +# Install Java +sudo apt-get update +sudo add-apt-repository -y ppa:webupd8team/java +sudo apt-get install -y oracle-java8-installer + +# Install Hadoop +curl -O http://mirror.cogentco.com/pub/apache/hadoop/common/hadoop-2.8.2/hadoop-2.8.2.tar.gz +tar xfz hadoop-2.8.2.tar.gz +sudo mv hadoop-2.8.2 /usr/local/hadoop +rm hadoop-2.8.2.tar.gz + +# Environmental Variables +echo export JAVA_HOME=/usr/lib/jvm/java-8-oracle >> ~/.bashrc +echo export HADOOP_PREFIX=/usr/local/hadoop >> ~/.bashrc +echo export PATH=\$PATH:/usr/local/hadoop/bin >> ~/.bashrc +echo export PATH=\$PATH:/usr/local/hadoop/sbin >> ~/.bashrc +source ~/.bashrc + +# Copy configuration files +cp master/* /usr/local/hadoop/etc/hadoop/ + +# Format HDFS +hdfs namenode -format + +# SSH keys for Hadoop to use. +ssh-keygen -t rsa -P 'password' -f ~/.ssh/id_rsa.pub +sudo mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys + +# SSH +ssh localhost +# authenticate with osboxes.org + +# Start NameNode daemon and DataNode daemon +start-dfs.sh +# stop-dfs.sh + +# Install Maven +sudo apt-get install maven + +# Access Hadoop - http://localhost:50070 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/.gitignore b/spring-cloud/spring-cloud-stream-starters/boot/.gitignore new file mode 100644 index 0000000000..e4b82e1c0f --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/.gitignore @@ -0,0 +1,3 @@ +.idea +*/target/* +*.iml diff --git a/spring-cloud/spring-cloud-stream-starters/boot/pom.xml b/spring-cloud/spring-cloud-stream-starters/boot/pom.xml new file mode 100644 index 0000000000..3e6bc134e3 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + com.baeldung.twitterhdfs + twitterhdfs + jar + 1.0.0 + + twitterhdfs + + + UTF-8 + UTF-8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.RELEASE + + + + + + org.springframework.cloud.stream.app + spring-cloud-starter-stream-source-twitterstream + 1.3.1.RELEASE + + + org.springframework.cloud.stream.app + spring-cloud-starter-stream-sink-hdfs + 1.3.1.RELEASE + + + + + javax.servlet + jstl + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + twitterhdfs + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java new file mode 100644 index 0000000000..8b9ca6dc62 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/aggregate/AggregateApp.java @@ -0,0 +1,18 @@ +package com.baeldung.twitterhdfs.aggregate; + +import com.baeldung.twitterhdfs.processor.ProcessorApp; +import com.baeldung.twitterhdfs.source.SourceApp; +import com.baeldung.twitterhdfs.sink.SinkApp; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder; + +@SpringBootApplication +public class AggregateApp { + public static void main(String[] args) { + new AggregateApplicationBuilder() + .from(SourceApp.class).args("--fixedDelay=5000") + .via(ProcessorApp.class) + .to(SinkApp.class).args("--debug=true") + .run(args); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java new file mode 100644 index 0000000000..e3bd1197f6 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/processor/ProcessorApp.java @@ -0,0 +1,20 @@ +package com.baeldung.twitterhdfs.processor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.integration.annotation.Transformer; + +@SpringBootApplication +@EnableBinding(Processor.class) +public class ProcessorApp { + Logger log = LoggerFactory.getLogger(ProcessorApp.class); + + @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT) + public String processMessage(String payload) { + log.info("Payload received!"); + return payload; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java new file mode 100644 index 0000000000..c0c1e287d3 --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/sink/SinkApp.java @@ -0,0 +1,22 @@ +package com.baeldung.twitterhdfs.sink; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.app.hdfs.sink.HdfsSinkConfiguration; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.context.annotation.Import; +import org.springframework.integration.annotation.ServiceActivator; + +@SpringBootApplication +@EnableBinding(Sink.class) +@Import(HdfsSinkConfiguration.class) +public class SinkApp { + Logger log = LoggerFactory.getLogger(SinkApp.class); + + @ServiceActivator(inputChannel= Sink.INPUT) + public void loggerSink(Object payload) { + log.info("Received: " + payload); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java new file mode 100644 index 0000000000..f9b220561b --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/java/com/baeldung/twitterhdfs/source/SourceApp.java @@ -0,0 +1,26 @@ +package com.baeldung.twitterhdfs.source; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.app.twitterstream.source.TwitterstreamSourceConfiguration; +import org.springframework.cloud.stream.messaging.Source; +import org.springframework.context.annotation.Import; +import org.springframework.integration.annotation.InboundChannelAdapter; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@SpringBootApplication +@EnableBinding(Source.class) +@Import(TwitterstreamSourceConfiguration.class) +public class SourceApp { + Logger log = LoggerFactory.getLogger(SourceApp.class); + + @InboundChannelAdapter(value = Source.OUTPUT) + public String timerMessageSource() { + return new SimpleDateFormat().format(new Date()); + } + +} diff --git a/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties b/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties new file mode 100644 index 0000000000..298a8ebf4d --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/boot/src/main/resources/application.properties @@ -0,0 +1,6 @@ +hdfs.fs-uri=hdfs://127.0.0.1:50010/ + +twitter.credentials.access-token= +twitter.credentials.access-token-secret= +twitter.credentials.consumer-key= +twitter.credentials.consumer-secret= \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties new file mode 100644 index 0000000000..1f4aaf88dd --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/application.properties @@ -0,0 +1 @@ +hdfs.fs-uri=hdfs://127.0.0.1:50010/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh new file mode 100644 index 0000000000..f1c6bfcf3f --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/hdfs/hdfs.sh @@ -0,0 +1,11 @@ +# Git spring-cloud-stream-app-starters +# https://github.com/spring-cloud-stream-app-starters/hdfs/blob/master/spring-cloud-starter-stream-sink-hdfs/README.adoc +git clone https://github.com/spring-cloud-stream-app-starters/hdfs.git + +# Build it +./mvnw clean install -PgenerateApps + +# Run it +cd target +# Optionally inject application.properties prior to build +java -jar hdfs-sink.jar --fsUri=hdfs://127.0.0.1:50010/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/twitter/application.properties b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties new file mode 100644 index 0000000000..e38612d25e --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/application.properties @@ -0,0 +1,4 @@ +twitter.credentials.access-token= +twitter.credentials.access-token-secret= +twitter.credentials.consumer-key= +twitter.credentials.consumer-secret= \ No newline at end of file diff --git a/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh new file mode 100644 index 0000000000..967cb54dfe --- /dev/null +++ b/spring-cloud/spring-cloud-stream-starters/twitter/twitter.sh @@ -0,0 +1,12 @@ +# Git spring-cloud-stream-app-starters +# https://github.com/spring-cloud-stream-app-starters/hdfs/blob/master/spring-cloud-starter-stream-sink-hdfs/README.adoc +git clone https://github.com/spring-cloud-stream-app-starters/twitter.git + +# Build it +./mvnw clean install -PgenerateApps + +# Run it +cd target +# Optionally inject application.properties prior to build +java -jar twitter_stream_source.jar --consumerKey= --consumerSecret= \ + --accessToken= --accessTokenSecret= \ No newline at end of file diff --git a/spring-data-spring-security/README.md b/spring-data-spring-security/README.md new file mode 100644 index 0000000000..15b4b50870 --- /dev/null +++ b/spring-data-spring-security/README.md @@ -0,0 +1,14 @@ +# About this project +This project contains examples from the [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security) article from Baeldung. + +# Running the project +The application uses [Spring Boot](http://projects.spring.io/spring-boot/), so it is easy to run. You can start it any of a few ways: +* Run the `main` method from `SpringDataRestApplication` +* Use the Maven Spring Boot plugin: `mvn spring-boot:run` +* Package the application as a JAR and run it using `java -jar spring-data-spring-security.jar` + +# Viewing the running application +To view the running application, visit [http://localhost:8080](http://localhost:8080) in your browser + +###Relevant Articles: +- [Spring Data with Spring Security](http://www.baeldung.com/spring-data-with-spring-security) diff --git a/spring-data-spring-security/pom.xml b/spring-data-spring-security/pom.xml new file mode 100644 index 0000000000..d6b671ee57 --- /dev/null +++ b/spring-data-spring-security/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.baeldung + spring-data-spring-security + 1.0 + jar + + intro-spring-data-spring-security + Spring Data with Spring Security + + + parent-boot-5 + com.baeldung + 0.0.1-SNAPSHOT + ../parent-boot-5 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.security + spring-security-data + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + + + org.apache.tomcat.embed + tomcat-embed-jasper + + + + com.h2database + h2 + + + javax.servlet + jstl + + + + + ${project.artifactId} + + + + diff --git a/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java new file mode 100644 index 0000000000..16bbe8b326 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/AppConfig.java @@ -0,0 +1,64 @@ +package com.baeldung; + +import java.util.Properties; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@SpringBootApplication +@PropertySource("classpath:persistence-h2.properties") +@EnableJpaRepositories(basePackages = { "com.baeldung.data.repositories" }) +@EnableWebMvc +@Import(SpringSecurityConfig.class) +public class AppConfig extends WebMvcConfigurerAdapter { + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("driverClassName")); + dataSource.setUrl(env.getProperty("url")); + dataSource.setUsername(env.getProperty("user")); + dataSource.setPassword(env.getProperty("password")); + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "com.baeldung.models" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + if (env.getProperty("hibernate.hbm2ddl.auto") != null) { + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + } + if (env.getProperty("hibernate.dialect") != null) { + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + } + if (env.getProperty("hibernate.show_sql") != null) { + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + } + return hibernateProperties; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java new file mode 100644 index 0000000000..ee13678a24 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/SpringSecurityConfig.java @@ -0,0 +1,89 @@ +package com.baeldung; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; +import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.security.AuthenticationSuccessHandlerImpl; +import com.baeldung.security.CustomUserDetailsService; + +@Configuration +@EnableWebSecurity +@ComponentScan("com.baeldung.security") +public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private WebApplicationContext applicationContext; + private CustomUserDetailsService userDetailsService; + @Autowired + private AuthenticationSuccessHandlerImpl successHandler; + @Autowired + private DataSource dataSource; + + @PostConstruct + public void completeSetup() { + userDetailsService = applicationContext.getBean(CustomUserDetailsService.class); + } + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService) + .passwordEncoder(encoder()) + .and() + .authenticationProvider(authenticationProvider()) + .jdbcAuthentication() + .dataSource(dataSource); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers("/resources/**"); + } + + @Override + protected void configure(final HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/login") + .permitAll() + .and() + .formLogin() + .permitAll() + .successHandler(successHandler) + .and() + .csrf() + .disable(); + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(encoder()); + return authProvider; + } + + @Bean + public PasswordEncoder encoder() { + return new BCryptPasswordEncoder(11); + } + + @Bean + public SecurityEvaluationContextExtension securityEvaluationContextExtension() { + return new SecurityEvaluationContextExtension(); + } +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java new file mode 100644 index 0000000000..7d6446ed0d --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/TweetRepository.java @@ -0,0 +1,14 @@ +package com.baeldung.data.repositories; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.PagingAndSortingRepository; + +import com.baeldung.models.Tweet; + +public interface TweetRepository extends PagingAndSortingRepository { + + @Query("select twt from Tweet twt JOIN twt.likes as lk where lk = ?#{ principal?.username } or twt.owner = ?#{ principal?.username }") + Page getMyTweetsAndTheOnesILiked(Pageable pageable); +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java new file mode 100644 index 0000000000..9f13c3197e --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/data/repositories/UserRepository.java @@ -0,0 +1,27 @@ +package com.baeldung.data.repositories; + +import java.util.Date; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; + +public interface UserRepository extends CrudRepository { + AppUser findByUsername(String username); + + List findByName(String name); + + @Query("UPDATE AppUser u SET u.lastLogin=:lastLogin WHERE u.username = ?#{ principal?.username }") + @Modifying + @Transactional + public void updateLastLogin(@Param("lastLogin") Date lastLogin); +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java new file mode 100644 index 0000000000..e48233f90a --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/models/AppUser.java @@ -0,0 +1,83 @@ +package com.baeldung.models; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "users") +public class AppUser { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + + private String name; + @Column(unique = true) + private String username; + private String password; + private boolean enabled = true; + private Date lastLogin; + + private AppUser() { + } + + public AppUser(String name, String email, String password) { + this.username = email; + this.name = name; + this.password = password; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public Date getLastLogin() { + return lastLogin; + } + + public void setLastLogin(Date lastLogin) { + this.lastLogin = lastLogin; + } +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java new file mode 100644 index 0000000000..b2e45009f6 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/models/Tweet.java @@ -0,0 +1,63 @@ +package com.baeldung.models; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Tweet { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + private String tweet; + private String owner; + @ElementCollection(targetClass = String.class, fetch = FetchType.EAGER) + private Set likes = new HashSet(); + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + private Tweet() { + } + + public Tweet(String tweet, String owner) { + this.tweet = tweet; + this.owner = owner; + } + + public String getTweet() { + return tweet; + } + + public void setTweet(String tweet) { + this.tweet = tweet; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Set getLikes() { + return likes; + } + + public void setLikes(Set likes) { + this.likes = likes; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java new file mode 100644 index 0000000000..195f9f7bf6 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AppUserPrincipal.java @@ -0,0 +1,67 @@ +package com.baeldung.security; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.baeldung.models.AppUser; + +public class AppUserPrincipal implements UserDetails { + + private final AppUser user; + + // + + public AppUserPrincipal(AppUser user) { + this.user = user; + } + + // + + @Override + public String getUsername() { + return user.getUsername(); + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public Collection getAuthorities() { + final List authorities = Collections.singletonList(new SimpleGrantedAuthority("User")); + return authorities; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + // + + public AppUser getAppUser() { + return user; + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java new file mode 100644 index 0000000000..3fc2bc6559 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/AuthenticationSuccessHandlerImpl.java @@ -0,0 +1,28 @@ +package com.baeldung.security; + +import java.io.IOException; +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import com.baeldung.data.repositories.UserRepository; + +@Component +public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { + + @Autowired + private UserRepository userRepository; + + @Override + public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2) throws IOException, ServletException { + userRepository.updateLastLogin(new Date()); + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java new file mode 100644 index 0000000000..016f4f7fa9 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/security/CustomUserDetailsService.java @@ -0,0 +1,40 @@ +package com.baeldung.security; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.data.repositories.UserRepository; +import com.baeldung.models.AppUser; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + @Autowired + private WebApplicationContext applicationContext; + private UserRepository userRepository; + + public CustomUserDetailsService() { + super(); + } + + @PostConstruct + public void completeSetup() { + userRepository = applicationContext.getBean(UserRepository.class); + } + + @Override + public UserDetails loadUserByUsername(final String username) { + final AppUser appUser = userRepository.findByUsername(username); + if (appUser == null) { + throw new UsernameNotFoundException(username); + } + return new AppUserPrincipal(appUser); + } + +} diff --git a/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java new file mode 100644 index 0000000000..f1640264d2 --- /dev/null +++ b/spring-data-spring-security/src/main/java/com/baeldung/util/DummyContentUtil.java @@ -0,0 +1,63 @@ +package com.baeldung.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; + +public class DummyContentUtil { + + public static final List generateDummyUsers() { + List appUsers = new ArrayList<>(); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + appUsers.add(new AppUser("Lionel Messi", "lionel@messi.com", passwordEncoder.encode("li1234"))); + appUsers.add(new AppUser("Cristiano Ronaldo", "cristiano@ronaldo.com", passwordEncoder.encode("c1234"))); + appUsers.add(new AppUser("Neymar Dos Santos", "neymar@neymar.com", passwordEncoder.encode("n1234"))); + appUsers.add(new AppUser("Luiz Suarez", "luiz@suarez.com", passwordEncoder.encode("lu1234"))); + appUsers.add(new AppUser("Andres Iniesta", "andres@iniesta.com", passwordEncoder.encode("a1234"))); + appUsers.add(new AppUser("Ivan Rakitic", "ivan@rakitic.com", passwordEncoder.encode("i1234"))); + appUsers.add(new AppUser("Ousman Dembele", "ousman@dembele.com", passwordEncoder.encode("o1234"))); + appUsers.add(new AppUser("Sergio Busquet", "sergio@busquet.com", passwordEncoder.encode("s1234"))); + appUsers.add(new AppUser("Gerard Pique", "gerard@pique.com", passwordEncoder.encode("g1234"))); + appUsers.add(new AppUser("Ter Stergen", "ter@stergen.com", passwordEncoder.encode("t1234"))); + return appUsers; + } + + public static final List generateDummyTweets(List users) { + List tweets = new ArrayList<>(); + Random random = new Random(); + IntStream.range(0, 9) + .sequential() + .forEach(i -> { + Tweet twt = new Tweet(String.format("Tweet %d", i), users.get(random.nextInt(users.size())) + .getUsername()); + twt.getLikes() + .addAll(users.subList(0, random.nextInt(users.size())) + .stream() + .map(AppUser::getUsername) + .collect(Collectors.toSet())); + tweets.add(twt); + }); + return tweets; + } + + public static Collection getAuthorities() { + Collection grantedAuthorities = new ArrayList(); + GrantedAuthority grantedAuthority = new GrantedAuthority() { + public String getAuthority() { + return "ROLE_USER"; + } + }; + grantedAuthorities.add(grantedAuthority); + return grantedAuthorities; + } + +} diff --git a/spring-data-spring-security/src/main/resources/application.properties b/spring-data-spring-security/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-data-spring-security/src/main/resources/persistence-h2.properties b/spring-data-spring-security/src/main/resources/persistence-h2.properties new file mode 100644 index 0000000000..a4b2af6361 --- /dev/null +++ b/spring-data-spring-security/src/main/resources/persistence-h2.properties @@ -0,0 +1,8 @@ +driverClassName=org.h2.Driver +url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 +username=sa +password= + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=false +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java new file mode 100644 index 0000000000..dbbfe7e85e --- /dev/null +++ b/spring-data-spring-security/src/test/java/com/baeldung/relationships/SpringDataWithSecurityTest.java @@ -0,0 +1,100 @@ +package com.baeldung.relationships; + +import static org.springframework.util.Assert.isTrue; + +import java.util.Date; +import java.util.List; + +import javax.servlet.ServletContext; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import com.baeldung.AppConfig; +import com.baeldung.data.repositories.TweetRepository; +import com.baeldung.data.repositories.UserRepository; +import com.baeldung.models.AppUser; +import com.baeldung.models.Tweet; +import com.baeldung.security.AppUserPrincipal; +import com.baeldung.util.DummyContentUtil; + +@RunWith(SpringRunner.class) +@WebAppConfiguration +@ContextConfiguration +@DirtiesContext +public class SpringDataWithSecurityTest { + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + @Autowired + private ServletContext servletContext; + private static UserRepository userRepository; + private static TweetRepository tweetRepository; + + @Before + public void testInit() { + ctx.register(AppConfig.class); + ctx.setServletContext(servletContext); + ctx.refresh(); + userRepository = ctx.getBean(UserRepository.class); + tweetRepository = ctx.getBean(TweetRepository.class); + List appUsers = (List) userRepository.save(DummyContentUtil.generateDummyUsers()); + tweetRepository.save(DummyContentUtil.generateDummyTweets(appUsers)); + } + + @AfterClass + public static void tearDown() { + tweetRepository.deleteAll(); + userRepository.deleteAll(); + } + + @Test + public void givenAppUser_whenLoginSuccessful_shouldUpdateLastLogin() { + AppUser appUser = userRepository.findByUsername("lionel@messi.com"); + Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities()); + SecurityContextHolder.getContext() + .setAuthentication(auth); + userRepository.updateLastLogin(new Date()); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void givenNoAppUserInSecurityContext_whenUpdateLastLoginAttempted_shouldFail() { + userRepository.updateLastLogin(new Date()); + } + + @Test + public void givenAppUser_whenLoginSuccessful_shouldReadMyPagedTweets() { + AppUser appUser = userRepository.findByUsername("lionel@messi.com"); + Authentication auth = new UsernamePasswordAuthenticationToken(new AppUserPrincipal(appUser), null, DummyContentUtil.getAuthorities()); + SecurityContextHolder.getContext() + .setAuthentication(auth); + Page page = null; + do { + page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5)); + for (Tweet twt : page.getContent()) { + isTrue((twt.getOwner() == appUser.getUsername()) || (twt.getLikes() + .contains(appUser.getUsername())), "I do not have any Tweets"); + } + } while (page.hasNext()); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + public void givenNoAppUser_whenPaginatedResultsRetrievalAttempted_shouldFail() { + Page page = null; + do { + page = tweetRepository.getMyTweetsAndTheOnesILiked(new PageRequest(page != null ? page.getNumber() + 1 : 0, 5)); + } while (page != null && page.hasNext()); + } +} diff --git a/spring-jinq/pom.xml b/spring-jinq/pom.xml new file mode 100644 index 0000000000..a895ae8dd4 --- /dev/null +++ b/spring-jinq/pom.xml @@ -0,0 +1,83 @@ + + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + 4.0.0 + spring-jinq + 0.1-SNAPSHOT + + spring-jinq + + jar + + + UTF-8 + 1.8 + + 1.8.22 + + + + + + org.springframework.boot + spring-boot-dependencies + 1.5.9.RELEASE + pom + import + + + + + + + org.jinq + jinq-jpa + ${jinq.version} + + + + + com.h2database + h2 + + + + org.hibernate + hibernate-entitymanager + + + + + org.springframework + spring-orm + + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + false + + + + + + diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java new file mode 100644 index 0000000000..d53b585d36 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/JinqApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.jinq; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JinqApplication { + + public static void main(String[] args) { + SpringApplication.run(JinqApplication.class, args); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java new file mode 100644 index 0000000000..6d921045b7 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/config/JinqProviderConfiguration.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.jinq.config; + +import javax.persistence.EntityManagerFactory; + +import org.jinq.jpa.JinqJPAStreamProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JinqProviderConfiguration { + + @Bean + @Autowired + JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) { + return new JinqJPAStreamProvider(emf); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java new file mode 100644 index 0000000000..263e6c7622 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Car.java @@ -0,0 +1,58 @@ +package com.baeldung.spring.jinq.entities; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + +@Entity(name = "CAR") +public class Car { + private String model; + private String description; + private int year; + private String engine; + private Manufacturer manufacturer; + + @Id + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getEngine() { + return engine; + } + + public void setEngine(String engine) { + this.engine = engine; + } + + @OneToOne + @JoinColumn(name = "name") + public Manufacturer getManufacturer() { + return manufacturer; + } + + public void setManufacturer(Manufacturer manufacturer) { + this.manufacturer = manufacturer; + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java new file mode 100644 index 0000000000..f6e5fd23de --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/entities/Manufacturer.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.jinq.entities; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity(name = "MANUFACTURER") +public class Manufacturer { + + private String name; + private String city; + private List cars; + + @Id + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + @OneToMany(mappedBy = "model") + public List getCars() { + return cars; + } + + public void setCars(List cars) { + this.cars = cars; + } + +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java new file mode 100644 index 0000000000..42b81ecc59 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/BaseJinqRepositoryImpl.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.jinq.repositories; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.jinq.jpa.JPAJinqStream; +import org.jinq.jpa.JinqJPAStreamProvider; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class BaseJinqRepositoryImpl { + @Autowired + private JinqJPAStreamProvider jinqDataProvider; + + @PersistenceContext + private EntityManager entityManager; + + protected abstract Class entityType(); + + public JPAJinqStream stream() { + return streamOf(entityType()); + } + + protected JPAJinqStream streamOf(Class clazz) { + return jinqDataProvider.streamAll(entityManager, clazz); + } +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java new file mode 100644 index 0000000000..56f6106e08 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepository.java @@ -0,0 +1,27 @@ +package com.baeldung.spring.jinq.repositories; + +import java.util.List; +import java.util.Optional; + +import org.jinq.tuples.Pair; +import org.jinq.tuples.Tuple3; + +import com.baeldung.spring.jinq.entities.Car; +import com.baeldung.spring.jinq.entities.Manufacturer; + +public interface CarRepository { + + Optional findByModel(String model); + + List findByModelAndDescription(String model, String desc); + + List> findWithModelYearAndEngine(); + + Optional findManufacturerByModel(String model); + + List> findCarsPerManufacturer(); + + long countCarsByModel(String model); + + List findAll(int skip, int limit); +} diff --git a/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java new file mode 100644 index 0000000000..bf16c87461 --- /dev/null +++ b/spring-jinq/src/main/java/com/baeldung/spring/jinq/repositories/CarRepositoryImpl.java @@ -0,0 +1,72 @@ +package com.baeldung.spring.jinq.repositories; + +import java.util.List; +import java.util.Optional; + +import org.jinq.orm.stream.JinqStream; +import org.jinq.tuples.Pair; +import org.jinq.tuples.Tuple3; +import org.springframework.stereotype.Repository; + +import com.baeldung.spring.jinq.entities.Car; +import com.baeldung.spring.jinq.entities.Manufacturer; + +@Repository +public class CarRepositoryImpl extends BaseJinqRepositoryImpl implements CarRepository { + + @Override + public Optional findByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .findFirst(); + } + + @Override + public List findByModelAndDescription(String model, String desc) { + return stream().where(c -> c.getModel() + .equals(model) + && c.getDescription() + .contains(desc)) + .toList(); + } + + @Override + public List> findWithModelYearAndEngine() { + return stream().select(c -> new Tuple3<>(c.getModel(), c.getYear(), c.getEngine())) + .toList(); + } + + @Override + public Optional findManufacturerByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .select(c -> c.getManufacturer()) + .findFirst(); + } + + @Override + public List> findCarsPerManufacturer() { + return streamOf(Manufacturer.class).join(m -> JinqStream.from(m.getCars())) + .toList(); + } + + @Override + public long countCarsByModel(String model) { + return stream().where(c -> c.getModel() + .equals(model)) + .count(); + } + + @Override + public List findAll(int skip, int limit) { + return stream().skip(skip) + .limit(limit) + .toList(); + } + + @Override + protected Class entityType() { + return Car.class; + } + +} diff --git a/spring-jinq/src/main/resources/application.properties b/spring-jinq/src/main/resources/application.properties new file mode 100644 index 0000000000..dc73bed0c5 --- /dev/null +++ b/spring-jinq/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:h2:~/jinq +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true \ No newline at end of file diff --git a/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java b/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java new file mode 100644 index 0000000000..9cb126cbaa --- /dev/null +++ b/spring-jinq/src/test/java/com/baeldung/spring/jinq/repositories/CarRepositoryIntegrationTest.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.jinq.repositories; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.jinq.JinqApplication; + +@ContextConfiguration(classes = JinqApplication.class) +@RunWith(SpringJUnit4ClassRunner.class) +public class CarRepositoryIntegrationTest { + + @Autowired + private CarRepository repository; + + @Test + public void givenACar_whenFilter_thenShouldBeFound() { + assertThat(repository.findByModel("model1") + .isPresent()).isFalse(); + } + + @Test + public void givenACar_whenMultipleFilters_thenShouldBeFound() { + assertThat(repository.findByModelAndDescription("model1", "desc") + .isEmpty()).isTrue(); + } + + @Test + public void whenUseASelectClause() { + assertThat(repository.findWithModelYearAndEngine() + .isEmpty()).isTrue(); + } + + @Test + public void whenUsingOneToOneRelationship() { + assertThat(repository.findManufacturerByModel("model1")).isNotNull(); + } +} diff --git a/spring-mvc-simple/README.md b/spring-mvc-simple/README.md index 197a22cbac..69a9027280 100644 --- a/spring-mvc-simple/README.md +++ b/spring-mvc-simple/README.md @@ -2,3 +2,4 @@ - [HandlerAdapters in Spring MVC](http://www.baeldung.com/spring-mvc-handler-adapters) - [Template Engines for Spring](http://www.baeldung.com/spring-template-engines) +- [Spring 5 and Servlet 4 – The PushBuilder](http://www.baeldung.com/spring-5-push) diff --git a/spring-mvc-simple/pom.xml b/spring-mvc-simple/pom.xml index 05b2eb49b6..595e58f5f3 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -30,6 +30,7 @@ 5.0.2 5.0.2 1.0.2 + 1.9.0 @@ -115,6 +116,11 @@ ${junit.jupiter.version} test + + com.rometools + rome + ${rome.version} + diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java new file mode 100644 index 0000000000..cfbd33cd57 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java @@ -0,0 +1,54 @@ +package com.baeldung.spring.controller.rss; + +import com.rometools.rome.feed.rss.Channel; +import com.rometools.rome.feed.rss.Description; +import com.rometools.rome.feed.rss.Item; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.view.feed.AbstractRssFeedView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service("articleFeedView") +public class ArticleFeedView extends AbstractRssFeedView { + + protected Channel newFeed() { + Channel channel = new Channel("rss_2.0"); + channel.setLink("http://localhost:8080/spring-mvc-simple/rss"); + channel.setTitle("Article Feed"); + channel.setDescription("Article Feed Description"); + channel.setPubDate(new Date()); + return channel; + } + + @Override + protected List buildFeedItems(Map map, HttpServletRequest httpStRequest, HttpServletResponse httpStResponse) throws Exception { + List list = new ArrayList(); + + Item item1 = new Item(); + item1.setLink("http://www.baeldung.com/netty-exception-handling"); + item1.setTitle("Exceptions in Netty"); + Description description1 = new Description(); + description1.setValue("In this quick article, we’ll be looking at exception handling in Netty."); + item1.setDescription(description1); + item1.setPubDate(new Date()); + item1.setAuthor("Carlos"); + + Item item2 = new Item(); + item2.setLink("http://www.baeldung.com/cockroachdb-java"); + item2.setTitle("Guide to CockroachDB in Java"); + Description description2 = new Description(); + description2.setValue("This tutorial is an introductory guide to using CockroachDB with Java."); + item2.setDescription(description2); + item2.setPubDate(new Date()); + item2.setAuthor("Baeldung"); + + list.add(item1); + list.add(item2); + return list; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java new file mode 100644 index 0000000000..1f51b238ac --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/rss/ArticleRssController.java @@ -0,0 +1,14 @@ +package com.baeldung.spring.controller.rss; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class ArticleRssController { + + @GetMapping(value = "/rss", produces = "application/*") + public String articleFeed() { + return "articleFeedView"; + } + +} diff --git a/testing-modules/testing/pom.xml b/testing-modules/testing/pom.xml index 1fd6357b87..c76045380b 100644 --- a/testing-modules/testing/pom.xml +++ b/testing-modules/testing/pom.xml @@ -154,6 +154,16 @@ + + org.assertj + assertj-assertions-generator-maven-plugin + ${assertj-generator.version} + + + com.baeldung.testing.assertj.custom.Person + + + @@ -164,6 +174,7 @@ 21.0 3.1.0 3.6.1 + 2.1.0 0.32 1.1.0 0.12 diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java new file mode 100644 index 0000000000..a0b3d0daac --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/Member.java @@ -0,0 +1,19 @@ +package com.baeldung.testing.assertj; + +public class Member { + private String name; + private int age; + + public Member(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } +} diff --git a/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java new file mode 100644 index 0000000000..34afc480e4 --- /dev/null +++ b/testing-modules/testing/src/main/java/com/baeldung/testing/assertj/custom/Person.java @@ -0,0 +1,32 @@ +package com.baeldung.testing.assertj.custom; + +import java.util.ArrayList; +import java.util.List; + +public class Person { + private String fullName; + private int age; + private List nicknames; + + public Person(String fullName, int age) { + this.fullName = fullName; + this.age = age; + this.nicknames = new ArrayList<>(); + } + + public void addNickname(String nickname) { + nicknames.add(nickname); + } + + public String getFullName() { + return fullName; + } + + public int getAge() { + return age; + } + + public List getNicknames() { + return nicknames; + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java new file mode 100644 index 0000000000..153af828f1 --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/AssertJConditionUnitTest.java @@ -0,0 +1,72 @@ +package com.baeldung.testing.assertj; + +import static org.assertj.core.api.Assertions.allOf; +import static org.assertj.core.api.Assertions.anyOf; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.not; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.assertj.core.api.Condition; +import org.junit.Test; + +public class AssertJConditionUnitTest { + private Condition senior = new Condition<>(m -> m.getAge() >= 60, "senior"); + private Condition nameJohn = new Condition<>(m -> m.getName().equalsIgnoreCase("John"), "name John"); + + @Test + public void whenUsingMemberAgeCondition_thenCorrect() { + Member member = new Member("John", 65); + assertThat(member).is(senior); + + try { + assertThat(member).isNot(senior); + fail(); + } catch (AssertionError e) { + assertThat(e).hasMessageContaining("not to be "); + } + } + + @Test + public void whenUsingMemberNameCondition_thenCorrect() { + Member member = new Member("Jane", 60); + assertThat(member).doesNotHave(nameJohn); + + try { + assertThat(member).has(nameJohn); + fail(); + } catch (AssertionError e) { + assertThat(e).hasMessageContaining("to have:\n "); + } + } + + @Test + public void whenCollectionConditionsAreSatisfied_thenCorrect() { + List members = new ArrayList<>(); + members.add(new Member("Alice", 50)); + members.add(new Member("Bob", 60)); + + assertThat(members).haveExactly(1, senior); + assertThat(members).doNotHave(nameJohn); + } + + @Test + public void whenCombiningAllOfConditions_thenCorrect() { + Member john = new Member("John", 60); + Member jane = new Member("Jane", 50); + + assertThat(john).is(allOf(senior, nameJohn)); + assertThat(jane).is(allOf(not(nameJohn), not(senior))); + } + + @Test + public void whenCombiningAnyOfConditions_thenCorrect() { + Member john = new Member("John", 50); + Member jane = new Member("Jane", 60); + + assertThat(john).is(anyOf(senior, nameJohn)); + assertThat(jane).is(anyOf(nameJohn, senior)); + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java new file mode 100644 index 0000000000..4c09311bac --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/AssertJCustomAssertionsUnitTest.java @@ -0,0 +1,44 @@ +package com.baeldung.testing.assertj.custom; + +import static com.baeldung.testing.assertj.custom.Assertions.assertThat; +import static org.junit.Assert.fail; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class AssertJCustomAssertionsUnitTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void whenPersonNameMatches_thenCorrect() { + Person person = new Person("John Doe", 20); + assertThat(person).hasFullName("John Doe"); + } + + @Test + public void whenPersonAgeLessThanEighteen_thenNotAdult() { + Person person = new Person("Jane Roe", 16); + + try { + assertThat(person).isAdult(); + fail(); + } catch (AssertionError e) { + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected person to be adult"); + } + } + + @Test + public void whenPersonDoesNotHaveAMatchingNickname_thenIncorrect() { + Person person = new Person("John Doe", 20); + person.addNickname("Nick"); + + try { + assertThat(person).hasNickname("John"); + fail(); + } catch (AssertionError e) { + org.assertj.core.api.Assertions.assertThat(e).hasMessage("Expected person to have nickname John"); + } + } +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java new file mode 100644 index 0000000000..fcffb8fc6c --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/Assertions.java @@ -0,0 +1,9 @@ +package com.baeldung.testing.assertj.custom; + +public class Assertions { + public static PersonAssert assertThat(Person actual) { + return new PersonAssert(actual); + } + + // static factory methods of other assertion classes +} diff --git a/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java new file mode 100644 index 0000000000..d6cc585e96 --- /dev/null +++ b/testing-modules/testing/src/test/java/com/baeldung/testing/assertj/custom/PersonAssert.java @@ -0,0 +1,38 @@ +package com.baeldung.testing.assertj.custom; + +import org.assertj.core.api.AbstractAssert; + +public class PersonAssert extends AbstractAssert { + + public PersonAssert(Person actual) { + super(actual, PersonAssert.class); + } + + public static PersonAssert assertThat(Person actual) { + return new PersonAssert(actual); + } + + public PersonAssert hasFullName(String fullName) { + isNotNull(); + if (!actual.getFullName().equals(fullName)) { + failWithMessage("Expected person to have full name %s but was %s", fullName, actual.getFullName()); + } + return this; + } + + public PersonAssert isAdult() { + isNotNull(); + if (actual.getAge() < 18) { + failWithMessage("Expected person to be adult"); + } + return this; + } + + public PersonAssert hasNickname(String nickName) { + isNotNull(); + if (!actual.getNicknames().contains(nickName)) { + failWithMessage("Expected person to have nickname %s", nickName); + } + return this; + } +} diff --git a/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java new file mode 100644 index 0000000000..f4e0728f32 --- /dev/null +++ b/vavr/src/main/java/com/baeldung/samples/java/vavr/VavrSampler.java @@ -0,0 +1,99 @@ +package com.baeldung.samples.java.vavr; + +import java.util.ArrayList; +import java.util.List; + +import io.vavr.collection.Stream; + +/** + * + * @author baeldung + */ +public class VavrSampler { + + static int[] intArray = new int[] { 1, 2, 4 }; + static List intList = new ArrayList<>(); + static int[][] intOfInts = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + + public static void main(String[] args) { + vavrStreamElementAccess(); + System.out.println("===================================="); + vavrParallelStreamAccess(); + System.out.println("===================================="); + vavrFlatMapping(); + System.out.println("===================================="); + vavrStreamManipulation(); + System.out.println("===================================="); + vavrStreamDistinct(); + } + + public static void vavrStreamElementAccess() { + System.out.println("Vavr Element Access"); + System.out.println("===================================="); + Stream vavredStream = Stream.ofAll(intArray); + System.out.println("Vavr index access: " + vavredStream.get(2)); + System.out.println("Vavr head element access: " + vavredStream.get()); + + Stream vavredStringStream = Stream.of("foo", "bar", "baz"); + System.out.println("Find foo " + vavredStringStream.indexOf("foo")); + } + + public static void vavrParallelStreamAccess() { + + System.out.println("Vavr Stream Concurrent Modification"); + System.out.println("===================================="); + Stream vavrStream = Stream.ofAll(intList); + // intList.add(5); + vavrStream.forEach(i -> System.out.println("in a Vavr Stream: " + i)); + + // Stream wrapped = Stream.ofAll(intArray); + // intArray[2] = 5; + // wrapped.forEach(i -> System.out.println("Vavr looped " + i)); + } + + public static void jdkFlatMapping() { + System.out.println("Java flatMapping"); + System.out.println("===================================="); + java.util.stream.Stream.of(42).flatMap(i -> java.util.stream.Stream.generate(() -> { + System.out.println("nested call"); + return 42; + })).findAny(); + } + + public static void vavrFlatMapping() { + System.out.println("Vavr flatMapping"); + System.out.println("===================================="); + Stream.of(42) + .flatMap(i -> Stream.continually(() -> { + System.out.println("nested call"); + return 42; + })) + .get(0); + } + + public static void vavrStreamManipulation() { + System.out.println("Vavr Stream Manipulation"); + System.out.println("===================================="); + List stringList = new ArrayList<>(); + stringList.add("foo"); + stringList.add("bar"); + stringList.add("baz"); + Stream vavredStream = Stream.ofAll(stringList); + vavredStream.forEach(item -> System.out.println("Vavr Stream item: " + item)); + Stream vavredStream2 = vavredStream.insert(2, "buzz"); + vavredStream2.forEach(item -> System.out.println("Vavr Stream item after stream addition: " + item)); + stringList.forEach(item -> System.out.println("List item after stream addition: " + item)); + Stream deletionStream = vavredStream.remove("bar"); + deletionStream.forEach(item -> System.out.println("Vavr Stream item after stream item deletion: " + item)); + + } + + public static void vavrStreamDistinct() { + Stream vavredStream = Stream.of("foo", "bar", "baz", "buxx", "bar", "bar", "foo"); + Stream distinctVavrStream = vavredStream.distinctBy((y, z) -> { + return y.compareTo(z); + }); + distinctVavrStream.forEach(item -> System.out.println("Vavr Stream item after distinct query " + item)); + + } +}