diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Board.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Board.java new file mode 100644 index 0000000000..ddb5901682 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Board.java @@ -0,0 +1,208 @@ +package com.baeldung.algorithms.play2048; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Board { + private static final Logger LOG = LoggerFactory.getLogger(Board.class); + + private final int[][] board; + + private final int score; + + public Board(int size) { + assert(size > 0); + + this.board = new int[size][]; + this.score = 0; + + for (int x = 0; x < size; ++x) { + this.board[x] = new int[size]; + for (int y = 0; y < size; ++y) { + board[x][y] = 0; + } + } + } + + private Board(int[][] board, int score) { + this.score = score; + this.board = new int[board.length][]; + + for (int x = 0; x < board.length; ++x) { + this.board[x] = Arrays.copyOf(board[x], board[x].length); + } + } + + public int getSize() { + return board.length; + } + + public int getScore() { + return score; + } + + public int getCell(Cell cell) { + int x = cell.getX(); + int y = cell.getY(); + assert(x >= 0 && x < board.length); + assert(y >= 0 && y < board.length); + + return board[x][y]; + } + + public boolean isEmpty(Cell cell) { + return getCell(cell) == 0; + } + + public List emptyCells() { + List result = new ArrayList<>(); + for (int x = 0; x < board.length; ++x) { + for (int y = 0; y < board[x].length; ++y) { + Cell cell = new Cell(x, y); + if (isEmpty(cell)) { + result.add(cell); + } + } + } + return result; + } + + public Board placeTile(Cell cell, int number) { + if (!isEmpty(cell)) { + throw new IllegalArgumentException("That cell is not empty"); + } + + Board result = new Board(this.board, this.score); + result.board[cell.getX()][cell.getY()] = number; + return result; + } + + public Board move(Move move) { + // Clone the board + int[][] tiles = new int[this.board.length][]; + for (int x = 0; x < this.board.length; ++x) { + tiles[x] = Arrays.copyOf(this.board[x], this.board[x].length); + } + + LOG.debug("Before move: {}", Arrays.deepToString(tiles)); + // If we're doing an Left/Right move then transpose the board to make it a Up/Down move + if (move == Move.LEFT || move == Move.RIGHT) { + tiles = transpose(tiles); + LOG.debug("After transpose: {}", Arrays.deepToString(tiles)); + } + // If we're doing a Right/Down move then reverse the board. + // With the above we're now always doing an Up move + if (move == Move.DOWN || move == Move.RIGHT) { + tiles = reverse(tiles); + LOG.debug("After reverse: {}", Arrays.deepToString(tiles)); + } + LOG.debug("Ready to move: {}", Arrays.deepToString(tiles)); + + // Shift everything up + int[][] result = new int[tiles.length][]; + int newScore = 0; + for (int x = 0; x < tiles.length; ++x) { + LinkedList thisRow = new LinkedList<>(); + for (int y = 0; y < tiles[0].length; ++y) { + if (tiles[x][y] > 0) { + thisRow.add(tiles[x][y]); + } + } + + LOG.debug("Unmerged row: {}", thisRow); + LinkedList newRow = new LinkedList<>(); + while (thisRow.size() >= 2) { + int first = thisRow.pop(); + int second = thisRow.peek(); + LOG.debug("Looking at numbers {} and {}", first, second); + if (second == first) { + LOG.debug("Numbers match, combining"); + int newNumber = first * 2; + newRow.add(newNumber); + newScore += newNumber; + thisRow.pop(); + } else { + LOG.debug("Numbers don't match"); + newRow.add(first); + } + } + newRow.addAll(thisRow); + LOG.debug("Merged row: {}", newRow); + + result[x] = new int[tiles[0].length]; + for (int y = 0; y < tiles[0].length; ++y) { + if (newRow.isEmpty()) { + result[x][y] = 0; + } else { + result[x][y] = newRow.pop(); + } + } + } + LOG.debug("After moves: {}", Arrays.deepToString(result)); + + // Un-reverse the board + if (move == Move.DOWN || move == Move.RIGHT) { + result = reverse(result); + LOG.debug("After reverse: {}", Arrays.deepToString(result)); + } + // Un-transpose the board + if (move == Move.LEFT || move == Move.RIGHT) { + result = transpose(result); + LOG.debug("After transpose: {}", Arrays.deepToString(result)); + } + return new Board(result, this.score + newScore); + } + + private static int[][] transpose(int[][] input) { + int[][] result = new int[input.length][]; + + for (int x = 0; x < input.length; ++x) { + result[x] = new int[input[0].length]; + for (int y = 0; y < input[0].length; ++y) { + result[x][y] = input[y][x]; + } + } + + return result; + } + + private static int[][] reverse(int[][] input) { + int[][] result = new int[input.length][]; + + for (int x = 0; x < input.length; ++x) { + result[x] = new int[input[0].length]; + for (int y = 0; y < input[0].length; ++y) { + result[x][y] = input[x][input.length - y - 1]; + } + } + + return result; + } + + @Override + public String toString() { + return Arrays.deepToString(board); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Board board1 = (Board) o; + return Arrays.deepEquals(board, board1.board); + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(board); + } +} diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Cell.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Cell.java new file mode 100644 index 0000000000..98ee23ac90 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Cell.java @@ -0,0 +1,26 @@ +package com.baeldung.algorithms.play2048; + +import java.util.StringJoiner; + +public class Cell { + private int x; + private int y; + + public Cell(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + @Override + public String toString() { + return new StringJoiner(", ", Cell.class.getSimpleName() + "[", "]").add("x=" + x).add("y=" + y).toString(); + } +} diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Computer.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Computer.java new file mode 100644 index 0000000000..5ef1d04368 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Computer.java @@ -0,0 +1,27 @@ +package com.baeldung.algorithms.play2048; + +import java.security.SecureRandom; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Computer { + private static final Logger LOG = LoggerFactory.getLogger(Computer.class); + + private final SecureRandom rng = new SecureRandom(); + + public Board makeMove(Board input) { + List emptyCells = input.emptyCells(); + LOG.info("Number of empty cells: {}", emptyCells.size()); + + double numberToPlace = rng.nextDouble(); + LOG.info("New number probability: {}", numberToPlace); + + int indexToPlace = rng.nextInt(emptyCells.size()); + Cell cellToPlace = emptyCells.get(indexToPlace); + LOG.info("Placing number into empty cell: {}", cellToPlace); + + return input.placeTile(cellToPlace, numberToPlace >= 0.9 ? 4 : 2); + } +} diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Human.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Human.java new file mode 100644 index 0000000000..d1b32f0309 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Human.java @@ -0,0 +1,126 @@ +package com.baeldung.algorithms.play2048; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.math3.util.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Human { + private static final Logger LOG = LoggerFactory.getLogger(Human.class); + + public Board makeMove(Board input) { + // For each move in MOVE + // Generate board from move + // Generate Score for Board + // Return board with the best score + // + // Generate Score + // If Depth Limit + // Return Final Score + // Total Score = 0 + // For every empty square in new board + // Generate board with "2" in square + // Calculate Score + // Total Score += (Score * 0.9) + // Generate board with "4" in square + // Calculate Score + // Total Score += (Score * 0.1) + // + // Calculate Score + // For each move in MOVE + // Generate board from move + // Generate score for board + // Return the best generated score + + return Arrays.stream(Move.values()) + .parallel() + .map(input::move) + .filter(board -> !board.equals(input)) + .max(Comparator.comparingInt(board -> generateScore(board, 0))) + .orElse(input); + } + + private int generateScore(Board board, int depth) { + if (depth >= 3) { + int finalScore = calculateFinalScore(board); + LOG.debug("Final score for board {}: {}", board,finalScore); + return finalScore; + } + + return board.emptyCells().stream() + .parallel() + .flatMap(cell -> Stream.of(new Pair<>(cell, 2), new Pair<>(cell, 4))) + .mapToInt(move -> { + LOG.debug("Simulating move {} at depth {}", move, depth); + Board newBoard = board.placeTile(move.getFirst(), move.getSecond()); + int boardScore = calculateScore(newBoard, depth + 1); + int calculatedScore = (int) (boardScore * (move.getSecond() == 2 ? 0.9 : 0.1)); + LOG.debug("Calculated score for board {} and move {} at depth {}: {}", newBoard, move, depth, calculatedScore); + return calculatedScore; + }) + .sum(); + } + + private int calculateScore(Board board, int depth) { + return Arrays.stream(Move.values()) + .parallel() + .map(board::move) + .filter(moved -> !moved.equals(board)) + .mapToInt(newBoard -> generateScore(newBoard, depth)) + .max() + .orElse(0); + } + + private int calculateFinalScore(Board board) { + List> rowsToScore = new ArrayList<>(); + for (int i = 0; i < board.getSize(); ++i) { + List row = new ArrayList<>(); + List col = new ArrayList<>(); + + for (int j = 0; j < board.getSize(); ++j) { + row.add(board.getCell(new Cell(i, j))); + col.add(board.getCell(new Cell(j, i))); + } + + rowsToScore.add(row); + rowsToScore.add(col); + } + + return rowsToScore.stream() + .parallel() + .mapToInt(row -> { + List preMerged = row.stream() + .filter(value -> value != 0) + .collect(Collectors.toList()); + + int numMerges = 0; + int monotonicityLeft = 0; + int monotonicityRight = 0; + for (int i = 0; i < preMerged.size() - 1; ++i) { + Integer first = preMerged.get(i); + Integer second = preMerged.get(i + 1); + if (first.equals(second)) { + ++numMerges; + } else if (first > second) { + monotonicityLeft += first - second; + } else { + monotonicityRight += second - first; + } + } + + int score = 1000; + score += 250 * row.stream().filter(value -> value == 0).count(); + score += 750 * numMerges; + score -= 10 * row.stream().mapToInt(value -> value).sum(); + score -= 50 * Math.min(monotonicityLeft, monotonicityRight); + return score; + }) + .sum(); + } +} diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Move.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Move.java new file mode 100644 index 0000000000..8678cec833 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Move.java @@ -0,0 +1,8 @@ +package com.baeldung.algorithms.play2048; + +public enum Move { + UP, + DOWN, + LEFT, + RIGHT +} diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Play2048.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Play2048.java new file mode 100644 index 0000000000..ec5b3dca40 --- /dev/null +++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/play2048/Play2048.java @@ -0,0 +1,73 @@ +package com.baeldung.algorithms.play2048; + +public class Play2048 { + private static final int SIZE = 3; + private static final int INITIAL_NUMBERS = 2; + + public static void main(String[] args) { + // The board and players + Board board = new Board(SIZE); + Computer computer = new Computer(); + Human human = new Human(); + + // The computer has two moves first + System.out.println("Setup"); + System.out.println("====="); + for (int i = 0; i < INITIAL_NUMBERS; ++i) { + board = computer.makeMove(board); + } + + printBoard(board); + do { + board = human.makeMove(board); + System.out.println("Human move"); + System.out.println("=========="); + printBoard(board); + + board = computer.makeMove(board); + System.out.println("Computer move"); + System.out.println("============="); + printBoard(board); + } while (!board.emptyCells().isEmpty()); + + System.out.println("Final Score: " + board.getScore()); + + } + + private static void printBoard(Board board) { + StringBuilder topLines = new StringBuilder(); + StringBuilder midLines = new StringBuilder(); + for (int x = 0; x < board.getSize(); ++x) { + topLines.append("+--------"); + midLines.append("| "); + } + topLines.append("+"); + midLines.append("|"); + + + for (int y = 0; y < board.getSize(); ++y) { + System.out.println(topLines); + System.out.println(midLines); + for (int x = 0; x < board.getSize(); ++x) { + Cell cell = new Cell(x, y); + System.out.print("|"); + if (board.isEmpty(cell)) { + System.out.print(" "); + } else { + StringBuilder output = new StringBuilder(Integer.toString(board.getCell(cell))); + while (output.length() < 8) { + output.append(" "); + if (output.length() < 8) { + output.insert(0, " "); + } + } + System.out.print(output); + } + } + System.out.println("|"); + System.out.println(midLines); + } + System.out.println(topLines); + System.out.println("Score: " + board.getScore()); + } +} diff --git a/aws-app-sync/README.md b/aws-app-sync/README.md new file mode 100644 index 0000000000..976a999f40 --- /dev/null +++ b/aws-app-sync/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [AWS AppSync With Spring Boot](https://www.baeldung.com/aws-appsync-spring) diff --git a/core-groovy-2/src/main/groovy/com/baeldung/determinedatatype/Person.groovy b/core-groovy-2/src/main/groovy/com/baeldung/determinedatatype/Person.groovy new file mode 100644 index 0000000000..3ac88b7952 --- /dev/null +++ b/core-groovy-2/src/main/groovy/com/baeldung/determinedatatype/Person.groovy @@ -0,0 +1,14 @@ +package com.baeldung.determinedatatype + +class Person { + + private int ageAsInt + private Double ageAsDouble + private String ageAsString + + Person() {} + Person(int ageAsInt) { this.ageAsInt = ageAsInt} + Person(Double ageAsDouble) { this.ageAsDouble = ageAsDouble} + Person(String ageAsString) { this.ageAsString = ageAsString} +} +class Student extends Person {} diff --git a/core-groovy-2/src/test/groovy/com/baeldung/determinedatatype/PersonTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/determinedatatype/PersonTest.groovy new file mode 100644 index 0000000000..4c6589f207 --- /dev/null +++ b/core-groovy-2/src/test/groovy/com/baeldung/determinedatatype/PersonTest.groovy @@ -0,0 +1,56 @@ +package com.baeldung.determinedatatype + +import org.junit.Assert +import org.junit.Test +import com.baeldung.determinedatatype.Person + +public class PersonTest { + + @Test + public void givenWhenParameterTypeIsInteger_thenReturnTrue() { + Person personObj = new Person(10) + Assert.assertTrue(personObj.ageAsInt instanceof Integer) + } + + @Test + public void givenWhenParameterTypeIsDouble_thenReturnTrue() { + Person personObj = new Person(10.0) + Assert.assertTrue((personObj.ageAsDouble).getClass() == Double) + } + + @Test + public void givenWhenParameterTypeIsString_thenReturnTrue() { + Person personObj = new Person("10 years") + Assert.assertTrue(personObj.ageAsString.class == String) + } + + @Test + public void givenClassName_WhenParameterIsInteger_thenReturnTrue() { + Assert.assertTrue(Person.class.getDeclaredField('ageAsInt').type == int.class) + } + + @Test + public void givenWhenObjectIsInstanceOfType_thenReturnTrue() { + Person personObj = new Person() + Assert.assertTrue(personObj instanceof Person) + } + + @Test + public void givenWhenInstanceIsOfSubtype_thenReturnTrue() { + Student studentObj = new Student() + Assert.assertTrue(studentObj in Person) + } + + @Test + public void givenGroovyList_WhenFindClassName_thenReturnTrue() { + def ageList = ['ageAsString','ageAsDouble', 10] + Assert.assertTrue(ageList.class == ArrayList) + Assert.assertTrue(ageList.getClass() == ArrayList) + } + + @Test + public void givenGrooyMap_WhenFindClassName_thenReturnTrue() { + def ageMap = [ageAsString: '10 years', ageAsDouble: 10.0] + Assert.assertFalse(ageMap.class == LinkedHashMap) + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java b/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java index 33243c4ecf..7ae2af0857 100644 --- a/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java +++ b/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java @@ -4,19 +4,19 @@ import java.util.Objects; public record Person (String name, String address) { - public static String UNKWOWN_ADDRESS = "Unknown"; - public static String UNNAMED = "Unnamed"; + public static String UNKNOWN_ADDRESS = "Unknown"; + public static String UNNAMED = "Unnamed"; - public Person { - Objects.requireNonNull(name); - Objects.requireNonNull(address); - } + public Person { + Objects.requireNonNull(name); + Objects.requireNonNull(address); + } - public Person(String name) { - this(name, UNKWOWN_ADDRESS); - } + public Person(String name) { + this(name, UNKNOWN_ADDRESS); + } - public static Person unnamed(String address) { - return new Person(UNNAMED, address); - } -} + public static Person unnamed(String address) { + return new Person(UNNAMED, address); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java index 4a7d4ede5f..9bed3dab8f 100644 --- a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java +++ b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java @@ -134,7 +134,7 @@ public class PersonTest { Person person = new Person(name); assertEquals(name, person.name()); - assertEquals(Person.UNKWOWN_ADDRESS, person.address()); + assertEquals(Person.UNKNOWN_ADDRESS, person.address()); } @Test @@ -147,4 +147,4 @@ public class PersonTest { assertEquals(Person.UNNAMED, person.name()); assertEquals(address, person.address()); } -} +} \ No newline at end of file diff --git a/core-java-modules/core-java-8-datetime-2/pom.xml b/core-java-modules/core-java-8-datetime-2/pom.xml index 629ce5234d..f66a89ca55 100644 --- a/core-java-modules/core-java-8-datetime-2/pom.xml +++ b/core-java-modules/core-java-8-datetime-2/pom.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - core-java-8-datetime + core-java-8-datetime-2 ${project.parent.version} core-java-8-datetime jar @@ -41,7 +41,6 @@ - core-java-datetime-java8 src/main/resources diff --git a/core-java-modules/core-java-9-new-features/README.md b/core-java-modules/core-java-9-new-features/README.md index d547b9a221..c2ef63a530 100644 --- a/core-java-modules/core-java-9-new-features/README.md +++ b/core-java-modules/core-java-9-new-features/README.md @@ -12,3 +12,4 @@ This module contains articles about core Java features that have been introduced - [Introduction to Java 9 StackWalking API](https://www.baeldung.com/java-9-stackwalking-api) - [Java 9 Platform Logging API](https://www.baeldung.com/java-9-logging-api) - [Java 9 Reactive Streams](https://www.baeldung.com/java-9-reactive-streams) +- [Multi-Release JAR Files with Maven](https://www.baeldung.com/maven-multi-release-jars) diff --git a/core-java-modules/core-java-9-new-features/pom.xml b/core-java-modules/core-java-9-new-features/pom.xml index b0fb6ab7f9..70fae73bd5 100644 --- a/core-java-modules/core-java-9-new-features/pom.xml +++ b/core-java-modules/core-java-9-new-features/pom.xml @@ -28,8 +28,104 @@ ${junit.platform.version} test + + org.awaitility + awaitility + ${awaitility.version} + test + - + + + incubator-features + + core-java-9-new-features + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + --add-modules=jdk.incubator.httpclient + + + + maven-surefire-plugin + + --add-modules=jdk.incubator.httpclient + + + + + + + mrjar-generation + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile-java-8 + + compile + + + 1.8 + 1.8 + + ${project.basedir}/src/main/java8 + + + + + compile-java-9 + compile + + compile + + + 9 + + ${project.basedir}/src/main/java9 + + ${project.build.outputDirectory}/META-INF/versions/9 + + + + default-testCompile + test-compile + + testCompile + + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + true + + + com.baeldung.multireleaseapp.App + + + + + + + + core-java-9-new-features @@ -56,8 +152,10 @@ 3.10.0 1.2.0 + 4.0.2 1.9 1.9 + 3.2.0 diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java new file mode 100644 index 0000000000..cc00223e05 --- /dev/null +++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java @@ -0,0 +1,14 @@ +package com.baeldung.multireleaseapp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class App { + + private static final Logger logger = LoggerFactory.getLogger(App.class); + + public static void main(String[] args) { + logger.info(String.format("Running on %s", new DefaultVersion().version())); + } + +} diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java new file mode 100644 index 0000000000..b24be606de --- /dev/null +++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java @@ -0,0 +1,9 @@ +package com.baeldung.multireleaseapp; + +public class DefaultVersion implements Version { + + @Override + public String version() { + return System.getProperty("java.version"); + } +} diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java new file mode 100644 index 0000000000..ef95f08205 --- /dev/null +++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java @@ -0,0 +1,5 @@ +package com.baeldung.multireleaseapp; + +interface Version { + public String version(); +} \ No newline at end of file diff --git a/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java b/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java new file mode 100644 index 0000000000..0842f578dd --- /dev/null +++ b/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java @@ -0,0 +1,9 @@ +package com.baeldung.multireleaseapp; + +public class DefaultVersion implements Version { + + @Override + public String version() { + return Runtime.version().toString(); + } +} diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java similarity index 98% rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java index 5cf3b9098f..fc59ae8d8d 100644 --- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java +++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java @@ -24,7 +24,7 @@ import static org.junit.Assert.assertThat; /** * Created by adam. */ -public class HttpClientTest { +public class HttpClientIntegrationTest { @Test public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException { @@ -55,7 +55,7 @@ public class HttpClientTest { .send(request, HttpResponse.BodyHandler.asString()); assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM)); - assertThat(response.body(), containsString("https://stackoverflow.com/")); + assertThat(response.body(), containsString("")); } @Test diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java similarity index 99% rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java index 7c0e9a90e0..17af7bd8ba 100644 --- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java +++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat; /** * Created by adam. */ -public class HttpRequestTest { +public class HttpRequestIntegrationTest { @Test public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java similarity index 97% rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java index 80295ff34c..5c6f9c8a52 100644 --- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java +++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java @@ -18,7 +18,7 @@ import static org.junit.Assert.assertThat; /** * Created by adam. */ -public class HttpResponseTest { +public class HttpResponseIntegrationTest { @Test public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException { diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java similarity index 86% rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java index 647557532d..92cdc1c074 100644 --- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java +++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java @@ -5,10 +5,12 @@ import org.junit.Test; import java.util.List; import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; -public class ReactiveStreamsTest { +public class ReactiveStreamsUnitTest { @Test public void givenPublisher_whenSubscribeToIt_thenShouldConsumeAllElements() throws InterruptedException { @@ -25,7 +27,7 @@ public class ReactiveStreamsTest { //then - await().atMost(1000, TimeUnit.MILLISECONDS).until( + await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted( () -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(items) ); } @@ -46,7 +48,7 @@ public class ReactiveStreamsTest { publisher.close(); //then - await().atMost(1000, TimeUnit.MILLISECONDS).until( + await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted( () -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expectedResult) ); } @@ -66,7 +68,7 @@ public class ReactiveStreamsTest { publisher.close(); //then - await().atMost(1000, TimeUnit.MILLISECONDS).until( + await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted( () -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expected) ); } diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java deleted file mode 100644 index 50766502ec..0000000000 --- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.baeldung.java9.varhandles; - -import org.junit.Test; - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; - -import static org.assertj.core.api.Assertions.assertThat; - -public class VariableHandlesTest { - - public int publicTestVariable = 1; - private int privateTestVariable = 1; - public int variableToSet = 1; - public int variableToCompareAndSet = 1; - public int variableToGetAndAdd = 0; - public byte variableToBitwiseOr = 0; - - @Test - public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class); - - assertThat(publicIntHandle.coordinateTypes().size() == 1); - assertThat(publicIntHandle.coordinateTypes().get(0) == VariableHandles.class); - - } - - @Test - public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { - VarHandle privateIntHandle = MethodHandles - .privateLookupIn(VariableHandlesTest.class, MethodHandles.lookup()) - .findVarHandle(VariableHandlesTest.class, "privateTestVariable", int.class); - - assertThat(privateIntHandle.coordinateTypes().size() == 1); - assertThat(privateIntHandle.coordinateTypes().get(0) == VariableHandlesTest.class); - - } - - @Test - public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { - VarHandle arrayVarHandle = MethodHandles - .arrayElementVarHandle(int[].class); - - assertThat(arrayVarHandle.coordinateTypes().size() == 2); - assertThat(arrayVarHandle.coordinateTypes().get(0) == int[].class); - } - - @Test - public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class); - - assertThat((int) publicIntHandle.get(this) == 1); - } - - @Test - public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "variableToSet", int.class); - publicIntHandle.set(this, 15); - - assertThat((int) publicIntHandle.get(this) == 15); - } - - @Test - public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "variableToCompareAndSet", int.class); - publicIntHandle.compareAndSet(this, 1, 100); - - assertThat((int) publicIntHandle.get(this) == 100); - } - - @Test - public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "variableToGetAndAdd", int.class); - int before = (int) publicIntHandle.getAndAdd(this, 200); - - assertThat(before == 0); - assertThat((int) publicIntHandle.get(this) == 200); - } - - @Test - public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { - VarHandle publicIntHandle = MethodHandles - .lookup() - .in(VariableHandlesTest.class) - .findVarHandle(VariableHandlesTest.class, "variableToBitwiseOr", byte.class); - byte before = (byte) publicIntHandle.getAndBitwiseOr(this, (byte) 127); - - assertThat(before == 0); - assertThat(variableToBitwiseOr == 127); - } -} diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java new file mode 100644 index 0000000000..a7263f0bb2 --- /dev/null +++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java @@ -0,0 +1,105 @@ +package com.baeldung.java9.varhandles; + +import org.junit.Test; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +import static org.junit.Assert.assertEquals; + +public class VariableHandlesUnitTest { + + public int publicTestVariable = 1; + private int privateTestVariable = 1; + public int variableToSet = 1; + public int variableToCompareAndSet = 1; + public int variableToGetAndAdd = 0; + public byte variableToBitwiseOr = 0; + + @Test + public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { + VarHandle PUBLIC_TEST_VARIABLE = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class); + + assertEquals(1, PUBLIC_TEST_VARIABLE.coordinateTypes().size()); + assertEquals(VariableHandlesUnitTest.class, PUBLIC_TEST_VARIABLE.coordinateTypes().get(0)); + } + + @Test + public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { + VarHandle PRIVATE_TEST_VARIABLE = MethodHandles + .privateLookupIn(VariableHandlesUnitTest.class, MethodHandles.lookup()) + .findVarHandle(VariableHandlesUnitTest.class, "privateTestVariable", int.class); + + assertEquals(1, PRIVATE_TEST_VARIABLE.coordinateTypes().size()); + assertEquals(VariableHandlesUnitTest.class, PRIVATE_TEST_VARIABLE.coordinateTypes().get(0)); + } + + @Test + public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException { + VarHandle arrayVarHandle = MethodHandles + .arrayElementVarHandle(int[].class); + + assertEquals(2, arrayVarHandle.coordinateTypes().size()); + assertEquals(int[].class, arrayVarHandle.coordinateTypes().get(0)); + } + + @Test + public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException { + VarHandle PUBLIC_TEST_VARIABLE = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class); + + assertEquals(1, (int) PUBLIC_TEST_VARIABLE.get(this)); + } + + @Test + public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { + VarHandle VARIABLE_TO_SET = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "variableToSet", int.class); + + VARIABLE_TO_SET.set(this, 15); + assertEquals(15, (int) VARIABLE_TO_SET.get(this)); + } + + @Test + public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { + VarHandle VARIABLE_TO_COMPARE_AND_SET = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "variableToCompareAndSet", int.class); + + VARIABLE_TO_COMPARE_AND_SET.compareAndSet(this, 1, 100); + assertEquals(100, (int) VARIABLE_TO_COMPARE_AND_SET.get(this)); + } + + @Test + public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { + VarHandle VARIABLE_TO_GET_AND_ADD = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "variableToGetAndAdd", int.class); + + int before = (int) VARIABLE_TO_GET_AND_ADD.getAndAdd(this, 200); + + assertEquals(0, before); + assertEquals(200, (int) VARIABLE_TO_GET_AND_ADD.get(this)); + } + + @Test + public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException { + VarHandle VARIABLE_TO_BITWISE_OR = MethodHandles + .lookup() + .in(VariableHandlesUnitTest.class) + .findVarHandle(VariableHandlesUnitTest.class, "variableToBitwiseOr", byte.class); + byte before = (byte) VARIABLE_TO_BITWISE_OR.getAndBitwiseOr(this, (byte) 127); + + assertEquals(0, before); + assertEquals(127, (byte) VARIABLE_TO_BITWISE_OR.get(this)); + } +} diff --git a/core-java-modules/core-java-arrays-guides/src/test/java/com/baeldung/arrays/JavaArraysToStringUnitTest.java b/core-java-modules/core-java-arrays-guides/src/test/java/com/baeldung/arrays/JavaArraysToStringUnitTest.java new file mode 100644 index 0000000000..064803465d --- /dev/null +++ b/core-java-modules/core-java-arrays-guides/src/test/java/com/baeldung/arrays/JavaArraysToStringUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.arrays; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JavaArraysToStringUnitTest { + + @Test + public void givenInstanceOfArray_whenTryingToConvertToString_thenNameOfClassIsShown() { + Object[] arrayOfObjects = { "John", 2, true }; + assertTrue(arrayOfObjects.toString().startsWith("[Ljava.lang.Object;")); + } + + @Test + public void givenInstanceOfArray_whenUsingArraysToStringToConvert_thenValueOfObjectsAreShown() { + Object[] arrayOfObjects = { "John", 2, true }; + assertEquals(Arrays.toString(arrayOfObjects), "[John, 2, true]"); + } + + @Test + public void givenInstanceOfDeepArray_whenUsingArraysDeepToStringToConvert_thenValueOfInnerObjectsAreShown() { + Object[] innerArray = { "We", "Are", "Inside" }; + Object[] arrayOfObjects = { "John", 2, innerArray }; + assertEquals(Arrays.deepToString(arrayOfObjects), "[John, 2, [We, Are, Inside]]"); + } + + @Test + public void givenInstanceOfDeepArray_whenUsingStreamsToConvert_thenValueOfObjectsAreShown() { + Object[] arrayOfObjects = { "John", 2, true }; + List listOfString = Stream.of(arrayOfObjects) + .map(Object::toString) + .collect(Collectors.toList()); + assertEquals(listOfString.toString(), "[John, 2, true]"); + } +} diff --git a/core-java-modules/core-java-collections-3/README.md b/core-java-modules/core-java-collections-3/README.md index 9218384640..fb983d9abc 100644 --- a/core-java-modules/core-java-collections-3/README.md +++ b/core-java-modules/core-java-collections-3/README.md @@ -9,3 +9,4 @@ - [Differences Between Collection.clear() and Collection.removeAll()](https://www.baeldung.com/java-collection-clear-vs-removeall) - [Performance of contains() in a HashSet vs ArrayList](https://www.baeldung.com/java-hashset-arraylist-contains-performance) - [Fail-Safe Iterator vs Fail-Fast Iterator](https://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator) +- [Quick Guide to the Java Stack](https://www.baeldung.com/java-stack) diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/stack/StackUnitTest.java b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/stack/StackUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/stack/StackUnitTest.java rename to core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/stack/StackUnitTest.java diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java new file mode 100644 index 0000000000..ee1bdcd55b --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java @@ -0,0 +1,66 @@ +package com.baeldung.abaproblem; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class Account { + + private AtomicInteger balance; + private AtomicInteger transactionCount; + private ThreadLocal currentThreadCASFailureCount; + + public Account() { + this.balance = new AtomicInteger(0); + this.transactionCount = new AtomicInteger(0); + this.currentThreadCASFailureCount = new ThreadLocal<>(); + this.currentThreadCASFailureCount.set(0); + } + + public int getBalance() { + return balance.get(); + } + + public int getTransactionCount() { + return transactionCount.get(); + } + + public int getCurrentThreadCASFailureCount() { + return currentThreadCASFailureCount.get(); + } + + public boolean withdraw(int amount) { + int current = getBalance(); + maybeWait(); + boolean result = balance.compareAndSet(current, current - amount); + if (result) { + transactionCount.incrementAndGet(); + } else { + int currentCASFailureCount = currentThreadCASFailureCount.get(); + currentThreadCASFailureCount.set(currentCASFailureCount + 1); + } + return result; + } + + private void maybeWait() { + if ("thread1".equals(Thread.currentThread().getName())) { + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public boolean deposit(int amount) { + int current = balance.get(); + boolean result = balance.compareAndSet(current, current + amount); + if (result) { + transactionCount.incrementAndGet(); + } else { + int currentCASFailureCount = currentThreadCASFailureCount.get(); + currentThreadCASFailureCount.set(currentCASFailureCount + 1); + } + return result; + } + +} diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java index 1a46e1ba52..415b24738a 100644 --- a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java +++ b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java @@ -9,13 +9,11 @@ public class StampedAccount { private AtomicStampedReference account = new AtomicStampedReference<>(0, 0); public int getBalance() { - return this.account.get(new int[1]); + return account.getReference(); } public int getStamp() { - int[] stamps = new int[1]; - this.account.get(stamps); - return stamps[0]; + return account.getStamp(); } public boolean deposit(int funds) { diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java new file mode 100644 index 0000000000..aa5f0f7997 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java @@ -0,0 +1,98 @@ +package com.baeldung.abaproblem; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AccountUnitTest { + + private Account account; + + @BeforeEach + public void setUp() { + account = new Account(); + } + + @Test + public void zeroBalanceInitializationTest() { + assertEquals(0, account.getBalance()); + assertEquals(0, account.getTransactionCount()); + assertEquals(0, account.getCurrentThreadCASFailureCount()); + } + + @Test + public void depositTest() { + final int moneyToDeposit = 50; + + assertTrue(account.deposit(moneyToDeposit)); + + assertEquals(moneyToDeposit, account.getBalance()); + } + + @Test + public void withdrawTest() throws InterruptedException { + final int defaultBalance = 50; + final int moneyToWithdraw = 20; + + account.deposit(defaultBalance); + + assertTrue(account.withdraw(moneyToWithdraw)); + + assertEquals(defaultBalance - moneyToWithdraw, account.getBalance()); + } + + @Test + public void abaProblemTest() throws InterruptedException { + final int defaultBalance = 50; + + final int amountToWithdrawByThread1 = 20; + final int amountToWithdrawByThread2 = 10; + final int amountToDepositByThread2 = 10; + + assertEquals(0, account.getTransactionCount()); + assertEquals(0, account.getCurrentThreadCASFailureCount()); + account.deposit(defaultBalance); + assertEquals(1, account.getTransactionCount()); + + Thread thread1 = new Thread(() -> { + + // this will take longer due to the name of the thread + assertTrue(account.withdraw(amountToWithdrawByThread1)); + + // thread 1 fails to capture ABA problem + assertNotEquals(1, account.getCurrentThreadCASFailureCount()); + + }, "thread1"); + + Thread thread2 = new Thread(() -> { + + assertTrue(account.deposit(amountToDepositByThread2)); + assertEquals(defaultBalance + amountToDepositByThread2, account.getBalance()); + + // this will be fast due to the name of the thread + assertTrue(account.withdraw(amountToWithdrawByThread2)); + + // thread 1 didn't finish yet, so the original value will be in place for it + assertEquals(defaultBalance, account.getBalance()); + + assertEquals(0, account.getCurrentThreadCASFailureCount()); + }, "thread2"); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // compareAndSet operation succeeds for thread 1 + assertEquals(defaultBalance - amountToWithdrawByThread1, account.getBalance()); + + //but there are other transactions + assertNotEquals(2, account.getTransactionCount()); + + // thread 2 did two modifications as well + assertEquals(4, account.getTransactionCount()); + } +} diff --git a/core-java-modules/core-java-console/README.md b/core-java-modules/core-java-console/README.md new file mode 100644 index 0000000000..725e2482bb --- /dev/null +++ b/core-java-modules/core-java-console/README.md @@ -0,0 +1,5 @@ +#Core Java Console + +[Read and Write User Input in Java](http://www.baeldung.com/java-console-input-output) +[Formatting with printf() in Java](https://www.baeldung.com/java-printstream-printf) +[ASCII Art in Java](http://www.baeldung.com/ascii-art-in-java) \ No newline at end of file diff --git a/core-java-modules/core-java-console/pom.xml b/core-java-modules/core-java-console/pom.xml new file mode 100644 index 0000000000..1d58d8c253 --- /dev/null +++ b/core-java-modules/core-java-console/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + core-java-console + 0.1.0-SNAPSHOT + core-java-console + jar + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + ../ + + + + core-java-console + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/libs + + + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + java + com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed + + -Xmx300m + -XX:+UseParallelGC + -classpath + + com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${source.version} + ${target.version} + + + + + + + + integration + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*ManualTest.java + + + **/*IntegrationTest.java + **/*IntTest.java + + + + + + + json + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + + run-benchmarks + + none + + exec + + + test + java + + -classpath + + org.openjdk.jmh.Main + .* + + + + + + + + + + + + 3.0.0-M1 + 1.6.0 + 1.8 + 1.8 + + + diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/asciiart/AsciiArt.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/asciiart/AsciiArt.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/asciiart/AsciiArt.java rename to core-java-modules/core-java-console/src/main/java/com/baeldung/asciiart/AsciiArt.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleConsoleClass.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleConsoleClass.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleConsoleClass.java rename to core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleConsoleClass.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleScannerClass.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleScannerClass.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleScannerClass.java rename to core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleScannerClass.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/printf/PrintfExamples.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/printf/PrintfExamples.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/printf/PrintfExamples.java rename to core-java-modules/core-java-console/src/main/java/com/baeldung/printf/PrintfExamples.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java b/core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java similarity index 94% rename from core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java rename to core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java index 8ab1695395..a8c42fbeb1 100644 --- a/core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java +++ b/core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java @@ -1,10 +1,9 @@ package com.baeldung.asciiart; -import java.awt.Font; - +import com.baeldung.asciiart.AsciiArt.Settings; import org.junit.Test; -import com.baeldung.asciiart.AsciiArt.Settings; +import java.awt.*; public class AsciiArtIntegrationTest { @@ -16,5 +15,4 @@ public class AsciiArtIntegrationTest { asciiArt.drawString(text, "*", settings); } - } diff --git a/core-java-modules/core-java-date-operations-2/README.md b/core-java-modules/core-java-date-operations-2/README.md index 19c7b98d30..6dc1302d99 100644 --- a/core-java-modules/core-java-date-operations-2/README.md +++ b/core-java-modules/core-java-date-operations-2/README.md @@ -8,4 +8,5 @@ This module contains articles about date operations in Java. - [Converting Java Date to OffsetDateTime](https://www.baeldung.com/java-convert-date-to-offsetdatetime) - [How to Set the JVM Time Zone](https://www.baeldung.com/java-jvm-time-zone) - [How to determine day of week by passing specific date in Java?](https://www.baeldung.com/java-get-day-of-week) +- [Finding Leap Years in Java](https://www.baeldung.com/java-leap-year) - [[<-- Prev]](/core-java-modules/core-java-date-operations-1) diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java rename to core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java diff --git a/core-java-modules/core-java-io-conversions-2/README.md b/core-java-modules/core-java-io-conversions-2/README.md index 5cb9c21c54..9ce36e7437 100644 --- a/core-java-modules/core-java-io-conversions-2/README.md +++ b/core-java-modules/core-java-io-conversions-2/README.md @@ -5,4 +5,5 @@ This module contains articles about core Java input/output(IO) conversions. ### Relevant Articles: - [Java InputStream to String](https://www.baeldung.com/convert-input-stream-to-string) - [Java – Write an InputStream to a File](https://www.baeldung.com/convert-input-stream-to-a-file) +- [Converting a BufferedReader to a JSONObject](https://www.baeldung.com/java-bufferedreader-to-jsonobject) - More articles: [[<-- prev]](/core-java-modules/core-java-io-conversions) diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml index 5312ad645d..46bce7988b 100644 --- a/core-java-modules/core-java-io-conversions-2/pom.xml +++ b/core-java-modules/core-java-io-conversions-2/pom.xml @@ -21,6 +21,11 @@ commons-lang3 ${commons-lang3.version} + + org.json + json + 20200518 + diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java new file mode 100644 index 0000000000..80007e9c2f --- /dev/null +++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java @@ -0,0 +1,48 @@ +package com.baeldung.bufferedreadertojsonobject; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.Test; + +public class JavaBufferedReaderToJSONObjectUnitTest { + + @Test + public void givenValidJson_whenUsingBufferedReader_thenJSONTokenerConverts() { + byte[] b = "{ \"name\" : \"John\", \"age\" : 18 }".getBytes(StandardCharsets.UTF_8); + InputStream is = new ByteArrayInputStream(b); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); + JSONTokener tokener = new JSONTokener(bufferedReader); + JSONObject json = new JSONObject(tokener); + + assertNotNull(json); + assertEquals("John", json.get("name")); + assertEquals(18, json.get("age")); + } + + @Test + public void givenValidJson_whenUsingString_thenJSONObjectConverts() throws IOException { + byte[] b = "{ \"name\" : \"John\", \"age\" : 18 }".getBytes(StandardCharsets.UTF_8); + InputStream is = new ByteArrayInputStream(b); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line); + } + JSONObject json = new JSONObject(sb.toString()); + + assertNotNull(json); + assertEquals("John", json.get("name")); + assertEquals(18, json.get("age")); + } +} diff --git a/core-java-modules/core-java-jvm/README.md b/core-java-modules/core-java-jvm/README.md index 2f80ea7372..0dae790ec0 100644 --- a/core-java-modules/core-java-jvm/README.md +++ b/core-java-modules/core-java-jvm/README.md @@ -12,3 +12,4 @@ This module contains articles about working with the Java Virtual Machine (JVM). - [Guide to System.gc()](https://www.baeldung.com/java-system-gc) - [Runtime.getRuntime().halt() vs System.exit() in Java](https://www.baeldung.com/java-runtime-halt-vs-system-exit) - [Adding Shutdown Hooks for JVM Applications](https://www.baeldung.com/jvm-shutdown-hooks) +- [How to Get the Size of an Object in Java](http://www.baeldung.com/java-size-of-object) diff --git a/core-java-modules/core-java-jvm/pom.xml b/core-java-modules/core-java-jvm/pom.xml index f3e5470a61..5e731363ae 100644 --- a/core-java-modules/core-java-jvm/pom.xml +++ b/core-java-modules/core-java-jvm/pom.xml @@ -51,14 +51,31 @@ system ${java.home}/../lib/tools.jar + + org.ow2.asm + asm + ${asm.version} + + + org.ow2.asm + asm-util + ${asm.version} + + + org.apache.bcel + bcel + ${bcel.version} + 3.6.1 - 3.21.0-GA + 3.27.0-GA 2.1.0.1 1.8.0 + 8.0.1 + 6.5.0 diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationExample.java b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationExample.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationExample.java rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationExample.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/MANIFEST.MF b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/MANIFEST.MF similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/MANIFEST.MF rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/MANIFEST.MF diff --git a/core-java-modules/core-java-jvm/src/test/java/com/baeldung/bytecode/ViewBytecodeUnitTest.java b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/bytecode/ViewBytecodeUnitTest.java new file mode 100644 index 0000000000..5b0fdf26d4 --- /dev/null +++ b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/bytecode/ViewBytecodeUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.bytecode; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.apache.bcel.Repository; +import org.apache.bcel.classfile.JavaClass; +import org.junit.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.util.TraceClassVisitor; +import javassist.ClassPool; +import javassist.NotFoundException; +import javassist.bytecode.ClassFile; + +public class ViewBytecodeUnitTest { + + @Test + public void whenUsingASM_thenReadBytecode() throws IOException { + ClassReader reader = new ClassReader("java.lang.Object"); + StringWriter sw = new StringWriter(); + TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(sw)); + reader.accept(tcv, 0); + + assertTrue(sw.toString().contains("public class java/lang/Object")); + } + + @Test + public void whenUsingBCEL_thenReadBytecode() throws ClassNotFoundException { + JavaClass objectClazz = Repository.lookupClass("java.lang.Object"); + + assertEquals(objectClazz.getClassName(), "java.lang.Object"); + assertEquals(objectClazz.getMethods().length, 14); + assertTrue(objectClazz.toString().contains("public class java.lang.Object")); + } + + @Test + public void whenUsingJavassist_thenReadBytecode() throws NotFoundException { + ClassPool cp = ClassPool.getDefault(); + ClassFile cf = cp.get("java.lang.Object").getClassFile(); + + assertEquals(cf.getName(), "java.lang.Object"); + assertEquals(cf.getMethods().size(), 14); + } + +} + + diff --git a/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java new file mode 100644 index 0000000000..47bb668727 --- /dev/null +++ b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.error.oom; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; + +public class ExecutorServiceUnitTest { + + @Test + public void givenAnExecutorService_WhenMoreTasksSubmitted_ThenAdditionalTasksWait() { + + // Given + int noOfThreads = 5; + ExecutorService executorService = Executors.newFixedThreadPool(noOfThreads); + + Runnable runnableTask = () -> { + try { + TimeUnit.HOURS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }; + + // When + IntStream.rangeClosed(1, 10) + .forEach(i -> executorService.submit(runnableTask)); + + // Then + assertThat(((ThreadPoolExecutor) executorService).getQueue() + .size(), is(equalTo(5))); + } +} diff --git a/core-java-modules/core-java-lang-oop-types/README.md b/core-java-modules/core-java-lang-oop-types/README.md index 80344c70fa..449a0f59cc 100644 --- a/core-java-modules/core-java-lang-oop-types/README.md +++ b/core-java-modules/core-java-lang-oop-types/README.md @@ -7,3 +7,6 @@ This module contains articles about types in Java - [Guide to the this Java Keyword](https://www.baeldung.com/java-this) - [Nested Classes in Java](https://www.baeldung.com/java-nested-classes) - [Marker Interfaces in Java](https://www.baeldung.com/java-marker-interfaces) +- [Iterating Over Enum Values in Java](https://www.baeldung.com/java-enum-iteration) +- [Attaching Values to Java Enum](https://www.baeldung.com/java-enum-values) +- [A Guide to Java Enums](https://www.baeldung.com/a-guide-to-java-enums) diff --git a/core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/Pizza.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/Pizza.java similarity index 100% rename from core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/Pizza.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/Pizza.java diff --git a/core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/PizzaDeliveryStrategy.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/PizzaDeliveryStrategy.java similarity index 100% rename from core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/PizzaDeliveryStrategy.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/PizzaDeliveryStrategy.java diff --git a/core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/PizzaDeliverySystemConfiguration.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/PizzaDeliverySystemConfiguration.java similarity index 100% rename from core-java-modules/core-java-lang-syntax/src/main/java/com/baeldung/enums/PizzaDeliverySystemConfiguration.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/PizzaDeliverySystemConfiguration.java diff --git a/core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element1.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element1.java similarity index 100% rename from core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element1.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element1.java diff --git a/core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element2.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element2.java similarity index 100% rename from core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element2.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element2.java diff --git a/core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element3.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element3.java similarity index 100% rename from core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element3.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element3.java diff --git a/core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element4.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element4.java similarity index 100% rename from core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Element4.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Element4.java diff --git a/core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Labeled.java b/core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Labeled.java similarity index 100% rename from core-java-modules/core-java-lang/src/main/java/com/baeldung/enums/values/Labeled.java rename to core-java-modules/core-java-lang-oop-types/src/main/java/com/baeldung/enums/values/Labeled.java diff --git a/core-java-modules/core-java-lang-syntax/src/test/java/com/baeldung/enums/PizzaUnitTest.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/PizzaUnitTest.java similarity index 100% rename from core-java-modules/core-java-lang-syntax/src/test/java/com/baeldung/enums/PizzaUnitTest.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/PizzaUnitTest.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/iteration/DaysOfWeekEnum.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/iteration/DaysOfWeekEnum.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/iteration/DaysOfWeekEnum.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/iteration/DaysOfWeekEnum.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/iteration/EnumIterationExamples.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/iteration/EnumIterationExamples.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/iteration/EnumIterationExamples.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/iteration/EnumIterationExamples.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element1UnitTest.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element1UnitTest.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element1UnitTest.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element1UnitTest.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element2UnitTest.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element2UnitTest.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element2UnitTest.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element2UnitTest.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element3UnitTest.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element3UnitTest.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element3UnitTest.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element3UnitTest.java diff --git a/core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element4UnitTest.java b/core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element4UnitTest.java similarity index 100% rename from core-java-modules/core-java-lang/src/test/java/com/baeldung/enums/values/Element4UnitTest.java rename to core-java-modules/core-java-lang-oop-types/src/test/java/com/baeldung/enums/values/Element4UnitTest.java diff --git a/core-java-modules/core-java-lang-syntax/src/test/java/com/baeldung/varargs/HeapPollutionUnitTest.java b/core-java-modules/core-java-lang-syntax/src/test/java/com/baeldung/varargs/HeapPollutionUnitTest.java new file mode 100644 index 0000000000..ced2c00bea --- /dev/null +++ b/core-java-modules/core-java-lang-syntax/src/test/java/com/baeldung/varargs/HeapPollutionUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.varargs; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class HeapPollutionUnitTest { + + @Test(expected = ClassCastException.class) + public void givenGenericVararg_whenUsedUnsafe_shouldThrowClassCastException() { + String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList()); + + assertEquals("one", one); + } + + @Test(expected = ClassCastException.class) + public void givenGenericVararg_whenRefEscapes_mayCauseSubtleBugs() { + String[] args = returnAsIs("One", "Two"); + } + + private static String firstOfFirst(List... strings) { + List ints = Collections.singletonList(42); + Object[] objects = strings; + objects[0] = ints; + + return strings[0].get(0); + } + + private static T[] toArray(T... arguments) { + return arguments; + } + + private static T[] returnAsIs(T a, T b) { + return toArray(a, b); + } +} diff --git a/core-java-modules/core-java-lang/README.md b/core-java-modules/core-java-lang/README.md index 9e98bb849b..9166b93b7f 100644 --- a/core-java-modules/core-java-lang/README.md +++ b/core-java-modules/core-java-lang/README.md @@ -4,7 +4,6 @@ This module contains articles about core features in the Java language ### Relevant Articles: - [Generate equals() and hashCode() with Eclipse](https://www.baeldung.com/java-eclipse-equals-and-hashcode) -- [Iterating Over Enum Values in Java](https://www.baeldung.com/java-enum-iteration) - [Comparator and Comparable in Java](https://www.baeldung.com/java-comparator-comparable) - [Recursion In Java](https://www.baeldung.com/java-recursion) - [A Guide to the finalize Method in Java](https://www.baeldung.com/java-finalize) @@ -12,8 +11,6 @@ This module contains articles about core features in the Java language - [Using Java Assertions](https://www.baeldung.com/java-assert) - [Synthetic Constructs in Java](https://www.baeldung.com/java-synthetic) - [Retrieving a Class Name in Java](https://www.baeldung.com/java-class-name) -- [Attaching Values to Java Enum](https://www.baeldung.com/java-enum-values) - [The Java continue and break Keywords](https://www.baeldung.com/java-continue-and-break) -- [A Guide to Java Enums](https://www.baeldung.com/a-guide-to-java-enums) - [Infinite Loops in Java](https://www.baeldung.com/infinite-loops-java) - [[More --> ]](/core-java-modules/core-java-lang-2) diff --git a/core-java-modules/core-java-reflection/README.MD b/core-java-modules/core-java-reflection/README.MD index 5aed62b378..d6b3a02e6a 100644 --- a/core-java-modules/core-java-reflection/README.MD +++ b/core-java-modules/core-java-reflection/README.MD @@ -8,3 +8,5 @@ - [Changing Annotation Parameters At Runtime](http://www.baeldung.com/java-reflection-change-annotation-params) - [Dynamic Proxies in Java](http://www.baeldung.com/java-dynamic-proxies) - [What Causes java.lang.reflect.InvocationTargetException?](https://www.baeldung.com/java-lang-reflect-invocationtargetexception) +- [How to Find all Getters Returning Null](http://www.baeldung.com/java-getters-returning-null) +- [How to Get a Name of a Method Being Executed?](http://www.baeldung.com/java-name-of-executing-method) diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/java/currentmethod/CurrentlyExecutedMethodFinderUnitTest.java b/core-java-modules/core-java-reflection/src/test/java/com/baeldung/currentmethod/CurrentlyExecutedMethodFinderUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/java/currentmethod/CurrentlyExecutedMethodFinderUnitTest.java rename to core-java-modules/core-java-reflection/src/test/java/com/baeldung/currentmethod/CurrentlyExecutedMethodFinderUnitTest.java diff --git a/core-java-modules/core-java-reflection/src/test/java/com/baeldung/reflection/java/reflection/ReflectionUnitTest.java b/core-java-modules/core-java-reflection/src/test/java/com/baeldung/reflection/java/reflection/ReflectionUnitTest.java index 0c090901e7..a791d64874 100644 --- a/core-java-modules/core-java-reflection/src/test/java/com/baeldung/reflection/java/reflection/ReflectionUnitTest.java +++ b/core-java-modules/core-java-reflection/src/test/java/com/baeldung/reflection/java/reflection/ReflectionUnitTest.java @@ -200,7 +200,7 @@ public class ReflectionUnitTest { @Test public void givenClassField_whenSetsAndGetsValue_thenCorrect() throws Exception { final Class birdClass = Class.forName("com.baeldung.java.reflection.Bird"); - final Bird bird = (Bird) birdClass.newInstance(); + final Bird bird = (Bird) birdClass.getConstructor().newInstance(); final Field field = birdClass.getDeclaredField("walks"); field.setAccessible(true); @@ -266,7 +266,7 @@ public class ReflectionUnitTest { @Test public void givenMethod_whenInvokes_thenCorrect() throws Exception { final Class birdClass = Class.forName("com.baeldung.java.reflection.Bird"); - final Bird bird = (Bird) birdClass.newInstance(); + final Bird bird = (Bird) birdClass.getConstructor().newInstance(); final Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class); final Method walksMethod = birdClass.getDeclaredMethod("walks"); final boolean walks = (boolean) walksMethod.invoke(bird); diff --git a/core-java-modules/core-java-string-conversions/src/test/java/com/baeldung/stringtoint/StringToIntOrIntegerUnitTest.java b/core-java-modules/core-java-string-conversions/src/test/java/com/baeldung/stringtoint/StringToIntOrIntegerUnitTest.java index 106f1fc974..336b2ac324 100644 --- a/core-java-modules/core-java-string-conversions/src/test/java/com/baeldung/stringtoint/StringToIntOrIntegerUnitTest.java +++ b/core-java-modules/core-java-string-conversions/src/test/java/com/baeldung/stringtoint/StringToIntOrIntegerUnitTest.java @@ -26,6 +26,17 @@ public class StringToIntOrIntegerUnitTest { assertThat(result).isEqualTo(new Integer(42)); } + @Test + public void givenString_whenCallingValueOf_shouldCacheSomeValues() { + for (int i = -128; i <= 127; i++) { + String value = i + ""; + Integer first = Integer.valueOf(value); + Integer second = Integer.valueOf(value); + + assertThat(first).isSameAs(second); + } + } + @Test public void givenString_whenCallingIntegerConstructor_shouldConvertToInt() { String givenString = "42"; diff --git a/core-java-modules/core-java-string-operations-2/README.md b/core-java-modules/core-java-string-operations-2/README.md index 2f54aa9467..cafb3b9017 100644 --- a/core-java-modules/core-java-string-operations-2/README.md +++ b/core-java-modules/core-java-string-operations-2/README.md @@ -12,4 +12,6 @@ This module contains articles about string operations. - [L-Trim and R-Trim Alternatives in Java](https://www.baeldung.com/java-trim-alternatives) - [Java Convert PDF to Base64](https://www.baeldung.com/java-convert-pdf-to-base64) - [Encode a String to UTF-8 in Java](https://www.baeldung.com/java-string-encode-utf-8) +- [Guide to Character Encoding](https://www.baeldung.com/java-char-encoding) +- [Convert Hex to ASCII in Java](https://www.baeldung.com/java-convert-hex-to-ascii) #remove additional readme file - More articles: [[<-- prev]](../core-java-string-operations) diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/encoding/CharacterEncodingExamples.java b/core-java-modules/core-java-string-operations-2/src/main/java/com/baeldung/encoding/CharacterEncodingExamples.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/encoding/CharacterEncodingExamples.java rename to core-java-modules/core-java-string-operations-2/src/main/java/com/baeldung/encoding/CharacterEncodingExamples.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/encoding/CharacterEncodingExamplesUnitTest.java b/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/encoding/CharacterEncodingExamplesUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/encoding/CharacterEncodingExamplesUnitTest.java rename to core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/encoding/CharacterEncodingExamplesUnitTest.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/hexToAscii/HexToAsciiUnitTest.java b/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/hexToAscii/HexToAsciiUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/hexToAscii/HexToAsciiUnitTest.java rename to core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/hexToAscii/HexToAsciiUnitTest.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/hexToAscii/README.md b/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/hexToAscii/README.md similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/hexToAscii/README.md rename to core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/hexToAscii/README.md diff --git a/core-java-modules/core-java/src/test/resources/encoding.txt b/core-java-modules/core-java-string-operations-2/src/test/resources/encoding.txt similarity index 100% rename from core-java-modules/core-java/src/test/resources/encoding.txt rename to core-java-modules/core-java-string-operations-2/src/test/resources/encoding.txt diff --git a/core-java-modules/core-java-string-operations-2/src/test/resources/output.pdf b/core-java-modules/core-java-string-operations-2/src/test/resources/output.pdf new file mode 100644 index 0000000000..94d9477974 Binary files /dev/null and b/core-java-modules/core-java-string-operations-2/src/test/resources/output.pdf differ diff --git a/core-java-modules/core-java-time-measurements/README.md b/core-java-modules/core-java-time-measurements/README.md index 1bd277b6b1..a3e8b71ae8 100644 --- a/core-java-modules/core-java-time-measurements/README.md +++ b/core-java-modules/core-java-time-measurements/README.md @@ -6,3 +6,4 @@ This module contains articles about the measurement of time in Java. - [Guide to the Java Clock Class](http://www.baeldung.com/java-clock) - [Measure Elapsed Time in Java](http://www.baeldung.com/java-measure-elapsed-time) - [Overriding System Time for Testing in Java](https://www.baeldung.com/java-override-system-time) +- [Java Timer](http://www.baeldung.com/java-timer-and-timertask) diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/timer/DatabaseMigrationTask.java b/core-java-modules/core-java-time-measurements/src/main/java/com/baeldung/timer/DatabaseMigrationTask.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/timer/DatabaseMigrationTask.java rename to core-java-modules/core-java-time-measurements/src/main/java/com/baeldung/timer/DatabaseMigrationTask.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/timer/NewsletterTask.java b/core-java-modules/core-java-time-measurements/src/main/java/com/baeldung/timer/NewsletterTask.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/timer/NewsletterTask.java rename to core-java-modules/core-java-time-measurements/src/main/java/com/baeldung/timer/NewsletterTask.java diff --git a/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/java/clock/ClockUnitTest.java b/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/clock/ClockUnitTest.java similarity index 99% rename from core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/java/clock/ClockUnitTest.java rename to core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/clock/ClockUnitTest.java index e83ba7afc8..4e34271214 100644 --- a/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/java/clock/ClockUnitTest.java +++ b/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/clock/ClockUnitTest.java @@ -1,4 +1,4 @@ -package com.baeldung.java.clock; +package com.baeldung.clock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/timer/DatabaseMigrationTaskUnitTest.java b/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/DatabaseMigrationTaskUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/timer/DatabaseMigrationTaskUnitTest.java rename to core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/DatabaseMigrationTaskUnitTest.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/timer/JavaTimerLongRunningUnitTest.java b/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/JavaTimerLongRunningUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/timer/JavaTimerLongRunningUnitTest.java rename to core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/JavaTimerLongRunningUnitTest.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/timer/NewsletterTaskUnitTest.java b/core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/NewsletterTaskUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/timer/NewsletterTaskUnitTest.java rename to core-java-modules/core-java-time-measurements/src/test/java/com/baeldung/timer/NewsletterTaskUnitTest.java diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md index bffb88cafb..7781382ae5 100644 --- a/core-java-modules/core-java/README.md +++ b/core-java-modules/core-java/README.md @@ -1,35 +1,13 @@ ## Core Java Cookbooks and Examples ### Relevant Articles: -- [Java Timer](http://www.baeldung.com/java-timer-and-timertask) -- [Getting Started with Java Properties](http://www.baeldung.com/java-properties) -- [Introduction to Nashorn](http://www.baeldung.com/java-nashorn) -- [Java Money and the Currency API](http://www.baeldung.com/java-money-and-currency) -- [JVM Log Forging](http://www.baeldung.com/jvm-log-forging) -- [How to Find all Getters Returning Null](http://www.baeldung.com/java-getters-returning-null) -- [How to Get a Name of a Method Being Executed?](http://www.baeldung.com/java-name-of-executing-method) -- [Introduction to Java Serialization](http://www.baeldung.com/java-serialization) -- [Guide to UUID in Java](http://www.baeldung.com/java-uuid) -- [Creating a Java Compiler Plugin](http://www.baeldung.com/java-build-compiler-plugin) -- [Quick Guide to the Java Stack](https://www.baeldung.com/java-stack) -- [Compiling Java *.class Files with javac](http://www.baeldung.com/javac) -- [Introduction to Javadoc](http://www.baeldung.com/javadoc) -- [Guide to the Externalizable Interface in Java](http://www.baeldung.com/java-externalizable) -- [ASCII Art in Java](http://www.baeldung.com/ascii-art-in-java) -- [What is the serialVersionUID?](http://www.baeldung.com/java-serial-version-uid) -- [A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle) -- [Java Global Exception Handler](http://www.baeldung.com/java-global-exception-handler) -- [How to Get the Size of an Object in Java](http://www.baeldung.com/java-size-of-object) -- [Common Java Exceptions](http://www.baeldung.com/java-common-exceptions) -- [Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties) -- [Java – Try with Resources](https://www.baeldung.com/java-try-with-resources) -- [Guide to Character Encoding](https://www.baeldung.com/java-char-encoding) -- [Graphs in Java](https://www.baeldung.com/java-graphs) -- [Read and Write User Input in Java](http://www.baeldung.com/java-console-input-output) -- [Formatting with printf() in Java](https://www.baeldung.com/java-printstream-printf) -- [Retrieve Fields from a Java Class Using Reflection](https://www.baeldung.com/java-reflection-class-fields) -- [Using Curl in Java](https://www.baeldung.com/java-curl) -- [Finding Leap Years in Java](https://www.baeldung.com/java-leap-year) -- [Making a JSON POST Request With HttpURLConnection](https://www.baeldung.com/httpurlconnection-post) -- [How to Find an Exception’s Root Cause in Java](https://www.baeldung.com/java-exception-root-cause) -- [Convert Hex to ASCII in Java](https://www.baeldung.com/java-convert-hex-to-ascii) +[Getting Started with Java Properties](http://www.baeldung.com/java-properties) +[Java Money and the Currency API](http://www.baeldung.com/java-money-and-currency) +[Introduction to Java Serialization](http://www.baeldung.com/java-serialization) +[Guide to UUID in Java](http://www.baeldung.com/java-uuid) +[Compiling Java *.class Files with javac](http://www.baeldung.com/javac) +[Introduction to Javadoc](http://www.baeldung.com/javadoc) +[Guide to the Externalizable Interface in Java](http://www.baeldung.com/java-externalizable) +[What is the serialVersionUID?](http://www.baeldung.com/java-serial-version-uid) +[A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle) +[Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties) diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java index 2659b29491..cb43a26929 100644 --- a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java +++ b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java @@ -3,30 +3,57 @@ package com.baeldung.uuid; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.Arrays; +import java.util.Random; import java.util.UUID; public class UUIDGenerator { - /** - * These are predefined UUID for name spaces - */ - private static final String NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; - private static final String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"; - private static final String NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"; - private static final String NAMESPACE_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"; - private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); - public static void main(String[] args) { - try { - System.out.println("Type 3 : " + generateType3UUID(NAMESPACE_DNS, "google.com")); - System.out.println("Type 4 : " + generateType4UUID()); - System.out.println("Type 5 : " + generateType5UUID(NAMESPACE_URL, "google.com")); - System.out.println("Unique key : " + generateUniqueKeysWithUUIDAndMessageDigest()); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - e.printStackTrace(); - } + /** + * Type 1 UUID Generation + */ + public static UUID generateType1UUID() { + + long most64SigBits = get64MostSignificantBitsForVersion1(); + long least64SigBits = get64LeastSignificantBitsForVersion1(); + + return new UUID(most64SigBits, least64SigBits); + } + + private static long get64LeastSignificantBitsForVersion1() { + Random random = new Random(); + long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL; + long variant3BitFlag = 0x8000000000000000L; + return random63BitLong + variant3BitFlag; + } + + private static long get64MostSignificantBitsForVersion1() { + LocalDateTime start = LocalDateTime.of(1582, 10, 15, 0, 0, 0); + Duration duration = Duration.between(start, LocalDateTime.now()); + long seconds = duration.getSeconds(); + long nanos = duration.getNano(); + long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100; + long least12SignificatBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4; + long version = 1 << 12; + return (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificatBitOfTime; + } + + /** + * Type 3 UUID Generation + * + * @throws UnsupportedEncodingException + */ + public static UUID generateType3UUID(String namespace, String name) throws UnsupportedEncodingException { + + byte[] nameSpaceBytes = bytesFromUUID(namespace); + byte[] nameBytes = name.getBytes("UTF-8"); + byte[] result = joinBytes(nameSpaceBytes, nameBytes); + + return UUID.nameUUIDFromBytes(result); } /** @@ -37,28 +64,18 @@ public class UUIDGenerator { return uuid; } - /** - * Type 3 UUID Generation - * - * @throws UnsupportedEncodingException - */ - public static UUID generateType3UUID(String namespace, String name) throws UnsupportedEncodingException { - String source = namespace + name; - byte[] bytes = source.getBytes("UTF-8"); - UUID uuid = UUID.nameUUIDFromBytes(bytes); - return uuid; - } - /** * Type 5 UUID Generation - * - * @throws UnsupportedEncodingException + * + * @throws UnsupportedEncodingException */ public static UUID generateType5UUID(String namespace, String name) throws UnsupportedEncodingException { - String source = namespace + name; - byte[] bytes = source.getBytes("UTF-8"); - UUID uuid = type5UUIDFromBytes(bytes); - return uuid; + + byte[] nameSpaceBytes = bytesFromUUID(namespace); + byte[] nameBytes = name.getBytes("UTF-8"); + byte[] result = joinBytes(nameSpaceBytes, nameBytes); + + return type5UUIDFromBytes(result); } public static UUID type5UUIDFromBytes(byte[] name) { @@ -91,20 +108,20 @@ public class UUIDGenerator { /** * Unique Keys Generation Using Message Digest and Type 4 UUID - * - * @throws NoSuchAlgorithmException - * @throws UnsupportedEncodingException + * + * @throws NoSuchAlgorithmException + * @throws UnsupportedEncodingException */ public static String generateUniqueKeysWithUUIDAndMessageDigest() throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest salt = MessageDigest.getInstance("SHA-256"); salt.update(UUID.randomUUID() - .toString() - .getBytes("UTF-8")); + .toString() + .getBytes("UTF-8")); String digest = bytesToHex(salt.digest()); return digest; } - public static String bytesToHex(byte[] bytes) { + private static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; @@ -114,4 +131,37 @@ public class UUIDGenerator { return new String(hexChars); } -} + private static byte[] bytesFromUUID(String uuidHexString) { + String normalizedUUIDHexString = uuidHexString.replace("-",""); + + assert normalizedUUIDHexString.length() == 32; + + byte[] bytes = new byte[16]; + for (int i = 0; i < 16; i++) { + byte b = hexToByte(normalizedUUIDHexString.substring(i*2, i*2+2)); + bytes[i] = b; + } + return bytes; + } + + public static byte hexToByte(String hexString) { + int firstDigit = Character.digit(hexString.charAt(0),16); + int secondDigit = Character.digit(hexString.charAt(1),16); + return (byte) ((firstDigit << 4) + secondDigit); + } + + public static byte[] joinBytes(byte[] byteArray1, byte[] byteArray2) { + int finalLength = byteArray1.length + byteArray2.length; + byte[] result = new byte[finalLength]; + + for(int i = 0; i < byteArray1.length; i++) { + result[i] = byteArray1[i]; + } + + for(int i = 0; i < byteArray2.length; i++) { + result[byteArray1.length+i] = byteArray2[i]; + } + + return result; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java new file mode 100644 index 0000000000..9e08363a63 --- /dev/null +++ b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java @@ -0,0 +1,64 @@ +package com.baeldung.uuid; + +import org.junit.jupiter.api.Test; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UUIDGeneratorUnitTest { + + private static final String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"; + private static final String NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; + + @Test + public void version_1_UUID_is_generated_with_correct_length_version_and_variant() { + + UUID uuid = UUIDGenerator.generateType1UUID(); + + assertEquals(36, uuid.toString().length()); + assertEquals(1, uuid.version()); + assertEquals(2, uuid.variant()); + } + + @Test + public void version_3_UUID_is_correctly_generated_for_domain_baeldung_com() throws UnsupportedEncodingException { + + UUID uuid = UUIDGenerator.generateType3UUID(NAMESPACE_DNS, "baeldung.com"); + + assertEquals("23785b78-0132-3ac6-aff6-cfd5be162139", uuid.toString()); + assertEquals(3, uuid.version()); + assertEquals(2, uuid.variant()); + } + + @Test + public void version_3_UUID_is_correctly_generated_for_domain_d() throws UnsupportedEncodingException { + + UUID uuid = UUIDGenerator.generateType3UUID(NAMESPACE_DNS, "d"); + + assertEquals("dbd41ecb-f466-33de-b309-1468addfc63b", uuid.toString()); + assertEquals(3, uuid.version()); + assertEquals(2, uuid.variant()); + } + + @Test + public void version_4_UUID_is_generated_with_correct_length_version_and_variant() { + + UUID uuid = UUIDGenerator.generateType4UUID(); + + assertEquals(36, uuid.toString().length()); + assertEquals(4, uuid.version()); + assertEquals(2, uuid.variant()); + } + + @Test + public void version_5_UUID_is_correctly_generated_for_domain_baeldung_com() throws UnsupportedEncodingException { + + UUID uuid = UUIDGenerator.generateType5UUID(NAMESPACE_URL, "baeldung.com"); + + assertEquals("aeff44a5-8a61-52b6-bcbe-c8e5bd7d0300", uuid.toString()); + assertEquals(5, uuid.version()); + assertEquals(2, uuid.variant()); + } +} \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin-2/README.md b/core-kotlin-modules/core-kotlin-2/README.md deleted file mode 100644 index d6d6b2f706..0000000000 --- a/core-kotlin-modules/core-kotlin-2/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## Core Kotlin 2 - -This module contains articles about Kotlin core features. - -### Relevant articles: -- [Working with Dates in Kotlin](https://www.baeldung.com/kotlin-dates) -- [Kotlin Ternary Conditional Operator](https://www.baeldung.com/kotlin-ternary-operator) -- [Sequences in Kotlin](https://www.baeldung.com/kotlin/sequences) -- [[<-- Prev]](/core-kotlin-modules/core-kotlin) diff --git a/core-kotlin-modules/core-kotlin-collections/README.md b/core-kotlin-modules/core-kotlin-collections/README.md index 66f15e7419..997680c2bc 100644 --- a/core-kotlin-modules/core-kotlin-collections/README.md +++ b/core-kotlin-modules/core-kotlin-collections/README.md @@ -10,3 +10,4 @@ This module contains articles about core Kotlin collections. - [Filtering Kotlin Collections](https://www.baeldung.com/kotlin-filter-collection) - [Collection Transformations in Kotlin](https://www.baeldung.com/kotlin-collection-transformations) - [Difference between fold and reduce in Kotlin](https://www.baeldung.com/kotlin/fold-vs-reduce) +- [Guide to Sorting in Kotlin](https://www.baeldung.com/kotlin-sort) diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/sorting/SortingExample.kt b/core-kotlin-modules/core-kotlin-collections/src/main/kotlin/com/baeldung/sorting/SortingExample.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/sorting/SortingExample.kt rename to core-kotlin-modules/core-kotlin-collections/src/main/kotlin/com/baeldung/sorting/SortingExample.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/sorting/SortingExampleKtTest.kt b/core-kotlin-modules/core-kotlin-collections/src/test/kotlin/com/baeldung/sorting/SortingExampleKtTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/sorting/SortingExampleKtTest.kt rename to core-kotlin-modules/core-kotlin-collections/src/test/kotlin/com/baeldung/sorting/SortingExampleKtTest.kt diff --git a/core-kotlin-modules/core-kotlin-datastructures/README.md b/core-kotlin-modules/core-kotlin-datastructures/README.md new file mode 100644 index 0000000000..3b22730a76 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-datastructures/README.md @@ -0,0 +1,6 @@ +## Core Kotlin + +This module contains articles about data structures in Kotlin + +### Relevant articles: +[Implementing a Binary Tree in Kotlin](https://www.baeldung.com/kotlin-binary-tree) diff --git a/core-kotlin-modules/core-kotlin-datastructures/pom.xml b/core-kotlin-modules/core-kotlin-datastructures/pom.xml new file mode 100644 index 0000000000..eae11c17cf --- /dev/null +++ b/core-kotlin-modules/core-kotlin-datastructures/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + core-kotlin-datastructures + core-kotlin-datastructures + jar + + + com.baeldung.core-kotlin-modules + core-kotlin-modules + 1.0.0-SNAPSHOT + + + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + + 1.1.1 + + + \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/binarytree/Main.kt b/core-kotlin-modules/core-kotlin-datastructures/src/main/kotlin/com/baeldung/binarytree/Main.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/binarytree/Main.kt rename to core-kotlin-modules/core-kotlin-datastructures/src/main/kotlin/com/baeldung/binarytree/Main.kt diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/binarytree/Node.kt b/core-kotlin-modules/core-kotlin-datastructures/src/main/kotlin/com/baeldung/binarytree/Node.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/binarytree/Node.kt rename to core-kotlin-modules/core-kotlin-datastructures/src/main/kotlin/com/baeldung/binarytree/Node.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/binarytree/NodeTest.kt b/core-kotlin-modules/core-kotlin-datastructures/src/test/kotlin/com/binarytree/NodeTest.kt similarity index 99% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/binarytree/NodeTest.kt rename to core-kotlin-modules/core-kotlin-datastructures/src/test/kotlin/com/binarytree/NodeTest.kt index 9414d7dde9..5a7f7fc50f 100644 --- a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/binarytree/NodeTest.kt +++ b/core-kotlin-modules/core-kotlin-datastructures/src/test/kotlin/com/binarytree/NodeTest.kt @@ -1,4 +1,4 @@ -package com.baeldung.binarytree +package com.binarytree import org.junit.After import org.junit.Assert.assertEquals diff --git a/core-kotlin-modules/core-kotlin-date-time/README.md b/core-kotlin-modules/core-kotlin-date-time/README.md new file mode 100644 index 0000000000..a3e358d4e3 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-date-time/README.md @@ -0,0 +1,6 @@ +## Core Kotlin Date and Time + +This module contains articles about Kotlin core date/time features. + +### Relevant articles: +[Working with Dates in Kotlin](https://www.baeldung.com/kotlin-dates) diff --git a/core-kotlin-modules/core-kotlin-date-time/pom.xml b/core-kotlin-modules/core-kotlin-date-time/pom.xml new file mode 100644 index 0000000000..f3cacefc19 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-date-time/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + core-kotlin-date-time + core-kotlin-date-time + jar + + + com.baeldung.core-kotlin-modules + core-kotlin-modules + 1.0.0-SNAPSHOT + + + + + org.assertj + assertj-core + ${org.assertj.core.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + + 1.1.1 + 3.9.0 + + + \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseDuration.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseDuration.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseDuration.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseDuration.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDate.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDate.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDate.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDate.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDateTime.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDateTime.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDateTime.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalDateTime.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalTime.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalTime.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseLocalTime.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseLocalTime.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UsePeriod.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UsePeriod.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UsePeriod.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UsePeriod.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseZonedDateTime.kt b/core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseZonedDateTime.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/main/kotlin/com/baeldung/dates/datetime/UseZonedDateTime.kt rename to core-kotlin-modules/core-kotlin-date-time/src/main/kotlin/com/baeldung/dates/datetime/UseZonedDateTime.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/CreateDateUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/CreateDateUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/CreateDateUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/CreateDateUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/ExtractDateUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/ExtractDateUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/ExtractDateUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/ExtractDateUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/FormatDateUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/FormatDateUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/FormatDateUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/FormatDateUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/PeriodDateUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/PeriodDateUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/PeriodDateUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/PeriodDateUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateTimeUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateTimeUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateTimeUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateTimeUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalDateUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalTimeUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalTimeUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseLocalTimeUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseLocalTimeUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UsePeriodUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UsePeriodUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UsePeriodUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UsePeriodUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseZonedDateTimeUnitTest.kt b/core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseZonedDateTimeUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/dates/datetime/UseZonedDateTimeUnitTest.kt rename to core-kotlin-modules/core-kotlin-date-time/src/test/kotlin/com/baeldung/dates/datetime/UseZonedDateTimeUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-design-patterns/README.md b/core-kotlin-modules/core-kotlin-design-patterns/README.md new file mode 100644 index 0000000000..4bdc164a47 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-design-patterns/README.md @@ -0,0 +1,6 @@ +## Core Kotlin Design Patterns + +This module contains articles about design patterns in Kotlin + +### Relevant articles: +- [Creational Design Patterns in Kotlin: Builder](https://www.baeldung.com/kotlin-builder-pattern) diff --git a/core-kotlin-modules/core-kotlin-design-patterns/pom.xml b/core-kotlin-modules/core-kotlin-design-patterns/pom.xml new file mode 100644 index 0000000000..c112602bc2 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-design-patterns/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + core-kotlin-design-patterns + core-kotlin-design-patterns + jar + + + com.baeldung.core-kotlin-modules + core-kotlin-modules + 1.0.0-SNAPSHOT + + + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + + 1.1.1 + + + \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrder.kt b/core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrder.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrder.kt rename to core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrder.kt diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrderApply.kt b/core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrderApply.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrderApply.kt rename to core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrderApply.kt diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrderNamed.kt b/core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrderNamed.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/FoodOrderNamed.kt rename to core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/FoodOrderNamed.kt diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/Main.kt b/core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/Main.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/builder/Main.kt rename to core-kotlin-modules/core-kotlin-design-patterns/src/main/kotlin/com/baeldung/builder/Main.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/builder/BuilderPatternUnitTest.kt b/core-kotlin-modules/core-kotlin-design-patterns/src/test/kotlin/com/baeldung/builder/BuilderPatternUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/builder/BuilderPatternUnitTest.kt rename to core-kotlin-modules/core-kotlin-design-patterns/src/test/kotlin/com/baeldung/builder/BuilderPatternUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-lang-2/README.md b/core-kotlin-modules/core-kotlin-lang-2/README.md index e64a39cb9b..76d490226f 100644 --- a/core-kotlin-modules/core-kotlin-lang-2/README.md +++ b/core-kotlin-modules/core-kotlin-lang-2/README.md @@ -10,4 +10,5 @@ This module contains articles about core features in the Kotlin language. - [Initializing Arrays in Kotlin](https://www.baeldung.com/kotlin-initialize-array) - [Lazy Initialization in Kotlin](https://www.baeldung.com/kotlin-lazy-initialization) - [Comprehensive Guide to Null Safety in Kotlin](https://www.baeldung.com/kotlin-null-safety) +- [Kotlin Scope Functions](https://www.baeldung.com/kotlin-scope-functions) - [[<-- Prev]](/core-kotlin-modules/core-kotlin-lang) diff --git a/core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt b/core-kotlin-modules/core-kotlin-lang-2/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt rename to core-kotlin-modules/core-kotlin-lang-2/src/main/kotlin/com/baeldung/scope/ScopeFunctions.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt b/core-kotlin-modules/core-kotlin-lang-2/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt rename to core-kotlin-modules/core-kotlin-lang-2/src/test/kotlin/com/baeldung/scope/ScopeFunctionsUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin-testing/README.md b/core-kotlin-modules/core-kotlin-testing/README.md new file mode 100644 index 0000000000..f4d89593a7 --- /dev/null +++ b/core-kotlin-modules/core-kotlin-testing/README.md @@ -0,0 +1,6 @@ +## Core Kotlin Testing + +This module contains articles about testing in Kotlin + +### Relevant articles: +- [JUnit 5 for Kotlin Developers](https://www.baeldung.com/junit-5-kotlin) \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin-2/pom.xml b/core-kotlin-modules/core-kotlin-testing/pom.xml similarity index 64% rename from core-kotlin-modules/core-kotlin-2/pom.xml rename to core-kotlin-modules/core-kotlin-testing/pom.xml index ae6e2d175a..d38bc62409 100644 --- a/core-kotlin-modules/core-kotlin-2/pom.xml +++ b/core-kotlin-modules/core-kotlin-testing/pom.xml @@ -3,8 +3,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - core-kotlin-2 - core-kotlin-2 + core-kotlin-testing + core-kotlin-testing jar @@ -15,11 +15,15 @@ - org.assertj - assertj-core - ${assertj.version} + org.junit.platform + junit-platform-runner + ${junit.platform.version} test + + 1.1.1 + + \ No newline at end of file diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/Calculator.kt b/core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/Calculator.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/Calculator.kt rename to core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/Calculator.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/CalculatorUnitTest.kt b/core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/CalculatorUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/CalculatorUnitTest.kt rename to core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/CalculatorUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/DivideByZeroException.kt b/core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/DivideByZeroException.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/DivideByZeroException.kt rename to core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/DivideByZeroException.kt diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/SimpleUnitTest.kt b/core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/SimpleUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/junit5/SimpleUnitTest.kt rename to core-kotlin-modules/core-kotlin-testing/src/test/kotlin/com/baeldung/junit5/SimpleUnitTest.kt diff --git a/core-kotlin-modules/core-kotlin/README.md b/core-kotlin-modules/core-kotlin/README.md index 90caccf5c8..48d19c987a 100644 --- a/core-kotlin-modules/core-kotlin/README.md +++ b/core-kotlin-modules/core-kotlin/README.md @@ -7,13 +7,5 @@ This module contains articles about Kotlin core features. - [Kotlin Java Interoperability](https://www.baeldung.com/kotlin-java-interoperability) - [Get a Random Number in Kotlin](https://www.baeldung.com/kotlin-random-number) - [Create a Java and Kotlin Project with Maven](https://www.baeldung.com/kotlin-maven-java-project) -- [Guide to Sorting in Kotlin](https://www.baeldung.com/kotlin-sort) -- [Creational Design Patterns in Kotlin: Builder](https://www.baeldung.com/kotlin-builder-pattern) -- [Kotlin Scope Functions](https://www.baeldung.com/kotlin-scope-functions) -- [Implementing a Binary Tree in Kotlin](https://www.baeldung.com/kotlin-binary-tree) -- [JUnit 5 for Kotlin Developers](https://www.baeldung.com/junit-5-kotlin) -- [Converting Kotlin Data Class from JSON using GSON](https://www.baeldung.com/kotlin-json-convert-data-class) -- [Fuel HTTP Library with Kotlin](https://www.baeldung.com/kotlin-fuel) -- [Introduction to Kovenant Library for Kotlin](https://www.baeldung.com/kotlin-kovenant) -- [Dependency Injection for Kotlin with Injekt](https://www.baeldung.com/kotlin-dependency-injection-with-injekt) -- [[More --> ]](/core-kotlin-modules/core-kotlin-2) +- [Kotlin Ternary Conditional Operator](https://www.baeldung.com/kotlin-ternary-operator) +- [Sequences in Kotlin](https://www.baeldung.com/kotlin/sequences) diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/nullassertion/NotNullAssertionUnitTest.kt b/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/nullassertion/NotNullAssertionUnitTest.kt new file mode 100644 index 0000000000..434f177927 --- /dev/null +++ b/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/nullassertion/NotNullAssertionUnitTest.kt @@ -0,0 +1,20 @@ +package com.baeldung.nullassertion + +import org.junit.Test +import kotlin.test.assertEquals + +class NotNullAssertionUnitTest { + + @Test + fun givenNullableValue_WhenNotNull_ShouldExtractTheValue() { + val answer: String? = "42" + + assertEquals(42, answer!!.toInt()) + } + + @Test(expected = KotlinNullPointerException::class) + fun givenNullableValue_WhenIsNull_ThenShouldThrow() { + val noAnswer: String? = null + noAnswer!! + } +} diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/sequences/SequencesTest.kt b/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/sequences/SequencesTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/sequences/SequencesTest.kt rename to core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/sequences/SequencesTest.kt diff --git a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt b/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt similarity index 74% rename from core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt rename to core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt index 21dfdd2ae0..347290de72 100644 --- a/core-kotlin-modules/core-kotlin-2/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt +++ b/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/ternary/TernaryOperatorTest.kt @@ -21,4 +21,12 @@ class TernaryOperatorTest { } assertEquals("yes", result) } + + @Test + fun `using elvis`() { + val a: String? = null + val result = a ?: "Default" + + assertEquals("Default", result) + } } \ No newline at end of file diff --git a/core-kotlin-modules/pom.xml b/core-kotlin-modules/pom.xml index de41aecf73..8b626e1c1b 100644 --- a/core-kotlin-modules/pom.xml +++ b/core-kotlin-modules/pom.xml @@ -18,17 +18,19 @@ core-kotlin - core-kotlin-2 core-kotlin-advanced core-kotlin-annotations core-kotlin-collections core-kotlin-concurrency + core-kotlin-date-time + core-kotlin-design-patterns core-kotlin-io core-kotlin-lang core-kotlin-lang-2 - core-kotlin-strings core-kotlin-lang-oop core-kotlin-lang-oop-2 + core-kotlin-strings + core-kotlin-testing diff --git a/data-structures/README.md b/data-structures/README.md index f9ca78679a..e3436695ce 100644 --- a/data-structures/README.md +++ b/data-structures/README.md @@ -10,3 +10,4 @@ This module contains articles about data structures in Java - [How to Print a Binary Tree Diagram](https://www.baeldung.com/java-print-binary-tree-diagram) - [Introduction to Big Queue](https://www.baeldung.com/java-big-queue) - [Guide to AVL Trees in Java](https://www.baeldung.com/java-avl-trees) +- [Graphs in Java](https://www.baeldung.com/java-graphs) diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/graph/Graph.java b/data-structures/src/main/java/com/baeldung/graph/Graph.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/graph/Graph.java rename to data-structures/src/main/java/com/baeldung/graph/Graph.java diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/graph/GraphTraversal.java b/data-structures/src/main/java/com/baeldung/graph/GraphTraversal.java similarity index 100% rename from core-java-modules/core-java/src/main/java/com/baeldung/graph/GraphTraversal.java rename to data-structures/src/main/java/com/baeldung/graph/GraphTraversal.java diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/graph/GraphUnitTest.java b/data-structures/src/test/java/com/baeldung/graph/GraphUnitTest.java similarity index 100% rename from core-java-modules/core-java/src/test/java/com/baeldung/graph/GraphUnitTest.java rename to data-structures/src/test/java/com/baeldung/graph/GraphUnitTest.java diff --git a/ddd-modules/infrastructure/pom.xml b/ddd-modules/infrastructure/pom.xml index c301eaa92a..abf90935c3 100644 --- a/ddd-modules/infrastructure/pom.xml +++ b/ddd-modules/infrastructure/pom.xml @@ -12,8 +12,9 @@ com.baeldung.dddmodules - dddmodules + ddd-modules 1.0 + ../ diff --git a/ddd-modules/mainapp/pom.xml b/ddd-modules/mainapp/pom.xml index a048263d37..59d2ad7d3a 100644 --- a/ddd-modules/mainapp/pom.xml +++ b/ddd-modules/mainapp/pom.xml @@ -11,8 +11,9 @@ com.baeldung.dddmodules - dddmodules + ddd-modules 1.0 + ../ diff --git a/ddd-modules/ordercontext/pom.xml b/ddd-modules/ordercontext/pom.xml index abd166fb69..8dee3a5148 100644 --- a/ddd-modules/ordercontext/pom.xml +++ b/ddd-modules/ordercontext/pom.xml @@ -11,8 +11,9 @@ com.baeldung.dddmodules - dddmodules + ddd-modules 1.0 + ../ diff --git a/ddd-modules/pom.xml b/ddd-modules/pom.xml index c6dd6e1f25..6ab1829198 100644 --- a/ddd-modules/pom.xml +++ b/ddd-modules/pom.xml @@ -28,9 +28,15 @@ - junit - junit - ${junit.version} + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + test + + + org.junit.vintage + junit-vintage-engine + ${junit-jupiter.version} test @@ -56,15 +62,31 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + 0 + + + - 3.8.1 + UTF-8 + 9 9 - UTF-8 - 3.12.2 + + 3.8.1 + 2.22.2 + 1.0 + + 5.6.2 + 3.12.2 diff --git a/ddd-modules/sharedkernel/pom.xml b/ddd-modules/sharedkernel/pom.xml index a61f03a494..1afddf1e22 100644 --- a/ddd-modules/sharedkernel/pom.xml +++ b/ddd-modules/sharedkernel/pom.xml @@ -11,8 +11,9 @@ com.baeldung.dddmodules - dddmodules + ddd-modules 1.0 + ../ diff --git a/ddd-modules/shippingcontext/pom.xml b/ddd-modules/shippingcontext/pom.xml index 2096923f90..25b5882ef1 100644 --- a/ddd-modules/shippingcontext/pom.xml +++ b/ddd-modules/shippingcontext/pom.xml @@ -11,8 +11,9 @@ com.baeldung.dddmodules - dddmodules + ddd-modules 1.0 + ../ diff --git a/ddd/pom.xml b/ddd/pom.xml index 1253f2ac48..422f9ccd15 100644 --- a/ddd/pom.xml +++ b/ddd/pom.xml @@ -17,6 +17,35 @@ ../parent-boot-2 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + + + + + org.junit + junit-bom + ${junit-jupiter.version} + pom + import + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.springframework.boot @@ -26,24 +55,6 @@ org.springframework.boot spring-boot-starter-data-cassandra - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - - - org.junit.platform - junit-platform-launcher - ${junit-platform.version} - test - org.joda joda-money @@ -95,7 +106,10 @@ - 1.0.1 - + 2.22.2 + 1.0.1 + + 5.6.2 + diff --git a/jhipster-5/bookstore-monolith/pom.xml b/jhipster-5/bookstore-monolith/pom.xml index 233765e0f3..4e4c82f327 100644 --- a/jhipster-5/bookstore-monolith/pom.xml +++ b/jhipster-5/bookstore-monolith/pom.xml @@ -225,6 +225,12 @@ io.dropwizard.metrics metrics-core + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + diff --git a/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java index d5c9e577a6..fd6e04dadc 100644 --- a/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java +++ b/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java @@ -14,6 +14,7 @@ import org.zalando.problem.Problem; import org.zalando.problem.ProblemBuilder; import org.zalando.problem.Status; import org.zalando.problem.spring.web.advice.ProblemHandling; +import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; import org.zalando.problem.violations.ConstraintViolationProblem; import javax.annotation.Nonnull; @@ -28,7 +29,7 @@ import java.util.stream.Collectors; * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807) */ @ControllerAdvice -public class ExceptionTranslator implements ProblemHandling { +public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { /** * Post-process the Problem payload to add the message key for the front-end if needed diff --git a/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java index 18baa42736..3bf4995405 100644 --- a/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java +++ b/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java @@ -14,6 +14,7 @@ import org.zalando.problem.Problem; import org.zalando.problem.ProblemBuilder; import org.zalando.problem.Status; import org.zalando.problem.spring.web.advice.ProblemHandling; +import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; import org.zalando.problem.violations.ConstraintViolationProblem; import javax.annotation.Nonnull; @@ -28,7 +29,7 @@ import java.util.stream.Collectors; * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807) */ @ControllerAdvice -public class ExceptionTranslator implements ProblemHandling { +public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { /** * Post-process the Problem payload to add the message key for the front-end if needed diff --git a/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java index 320636c51d..6af9fd126c 100644 --- a/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java +++ b/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java @@ -14,6 +14,7 @@ import org.zalando.problem.Problem; import org.zalando.problem.ProblemBuilder; import org.zalando.problem.Status; import org.zalando.problem.spring.web.advice.ProblemHandling; +import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; import org.zalando.problem.violations.ConstraintViolationProblem; import javax.annotation.Nonnull; @@ -28,7 +29,7 @@ import java.util.stream.Collectors; * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807) */ @ControllerAdvice -public class ExceptionTranslator implements ProblemHandling { +public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { /** * Post-process the Problem payload to add the message key for the front-end if needed diff --git a/jmh/src/main/java/com/baeldung/BenchMark.java b/jmh/src/main/java/com/baeldung/BenchMark.java index b0e1caf4dc..3c5c840db2 100644 --- a/jmh/src/main/java/com/baeldung/BenchMark.java +++ b/jmh/src/main/java/com/baeldung/BenchMark.java @@ -1,14 +1,20 @@ package com.baeldung; -import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; public class BenchMark { + @State(Scope.Benchmark) + public static class Log { + public int x = 8; + } + @State(Scope.Benchmark) public static class ExecutionPlan { @@ -45,4 +51,44 @@ public class BenchMark { // Do nothing } + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.AverageTime) + public void doNothing() { + + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.AverageTime) + public void objectCreation() { + new Object(); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.AverageTime) + public Object pillarsOfCreation() { + return new Object(); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @BenchmarkMode(Mode.AverageTime) + public void blackHole(Blackhole blackhole) { + blackhole.consume(new Object()); + } + + @Benchmark + public double foldedLog() { + int x = 8; + + return Math.log(x); + } + + @Benchmark + public double log(Log input) { + return Math.log(input.x); + } + } diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt b/kotlin-libraries-2/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt similarity index 100% rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt rename to kotlin-libraries-2/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt diff --git a/language-interop/README.md b/language-interop/README.md index a28c4a5405..f4f2fc0816 100644 --- a/language-interop/README.md +++ b/language-interop/README.md @@ -3,3 +3,6 @@ This module contains articles about Java interop with other language integrations. ### Relevant Articles: + +- [How to Call Python From Java](https://www.baeldung.com/java-working-with-python) +- [Introduction to Nashorn](http://www.baeldung.com/java-nashorn) diff --git a/core-java-modules/core-java/src/main/resources/js/bind.js b/language-interop/src/main/resources/js/bind.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/bind.js rename to language-interop/src/main/resources/js/bind.js diff --git a/core-java-modules/core-java/src/main/resources/js/locations.js b/language-interop/src/main/resources/js/locations.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/locations.js rename to language-interop/src/main/resources/js/locations.js diff --git a/core-java-modules/core-java/src/main/resources/js/math_module.js b/language-interop/src/main/resources/js/math_module.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/math_module.js rename to language-interop/src/main/resources/js/math_module.js diff --git a/core-java-modules/core-java/src/main/resources/js/no_such.js b/language-interop/src/main/resources/js/no_such.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/no_such.js rename to language-interop/src/main/resources/js/no_such.js diff --git a/core-java-modules/core-java/src/main/resources/js/script.js b/language-interop/src/main/resources/js/script.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/script.js rename to language-interop/src/main/resources/js/script.js diff --git a/core-java-modules/core-java/src/main/resources/js/trim.js b/language-interop/src/main/resources/js/trim.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/trim.js rename to language-interop/src/main/resources/js/trim.js diff --git a/core-java-modules/core-java/src/main/resources/js/typed_arrays.js b/language-interop/src/main/resources/js/typed_arrays.js similarity index 100% rename from core-java-modules/core-java/src/main/resources/js/typed_arrays.js rename to language-interop/src/main/resources/js/typed_arrays.js diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java b/language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java similarity index 94% rename from core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java rename to language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java index 9abe8a927c..a9e4243f9d 100644 --- a/core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java +++ b/language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java @@ -1,14 +1,10 @@ -package com.baeldung.scripting; +package com.baeldung.language.interop.javascript; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import javax.script.Bindings; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; +import javax.script.*; import java.io.InputStreamReader; import java.util.List; import java.util.Map; diff --git a/libraries-data-2/pom.xml b/libraries-data-2/pom.xml index bdfb2c5ed6..ac23747caa 100644 --- a/libraries-data-2/pom.xml +++ b/libraries-data-2/pom.xml @@ -153,6 +153,19 @@ renjin-script-engine ${renjin.version} + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + + + org.apache.kafka + kafka-clients + ${kafka.version} + test + test + diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java new file mode 100644 index 0000000000..1c77226037 --- /dev/null +++ b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java @@ -0,0 +1,17 @@ +package com.baeldung.kafka.producer; + +import org.apache.kafka.clients.producer.internals.DefaultPartitioner; +import org.apache.kafka.common.Cluster; + +public class EvenOddPartitioner extends DefaultPartitioner { + + @Override + public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { + + if (((String) key).length() % 2 == 0) { + return 0; + } + + return 1; + } +} diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java new file mode 100644 index 0000000000..911c9ed3d7 --- /dev/null +++ b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java @@ -0,0 +1,40 @@ +package com.baeldung.kafka.producer; + +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; + +import java.util.concurrent.Future; + +public class KafkaProducer { + + private final Producer producer; + + public KafkaProducer(Producer producer) { + this.producer = producer; + } + + public Future send(String key, String value) { + ProducerRecord record = new ProducerRecord("topic_sports_news", + key, value); + return producer.send(record); + } + + public void flush() { + producer.flush(); + } + + public void beginTransaction() { + producer.beginTransaction(); + } + + public void initTransaction() { + producer.initTransactions(); + } + + public void commitTransaction() { + producer.commitTransaction(); + } + + +} diff --git a/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java b/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java new file mode 100644 index 0000000000..a7156ed886 --- /dev/null +++ b/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java @@ -0,0 +1,116 @@ +package com.baeldung.kafka.producer; + +import com.baeldung.kafka.producer.EvenOddPartitioner; +import com.baeldung.kafka.producer.KafkaProducer; +import org.apache.kafka.clients.producer.MockProducer; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.Cluster; +import org.apache.kafka.common.Node; +import org.apache.kafka.common.PartitionInfo; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import static java.util.Collections.emptySet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class KafkaProducerUnitTest { + + private final String TOPIC_NAME = "topic_sports_news"; + + private KafkaProducer kafkaProducer; + private MockProducer mockProducer; + + private void buildMockProducer(boolean autoComplete) { + this.mockProducer = new MockProducer<>(autoComplete, new StringSerializer(), new StringSerializer()); + } + + @Test + void givenKeyValue_whenSend_thenVerifyHistory() throws ExecutionException, InterruptedException { + + buildMockProducer(true); + //when + kafkaProducer = new KafkaProducer(mockProducer); + Future recordMetadataFuture = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}"); + + //then + assertTrue(mockProducer.history().size() == 1); + assertTrue(mockProducer.history().get(0).key().equalsIgnoreCase("data")); + assertTrue(recordMetadataFuture.get().partition() == 0); + + } + + @Test + void givenKeyValue_whenSend_thenSendOnlyAfterFlush() { + + buildMockProducer(false); + //when + kafkaProducer = new KafkaProducer(mockProducer); + Future record = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}"); + assertFalse(record.isDone()); + + //then + kafkaProducer.flush(); + assertTrue(record.isDone()); + } + + @Test + void givenKeyValue_whenSend_thenReturnException() { + + buildMockProducer(false); + //when + kafkaProducer = new KafkaProducer(mockProducer); + Future record = kafkaProducer.send("site", "{\"site\" : \"baeldung\"}"); + RuntimeException e = new RuntimeException(); + mockProducer.errorNext(e); + //then + try { + record.get(); + } catch (ExecutionException | InterruptedException ex) { + assertEquals(e, ex.getCause()); + } + assertTrue(record.isDone()); + } + + @Test + void givenKeyValue_whenSendWithTxn_thenSendOnlyOnTxnCommit() { + + buildMockProducer(true); + //when + kafkaProducer = new KafkaProducer(mockProducer); + kafkaProducer.initTransaction(); + kafkaProducer.beginTransaction(); + Future record = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}"); + + //then + assertTrue(mockProducer.history().isEmpty()); + kafkaProducer.commitTransaction(); + assertTrue(mockProducer.history().size() == 1); + } + + @Test + void givenKeyValue_whenSendWithPartitioning_thenVerifyPartitionNumber() throws ExecutionException, InterruptedException { + + PartitionInfo partitionInfo0 = new PartitionInfo(TOPIC_NAME, 0, null, null, null); + PartitionInfo partitionInfo1 = new PartitionInfo(TOPIC_NAME, 1, null, null, null); + List list = new ArrayList<>(); + list.add(partitionInfo0); + list.add(partitionInfo1); + Cluster cluster = new Cluster("kafkab", new ArrayList(), list, emptySet(), emptySet()); + this.mockProducer = new MockProducer<>(cluster, true, new EvenOddPartitioner(), new StringSerializer(), new StringSerializer()); + //when + kafkaProducer = new KafkaProducer(mockProducer); + Future recordMetadataFuture = kafkaProducer.send("partition", "{\"site\" : \"baeldung\"}"); + + //then + assertTrue(recordMetadataFuture.get().partition() == 1); + + } + +} \ No newline at end of file diff --git a/libraries-data-db/pom.xml b/libraries-data-db/pom.xml index f028ffe8c3..d51580ccbc 100644 --- a/libraries-data-db/pom.xml +++ b/libraries-data-db/pom.xml @@ -211,7 +211,7 @@ 5.0.2 5.0.4 3.2.0-m7 - 2.7.2 + 3.4.5 11.22.4 diff --git a/libraries-rpc/README.md b/libraries-rpc/README.md new file mode 100644 index 0000000000..472aa883ad --- /dev/null +++ b/libraries-rpc/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Introduction to Finagle](https://www.baeldung.com/java-finagle) diff --git a/libraries-server-2/.gitignore b/libraries-server-2/.gitignore new file mode 100644 index 0000000000..e594daf27a --- /dev/null +++ b/libraries-server-2/.gitignore @@ -0,0 +1,9 @@ +*.class + +# Folders # +/gensrc +/target + +# Packaged files # +*.jar +/bin/ diff --git a/libraries-server-2/README.md b/libraries-server-2/README.md new file mode 100644 index 0000000000..38166bcd77 --- /dev/null +++ b/libraries-server-2/README.md @@ -0,0 +1,8 @@ +## Server + +This module contains articles about server libraries. + +### Relevant Articles: + +- [HTTP/2 in Jetty](https://www.baeldung.com/jetty-http-2) +- More articles: [[<-- prev]](../libraries-server) diff --git a/libraries-server-2/pom.xml b/libraries-server-2/pom.xml new file mode 100644 index 0000000000..5f500a7ced --- /dev/null +++ b/libraries-server-2/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + libraries-server-2 + 0.0.1-SNAPSHOT + libraries-server-2 + war + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty.version} + + 8888 + quit + + -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar + + ${basedir}/src/main/config/jetty.xml + + / + + + + + org.eclipse.jetty.http2 + http2-server + ${jetty.version} + + + org.eclipse.jetty + jetty-alpn-openjdk8-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlets + ${jetty.version} + + + + + + + + 9.4.27.v20200227 + 8.1.11.v20170118 + + + \ No newline at end of file diff --git a/libraries-server/src/main/config/jetty.xml b/libraries-server-2/src/main/config/jetty.xml similarity index 100% rename from libraries-server/src/main/config/jetty.xml rename to libraries-server-2/src/main/config/jetty.xml diff --git a/libraries-server/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java b/libraries-server-2/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java similarity index 100% rename from libraries-server/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java rename to libraries-server-2/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java diff --git a/libraries-server/src/main/resources/keystore.jks b/libraries-server-2/src/main/resources/keystore.jks similarity index 100% rename from libraries-server/src/main/resources/keystore.jks rename to libraries-server-2/src/main/resources/keystore.jks diff --git a/libraries-server-2/src/main/resources/logback.xml b/libraries-server-2/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/libraries-server-2/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/libraries-server/src/main/resources/truststore.jks b/libraries-server-2/src/main/resources/truststore.jks similarity index 100% rename from libraries-server/src/main/resources/truststore.jks rename to libraries-server-2/src/main/resources/truststore.jks diff --git a/libraries-server/src/main/webapp/WEB-INF/web.xml b/libraries-server-2/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from libraries-server/src/main/webapp/WEB-INF/web.xml rename to libraries-server-2/src/main/webapp/WEB-INF/web.xml diff --git a/libraries-server/src/main/webapp/http2.html b/libraries-server-2/src/main/webapp/http2.html similarity index 100% rename from libraries-server/src/main/webapp/http2.html rename to libraries-server-2/src/main/webapp/http2.html diff --git a/libraries-server/src/main/webapp/images/homepage-latest_articles.jpg b/libraries-server-2/src/main/webapp/images/homepage-latest_articles.jpg similarity index 100% rename from libraries-server/src/main/webapp/images/homepage-latest_articles.jpg rename to libraries-server-2/src/main/webapp/images/homepage-latest_articles.jpg diff --git a/libraries-server/src/main/webapp/images/homepage-rest_with_spring.jpg b/libraries-server-2/src/main/webapp/images/homepage-rest_with_spring.jpg similarity index 100% rename from libraries-server/src/main/webapp/images/homepage-rest_with_spring.jpg rename to libraries-server-2/src/main/webapp/images/homepage-rest_with_spring.jpg diff --git a/libraries-server/src/main/webapp/images/homepage-weekly_reviews.jpg b/libraries-server-2/src/main/webapp/images/homepage-weekly_reviews.jpg similarity index 100% rename from libraries-server/src/main/webapp/images/homepage-weekly_reviews.jpg rename to libraries-server-2/src/main/webapp/images/homepage-weekly_reviews.jpg diff --git a/libraries-server/src/main/webapp/index.html b/libraries-server-2/src/main/webapp/index.html similarity index 100% rename from libraries-server/src/main/webapp/index.html rename to libraries-server-2/src/main/webapp/index.html diff --git a/libraries-server/README.md b/libraries-server/README.md index 7e41f33a0c..570806611f 100644 --- a/libraries-server/README.md +++ b/libraries-server/README.md @@ -13,4 +13,4 @@ This module contains articles about server libraries. - [MQTT Client in Java](https://www.baeldung.com/java-mqtt-client) - [Guide to XMPP Smack Client](https://www.baeldung.com/xmpp-smack-chat-client) - [A Guide to NanoHTTPD](https://www.baeldung.com/nanohttpd) -- [HTTP/2 in Jetty](https://www.baeldung.com/jetty-http-2) +- More articles: [[more -->]](../libraries-server-2) \ No newline at end of file diff --git a/libraries-server/pom.xml b/libraries-server/pom.xml index eb9cb61e56..d9546f1678 100644 --- a/libraries-server/pom.xml +++ b/libraries-server/pom.xml @@ -5,7 +5,6 @@ libraries-server 0.0.1-SNAPSHOT libraries-server - war com.baeldung @@ -107,50 +106,11 @@ - - - - org.eclipse.jetty - jetty-maven-plugin - ${jetty.version} - - 8888 - quit - - -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar - - ${basedir}/src/main/config/jetty.xml - - / - - - - - org.eclipse.jetty.http2 - http2-server - ${jetty.version} - - - org.eclipse.jetty - jetty-alpn-openjdk8-server - ${jetty.version} - - - org.eclipse.jetty - jetty-servlets - ${jetty.version} - - - - - - 3.6.2 4.5.3 9.4.27.v20200227 4.1.20.Final - 8.1.11.v20170118 8.5.24 4.3.1 1.2.0 diff --git a/libraries-testing/pom.xml b/libraries-testing/pom.xml index 89cb0bd494..5a5cb99238 100644 --- a/libraries-testing/pom.xml +++ b/libraries-testing/pom.xml @@ -151,6 +151,13 @@ test + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + + diff --git a/maven-all/versions-maven-plugin/original/pom.xml b/maven-all/versions-maven-plugin/original/pom.xml index 54140aec9b..f81596661e 100644 --- a/maven-all/versions-maven-plugin/original/pom.xml +++ b/maven-all/versions-maven-plugin/original/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung - versions-maven-plugin-example + original 0.0.1-SNAPSHOT diff --git a/netflix-modules/genie/README.md b/netflix-modules/genie/README.md new file mode 100644 index 0000000000..f6e15ba403 --- /dev/null +++ b/netflix-modules/genie/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Introduction to Netflix Genie](https://www.baeldung.com/netflix-genie-intro) diff --git a/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java b/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java new file mode 100644 index 0000000000..abefbed0d9 --- /dev/null +++ b/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java @@ -0,0 +1,97 @@ +package com.baeldung.http.server; + +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.CharsetUtil; + +public class CustomHttpServerHandler extends SimpleChannelInboundHandler { + + private HttpRequest request; + StringBuilder responseData = new StringBuilder(); + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.flush(); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof HttpRequest) { + HttpRequest request = this.request = (HttpRequest) msg; + + if (HttpUtil.is100ContinueExpected(request)) { + writeResponse(ctx); + } + + responseData.setLength(0); + responseData.append(RequestUtils.formatParams(request)); + } + + responseData.append(RequestUtils.evaluateDecoderResult(request)); + + if (msg instanceof HttpContent) { + HttpContent httpContent = (HttpContent) msg; + + responseData.append(RequestUtils.formatBody(httpContent)); + responseData.append(RequestUtils.evaluateDecoderResult(request)); + + if (msg instanceof LastHttpContent) { + LastHttpContent trailer = (LastHttpContent) msg; + responseData.append(RequestUtils.prepareLastResponse(request, trailer)); + writeResponse(ctx, trailer, responseData); + } + } + } + + private void writeResponse(ChannelHandlerContext ctx) { + FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER); + ctx.write(response); + } + + private void writeResponse(ChannelHandlerContext ctx, LastHttpContent trailer, StringBuilder responseData) { + boolean keepAlive = HttpUtil.isKeepAlive(request); + + FullHttpResponse httpResponse = new DefaultFullHttpResponse(HTTP_1_1, ((HttpObject) trailer).decoderResult() + .isSuccess() ? OK : BAD_REQUEST, Unpooled.copiedBuffer(responseData.toString(), CharsetUtil.UTF_8)); + + httpResponse.headers() + .set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); + + if (keepAlive) { + httpResponse.headers() + .setInt(HttpHeaderNames.CONTENT_LENGTH, httpResponse.content() + .readableBytes()); + httpResponse.headers() + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + } + + ctx.write(httpResponse); + + if (!keepAlive) { + ctx.writeAndFlush(Unpooled.EMPTY_BUFFER) + .addListener(ChannelFutureListener.CLOSE); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } +} diff --git a/netty/src/main/java/com/baeldung/http/server/HttpServer.java b/netty/src/main/java/com/baeldung/http/server/HttpServer.java new file mode 100644 index 0000000000..529d14f0fd --- /dev/null +++ b/netty/src/main/java/com/baeldung/http/server/HttpServer.java @@ -0,0 +1,64 @@ +package com.baeldung.http.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; + +public class HttpServer { + + private int port; + static Logger logger = LoggerFactory.getLogger(HttpServer.class); + + public HttpServer(int port) { + this.port = port; + } + + public static void main(String[] args) throws Exception { + + int port = args.length > 0 ? Integer.parseInt(args[0]) : 8080; + + new HttpServer(port).run(); + } + + public void run() throws Exception { + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new HttpRequestDecoder()); + p.addLast(new HttpResponseEncoder()); + p.addLast(new CustomHttpServerHandler()); + } + }); + + ChannelFuture f = b.bind(port) + .sync(); + f.channel() + .closeFuture() + .sync(); + + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} diff --git a/netty/src/main/java/com/baeldung/http/server/RequestUtils.java b/netty/src/main/java/com/baeldung/http/server/RequestUtils.java new file mode 100644 index 0000000000..92ec2242f3 --- /dev/null +++ b/netty/src/main/java/com/baeldung/http/server/RequestUtils.java @@ -0,0 +1,86 @@ +package com.baeldung.http.server; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.DecoderResult; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.util.CharsetUtil; + +class RequestUtils { + + static StringBuilder formatParams(HttpRequest request) { + StringBuilder responseData = new StringBuilder(); + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri()); + Map> params = queryStringDecoder.parameters(); + if (!params.isEmpty()) { + for (Entry> p : params.entrySet()) { + String key = p.getKey(); + List vals = p.getValue(); + for (String val : vals) { + responseData.append("Parameter: ") + .append(key.toUpperCase()) + .append(" = ") + .append(val.toUpperCase()) + .append("\r\n"); + } + } + responseData.append("\r\n"); + } + return responseData; + } + + static StringBuilder formatBody(HttpContent httpContent) { + StringBuilder responseData = new StringBuilder(); + ByteBuf content = httpContent.content(); + if (content.isReadable()) { + responseData.append(content.toString(CharsetUtil.UTF_8) + .toUpperCase()); + responseData.append("\r\n"); + } + return responseData; + } + + static StringBuilder evaluateDecoderResult(HttpObject o) { + StringBuilder responseData = new StringBuilder(); + DecoderResult result = o.decoderResult(); + + if (!result.isSuccess()) { + responseData.append("..Decoder Failure: "); + responseData.append(result.cause()); + responseData.append("\r\n"); + } + + return responseData; + } + + static StringBuilder prepareLastResponse(HttpRequest request, LastHttpContent trailer) { + StringBuilder responseData = new StringBuilder(); + responseData.append("Good Bye!\r\n"); + + if (!trailer.trailingHeaders() + .isEmpty()) { + responseData.append("\r\n"); + for (CharSequence name : trailer.trailingHeaders() + .names()) { + for (CharSequence value : trailer.trailingHeaders() + .getAll(name)) { + responseData.append("P.S. Trailing Header: "); + responseData.append(name) + .append(" = ") + .append(value) + .append("\r\n"); + } + } + responseData.append("\r\n"); + } + return responseData; + } + +} diff --git a/netty/src/test/java/com/baeldung/http/server/HttpServerLiveTest.java b/netty/src/test/java/com/baeldung/http/server/HttpServerLiveTest.java new file mode 100644 index 0000000000..77f1288838 --- /dev/null +++ b/netty/src/test/java/com/baeldung/http/server/HttpServerLiveTest.java @@ -0,0 +1,177 @@ +package com.baeldung.http.server; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.cookie.ClientCookieEncoder; +import io.netty.handler.codec.http.cookie.DefaultCookie; +import io.netty.util.CharsetUtil; + +//Ensure the server class - HttpServer.java is already started before running this test +public class HttpServerLiveTest { + + private static final String HOST = "127.0.0.1"; + private static final int PORT = 8080; + private Channel channel; + private EventLoopGroup group = new NioEventLoopGroup(); + ResponseAggregator response = new ResponseAggregator(); + + @Before + public void setup() throws Exception { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new HttpClientCodec()); + p.addLast(new HttpContentDecompressor()); + p.addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { + response = prepareResponse(ctx, msg, response); + } + }); + } + }); + + channel = b.connect(HOST, PORT) + .sync() + .channel(); + } + + @Test + public void whenPostSent_thenContentReceivedInUppercase() throws Exception { + String body = "Hello World!"; + + DefaultFullHttpRequest request = createRequest(body); + + channel.writeAndFlush(request); + Thread.sleep(200); + + assertEquals(200, response.getStatus()); + assertEquals("HTTP/1.1", response.getVersion()); + + assertTrue(response.getContent() + .contains(body.toUpperCase())); + } + + @Test + public void whenGetSent_thenResponseOK() throws Exception { + DefaultFullHttpRequest request = createRequest(null); + + channel.writeAndFlush(request); + Thread.sleep(200); + + assertEquals(200, response.getStatus()); + assertEquals("HTTP/1.1", response.getVersion()); + + } + + @After + public void cleanup() throws InterruptedException { + channel.closeFuture() + .sync(); + group.shutdownGracefully(); + } + + private static DefaultFullHttpRequest createRequest(final CharSequence body) throws Exception { + DefaultFullHttpRequest request; + if (body != null) { + request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); + request.content() + .writeBytes(body.toString() + .getBytes(CharsetUtil.UTF_8.name())); + request.headers() + .set(HttpHeaderNames.CONTENT_TYPE, "application/json"); + request.headers() + .set(HttpHeaderNames.CONTENT_LENGTH, request.content() + .readableBytes()); + } else { + request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER); + request.headers() + .set(HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(new DefaultCookie("my-cookie", "foo"))); + } + + request.headers() + .set(HttpHeaderNames.HOST, HOST); + request.headers() + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + + return request; + } + + private static ResponseAggregator prepareResponse(ChannelHandlerContext ctx, HttpObject msg, ResponseAggregator responseAgg) { + + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; + + responseAgg.setStatus(response.status() + .code()); + + responseAgg.setVersion(response.protocolVersion() + .text()); + + if (!response.headers() + .isEmpty()) { + Map headers = new HashMap(); + for (CharSequence name : response.headers() + .names()) { + for (CharSequence value : response.headers() + .getAll(name)) { + headers.put(name.toString(), value.toString()); + } + } + responseAgg.setHeaders(headers); + } + if (HttpUtil.isTransferEncodingChunked(response)) { + responseAgg.setChunked(true); + } else { + responseAgg.setChunked(false); + } + } + if (msg instanceof HttpContent) { + HttpContent content = (HttpContent) msg; + String responseData = content.content() + .toString(CharsetUtil.UTF_8); + + if (content instanceof LastHttpContent) { + responseAgg.setContent(responseData + "} End Of Content"); + ctx.close(); + } + } + return responseAgg; + } +} diff --git a/netty/src/test/java/com/baeldung/http/server/ResponseAggregator.java b/netty/src/test/java/com/baeldung/http/server/ResponseAggregator.java new file mode 100644 index 0000000000..9f6e5e9823 --- /dev/null +++ b/netty/src/test/java/com/baeldung/http/server/ResponseAggregator.java @@ -0,0 +1,52 @@ +package com.baeldung.http.server; + +import java.util.Map; + +public class ResponseAggregator { + int status; + String version; + Map headers; + boolean isChunked; + String content; + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public boolean isChunked() { + return isChunked; + } + + public void setChunked(boolean isChunked) { + this.isChunked = isChunked; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/pdf/src/main/java/com/baeldung/pdf/PDFThymeleafExample.java b/pdf/src/main/java/com/baeldung/pdfthymeleaf/PDFThymeleafExample.java similarity index 97% rename from pdf/src/main/java/com/baeldung/pdf/PDFThymeleafExample.java rename to pdf/src/main/java/com/baeldung/pdfthymeleaf/PDFThymeleafExample.java index 2e1df1d320..28879b8958 100644 --- a/pdf/src/main/java/com/baeldung/pdf/PDFThymeleafExample.java +++ b/pdf/src/main/java/com/baeldung/pdfthymeleaf/PDFThymeleafExample.java @@ -1,4 +1,4 @@ -package com.baeldung.pdf; +package com.baeldung.pdfthymeleaf; import com.lowagie.text.DocumentException; import org.thymeleaf.TemplateEngine; diff --git a/pdf/src/test/java/com/baeldung/pdf/PDFThymeleafUnitTest.java b/pdf/src/test/java/com/baeldung/pdfthymeleaf/PDFThymeleafUnitTest.java similarity index 98% rename from pdf/src/test/java/com/baeldung/pdf/PDFThymeleafUnitTest.java rename to pdf/src/test/java/com/baeldung/pdfthymeleaf/PDFThymeleafUnitTest.java index e253dce06c..75d38fbf22 100644 --- a/pdf/src/test/java/com/baeldung/pdf/PDFThymeleafUnitTest.java +++ b/pdf/src/test/java/com/baeldung/pdfthymeleaf/PDFThymeleafUnitTest.java @@ -1,4 +1,4 @@ -package com.baeldung.pdf; +package com.baeldung.pdfthymeleaf; import com.lowagie.text.DocumentException; import org.junit.Test; diff --git a/persistence-modules/hibernate-enterprise/pom.xml b/persistence-modules/hibernate-enterprise/pom.xml index 060cb4c904..ae58e409c4 100644 --- a/persistence-modules/hibernate-enterprise/pom.xml +++ b/persistence-modules/hibernate-enterprise/pom.xml @@ -55,6 +55,12 @@ hibernate-testing ${hibernate.version} + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + diff --git a/persistence-modules/hibernate-exceptions/pom.xml b/persistence-modules/hibernate-exceptions/pom.xml new file mode 100644 index 0000000000..e5217e83bd --- /dev/null +++ b/persistence-modules/hibernate-exceptions/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + hibernate-exceptions + 0.0.1-SNAPSHOT + hibernate-exceptions + + + com.baeldung + persistence-modules + 1.0.0-SNAPSHOT + + + + + org.hsqldb + hsqldb + ${hsqldb.version} + + + org.hibernate + hibernate-core + ${hibernate.version} + + + javax.xml.bind + jaxb-api + ${jaxb.version} + + + + + 2.4.0 + 2.3.0 + 5.3.7.Final + + + diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateUtil.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateUtil.java new file mode 100644 index 0000000000..b84a512fd4 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateUtil.java @@ -0,0 +1,42 @@ +package com.baeldung.hibernate.exception.lazyinitialization; + +import java.util.Properties; + +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.service.ServiceRegistry; + +import com.baeldung.hibernate.exception.lazyinitialization.entity.Role; +import com.baeldung.hibernate.exception.lazyinitialization.entity.User; + +public class HibernateUtil { + private static SessionFactory sessionFactory; + + public static SessionFactory getSessionFactory() { + if (sessionFactory == null) { + try { + Configuration configuration = new Configuration(); + Properties settings = new Properties(); + settings.put(Environment.DRIVER, "org.hsqldb.jdbcDriver"); + settings.put(Environment.URL, "jdbc:hsqldb:mem:userrole"); + settings.put(Environment.USER, "sa"); + settings.put(Environment.PASS, ""); + settings.put(Environment.DIALECT, "org.hibernate.dialect.HSQLDialect"); + settings.put(Environment.SHOW_SQL, "true"); + settings.put(Environment.HBM2DDL_AUTO, "update"); + configuration.setProperties(settings); + configuration.addAnnotatedClass(User.class); + configuration.addAnnotatedClass(Role.class); + + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() + .applySettings(configuration.getProperties()).build(); + sessionFactory = configuration.buildSessionFactory(serviceRegistry); + } catch (Exception e) { + e.printStackTrace(); + } + } + return sessionFactory; + } +} \ No newline at end of file diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/Role.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/Role.java new file mode 100644 index 0000000000..c15e674c52 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/Role.java @@ -0,0 +1,45 @@ +package com.baeldung.hibernate.exception.lazyinitialization.entity; + +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 = "role") +public class Role { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "role_name") + private String roleName; + + public Role() { + + } + + public Role(String roleName) { + this.roleName = roleName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + +} diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/User.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/User.java new file mode 100644 index 0000000000..5bd7e00801 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/lazyinitialization/entity/User.java @@ -0,0 +1,55 @@ +package com.baeldung.hibernate.exception.lazyinitialization.entity; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "first_name") + private String firstName; + + @Column(name = "last_name") + private String lastName; + + @OneToMany + @JoinColumn(name = "user_id") + private Set roles = new HashSet<>(); + + public User() { + + } + + public User(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public int getId() { + return id; + } + + public Set getRoles() { + return roles; + } + + public void addRole(Role role) { + roles.add(role); + } + +} diff --git a/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateNoSessionUnitTest.java b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateNoSessionUnitTest.java new file mode 100644 index 0000000000..672c438c88 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/lazyinitialization/HibernateNoSessionUnitTest.java @@ -0,0 +1,85 @@ +package com.baeldung.hibernate.exception.lazyinitialization; + +import org.hibernate.LazyInitializationException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.baeldung.hibernate.exception.lazyinitialization.entity.Role; +import com.baeldung.hibernate.exception.lazyinitialization.entity.User; + +public class HibernateNoSessionUnitTest { + + private static SessionFactory sessionFactory; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(); + } + + @Test + public void whenAccessUserRolesInsideSession_thenSuccess() { + + User detachedUser = createUserWithRoles(); + + Session session = sessionFactory.openSession(); + session.beginTransaction(); + + User persistentUser = session.find(User.class, detachedUser.getId()); + + Assert.assertEquals(2, persistentUser.getRoles().size()); + + session.getTransaction().commit(); + session.close(); + } + + @Test + public void whenAccessUserRolesOutsideSession_thenThrownException() { + + User detachedUser = createUserWithRoles(); + + Session session = sessionFactory.openSession(); + session.beginTransaction(); + + User persistentUser = session.find(User.class, detachedUser.getId()); + + session.getTransaction().commit(); + session.close(); + + thrown.expect(LazyInitializationException.class); + System.out.println(persistentUser.getRoles().size()); + } + + private User createUserWithRoles() { + + Role admin = new Role("Admin"); + Role dba = new Role("DBA"); + + User user = new User("Bob", "Smith"); + + user.addRole(admin); + user.addRole(dba); + + Session session = sessionFactory.openSession(); + session.beginTransaction(); + user.getRoles().forEach(role -> session.save(role)); + session.save(user); + session.getTransaction().commit(); + session.close(); + + return user; + } + + @AfterClass + public static void cleanUp() { + sessionFactory.close(); + } +} diff --git a/persistence-modules/r2dbc/pom.xml b/persistence-modules/r2dbc/pom.xml index 70ff8d6a87..b1de88e9ea 100644 --- a/persistence-modules/r2dbc/pom.xml +++ b/persistence-modules/r2dbc/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung.examples.r2dbc - r2dbc-example + r2dbc 0.0.1-SNAPSHOT r2dbc Sample R2DBC Project diff --git a/persistence-modules/redis/README.md b/persistence-modules/redis/README.md index 71d009241a..75c1c18de4 100644 --- a/persistence-modules/redis/README.md +++ b/persistence-modules/redis/README.md @@ -4,3 +4,4 @@ - [Introduction to Lettuce – the Java Redis Client](https://www.baeldung.com/java-redis-lettuce) - [List All Available Redis Keys](https://www.baeldung.com/redis-list-available-keys) - [Spring Data Redis’s Property-Based Configuration](https://www.baeldung.com/spring-data-redis-properties) +- [Delete Everything in Redis](https://www.baeldung.com/redis-delete-data) diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml index c9206e5f92..dab7fc5654 100644 --- a/persistence-modules/redis/pom.xml +++ b/persistence-modules/redis/pom.xml @@ -33,7 +33,8 @@ redis.clients jedis - + 3.3.0 + com.github.kstyrc embedded-redis diff --git a/persistence-modules/redis/src/test/java/com/baeldung/redis/deleteeverything/DeleteEverythingInRedisIntegrationTest.java b/persistence-modules/redis/src/test/java/com/baeldung/redis/deleteeverything/DeleteEverythingInRedisIntegrationTest.java new file mode 100644 index 0000000000..e0376fc6a5 --- /dev/null +++ b/persistence-modules/redis/src/test/java/com/baeldung/redis/deleteeverything/DeleteEverythingInRedisIntegrationTest.java @@ -0,0 +1,91 @@ +package com.baeldung.redis.deleteeverything; + +import org.junit.*; +import redis.clients.jedis.Jedis; +import redis.embedded.RedisServer; + +import java.io.IOException; +import java.net.ServerSocket; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteEverythingInRedisIntegrationTest { + private Jedis jedis; + private RedisServer redisServer; + private int port; + + @Before + public void setUp() throws IOException { + + // Take an available port + ServerSocket s = new ServerSocket(0); + port = s.getLocalPort(); + s.close(); + + redisServer = new RedisServer(port); + redisServer.start(); + + // Configure JEDIS + jedis = new Jedis("localhost", port); + } + + @After + public void destroy() { + redisServer.stop(); + } + + @Test + public void whenPutDataIntoRedis_thenCanBeFound() { + String key = "key"; + String value = "value"; + + jedis.set(key, value); + String received = jedis.get(key); + + assertEquals(value, received); + } + + @Test + public void whenPutDataIntoRedisAndThenFlush_thenCannotBeFound() { + String key = "key"; + String value = "value"; + + jedis.set(key, value); + + jedis.flushDB(); + + String received = jedis.get(key); + + assertNull(received); + } + + @Test + public void whenPutDataIntoMultipleDatabases_thenFlushAllRemovesAll() { + // add keys in different databases + jedis.select(0); + jedis.set("key1", "value1"); + jedis.select(1); + jedis.set("key2", "value2"); + + // we'll find the correct keys in the correct dbs + jedis.select(0); + assertEquals("value1", jedis.get("key1")); + assertNull(jedis.get("key2")); + + jedis.select(1); + assertEquals("value2", jedis.get("key2")); + assertNull(jedis.get("key1")); + + // then, when we flush + jedis.flushAll(); + + // the keys will have gone + jedis.select(0); + assertNull(jedis.get("key1")); + assertNull(jedis.get("key2")); + jedis.select(1); + assertNull(jedis.get("key1")); + assertNull(jedis.get("key2")); + } +} diff --git a/persistence-modules/spring-boot-persistence-h2/pom.xml b/persistence-modules/spring-boot-persistence-h2/pom.xml index 7070b5e674..9e6c780931 100644 --- a/persistence-modules/spring-boot-persistence-h2/pom.xml +++ b/persistence-modules/spring-boot-persistence-h2/pom.xml @@ -40,11 +40,6 @@ db-util ${db-util.version} - - net.bytebuddy - byte-buddy - ${byte-buddy.version} - diff --git a/persistence-modules/spring-boot-persistence/pom.xml b/persistence-modules/spring-boot-persistence/pom.xml index c58e8dbf86..cc26ff58d5 100644 --- a/persistence-modules/spring-boot-persistence/pom.xml +++ b/persistence-modules/spring-boot-persistence/pom.xml @@ -77,7 +77,6 @@ UTF-8 2.23.0 2.0.1.Final - 2.1.7.RELEASE diff --git a/persistence-modules/spring-data-elasticsearch/pom.xml b/persistence-modules/spring-data-elasticsearch/pom.xml index 3446528323..6a983145ee 100644 --- a/persistence-modules/spring-data-elasticsearch/pom.xml +++ b/persistence-modules/spring-data-elasticsearch/pom.xml @@ -15,13 +15,7 @@ org.springframework - spring-core - ${spring.version} - - - - org.springframework - spring-context + spring-web ${spring.version} @@ -36,6 +30,7 @@ elasticsearch ${elasticsearch.version} + com.alibaba fastjson @@ -49,8 +44,8 @@ - com.vividsolutions - jts + org.locationtech.jts + jts-core ${jts.version} @@ -60,41 +55,19 @@ - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - - org.elasticsearch.client - transport - ${elasticsearch.version} - - org.springframework spring-test ${spring.version} test - - - net.java.dev.jna - jna - ${jna.version} - test - - 3.0.8.RELEASE - 4.5.2 - 5.6.0 + 4.0.0.RELEASE + 7.6.2 1.2.47 - 0.6 - 1.13 - 2.9.1 + 0.7 + 1.15.0 - \ No newline at end of file diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java index e6ce795b45..51bbe73e9e 100644 --- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java +++ b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java @@ -1,19 +1,13 @@ package com.baeldung.spring.data.es.config; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import org.elasticsearch.client.Client; -import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.transport.InetSocketTransportAddress; -import org.elasticsearch.transport.client.PreBuiltTransportClient; -import org.springframework.beans.factory.annotation.Value; +import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.RestClients; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; @Configuration @@ -21,30 +15,18 @@ import org.springframework.data.elasticsearch.repository.config.EnableElasticsea @ComponentScan(basePackages = { "com.baeldung.spring.data.es.service" }) public class Config { - @Value("${elasticsearch.home:/usr/local/Cellar/elasticsearch/5.6.0}") - private String elasticsearchHome; - - @Value("${elasticsearch.cluster.name:elasticsearch}") - private String clusterName; - @Bean - public Client client() { - TransportClient client = null; - try { - final Settings elasticsearchSettings = Settings.builder() - .put("client.transport.sniff", true) - .put("path.home", elasticsearchHome) - .put("cluster.name", clusterName).build(); - client = new PreBuiltTransportClient(elasticsearchSettings); - client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300)); - } catch (UnknownHostException e) { - e.printStackTrace(); - } - return client; + RestHighLevelClient client() { + ClientConfiguration clientConfiguration = ClientConfiguration.builder() + .connectedTo("localhost:9200") + .build(); + + return RestClients.create(clientConfiguration) + .rest(); } @Bean public ElasticsearchOperations elasticsearchTemplate() { - return new ElasticsearchTemplate(client()); + return new ElasticsearchRestTemplate(client()); } } diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java index 38f50e1614..1d596cd92b 100644 --- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java +++ b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java @@ -1,7 +1,12 @@ package com.baeldung.spring.data.es.model; +import static org.springframework.data.elasticsearch.annotations.FieldType.Text; + +import org.springframework.data.elasticsearch.annotations.Field; + public class Author { + @Field(type = Text) private String name; public Author() { diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java deleted file mode 100644 index a0f72aa5f7..0000000000 --- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.baeldung.spring.data.es.service; - -import java.util.Optional; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -import com.baeldung.spring.data.es.model.Article; - -public interface ArticleService { - Article save(Article article); - - Optional
findOne(String id); - - Iterable
findAll(); - - Page
findByAuthorName(String name, Pageable pageable); - - Page
findByAuthorNameUsingCustomQuery(String name, Pageable pageable); - - Page
findByFilteredTagQuery(String tag, Pageable pageable); - - Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable); - - long count(); - - void delete(Article article); -} diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java deleted file mode 100644 index 5064f16508..0000000000 --- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.baeldung.spring.data.es.service; - -import java.util.Optional; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; - -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.repository.ArticleRepository; - -@Service -public class ArticleServiceImpl implements ArticleService { - - private final ArticleRepository articleRepository; - - @Autowired - public ArticleServiceImpl(ArticleRepository articleRepository) { - this.articleRepository = articleRepository; - } - - @Override - public Article save(Article article) { - return articleRepository.save(article); - } - - @Override - public Optional
findOne(String id) { - return articleRepository.findById(id); - } - - @Override - public Iterable
findAll() { - return articleRepository.findAll(); - } - - @Override - public Page
findByAuthorName(String name, Pageable pageable) { - return articleRepository.findByAuthorsName(name, pageable); - } - - @Override - public Page
findByAuthorNameUsingCustomQuery(String name, Pageable pageable) { - return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable); - } - - @Override - public Page
findByFilteredTagQuery(String tag, Pageable pageable) { - return articleRepository.findByFilteredTagQuery(tag, pageable); - } - - @Override - public Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable) { - return articleRepository.findByAuthorsNameAndFilteredTagQuery(name, tag, pageable); - } - - @Override - public long count() { - return articleRepository.count(); - } - - @Override - public void delete(Article article) { - articleRepository.delete(article); - } -} diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java index c69deeb77c..6572896eca 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java @@ -8,10 +8,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.baeldung.spring.data.es.config.Config; /** + * This Manual test requires: Elasticsearch instance running on localhost:9200. * - * This Manual test requires: - * * Elasticsearch instance running on host - * + * The following docker command can be used: docker run -d --name es762 -p + * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java index e43dcdf43e..2ca5f28f13 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java @@ -3,43 +3,48 @@ package com.baeldung.elasticsearch; import static org.junit.Assert.assertEquals; import java.io.IOException; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.alibaba.fastjson.JSON; import org.elasticsearch.action.DocWriteResponse.Result; +import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.client.Client; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.transport.InetSocketTransportAddress; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; -import org.elasticsearch.transport.client.PreBuiltTransportClient; +import org.elasticsearch.search.builder.SearchSourceBuilder; import org.junit.Before; import org.junit.Test; - -import com.alibaba.fastjson.JSON; +import org.springframework.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.RestClients; /** + * This Manual test requires: Elasticsearch instance running on localhost:9200. * - * This Manual test requires: - * * Elasticsearch instance running on host - * * with cluster name = elasticsearch - * + * The following docker command can be used: docker run -d --name es762 -p + * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 */ public class ElasticSearchManualTest { private List listOfPersons = new ArrayList<>(); - private Client client = null; + private RestHighLevelClient client = null; @Before public void setUp() throws UnknownHostException { @@ -47,115 +52,122 @@ public class ElasticSearchManualTest { Person person2 = new Person(25, "Janette Doe", new Date()); listOfPersons.add(person1); listOfPersons.add(person2); - - client = new PreBuiltTransportClient(Settings.builder().put("client.transport.sniff", true) - .put("cluster.name","elasticsearch").build()) - .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300)); + + ClientConfiguration clientConfiguration = ClientConfiguration.builder() + .connectedTo("localhost:9200") + .build(); + client = RestClients.create(clientConfiguration) + .rest(); } @Test - public void givenJsonString_whenJavaObject_thenIndexDocument() { + public void givenJsonString_whenJavaObject_thenIndexDocument() throws Exception { String jsonObject = "{\"age\":20,\"dateOfBirth\":1471466076564,\"fullName\":\"John Doe\"}"; - IndexResponse response = client - .prepareIndex("people", "Doe") - .setSource(jsonObject, XContentType.JSON) - .get(); + IndexRequest request = new IndexRequest("people"); + request.source(jsonObject, XContentType.JSON); + + IndexResponse response = client.index(request, RequestOptions.DEFAULT); String index = response.getIndex(); - String type = response.getType(); + long version = response.getVersion(); assertEquals(Result.CREATED, response.getResult()); - assertEquals(index, "people"); - assertEquals(type, "Doe"); + assertEquals(1, version); + assertEquals("people", index); } @Test - public void givenDocumentId_whenJavaObject_thenDeleteDocument() { + public void givenDocumentId_whenJavaObject_thenDeleteDocument() throws Exception { String jsonObject = "{\"age\":10,\"dateOfBirth\":1471455886564,\"fullName\":\"Johan Doe\"}"; - IndexResponse response = client - .prepareIndex("people", "Doe") - .setSource(jsonObject, XContentType.JSON) - .get(); + IndexRequest indexRequest = new IndexRequest("people"); + indexRequest.source(jsonObject, XContentType.JSON); + + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); String id = response.getId(); - DeleteResponse deleteResponse = client - .prepareDelete("people", "Doe", id) - .get(); - assertEquals(Result.DELETED,deleteResponse.getResult()); + GetRequest getRequest = new GetRequest("people"); + getRequest.id(id); + + GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); + System.out.println(getResponse.getSourceAsString()); + + DeleteRequest deleteRequest = new DeleteRequest("people"); + deleteRequest.id(id); + + DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT); + + assertEquals(Result.DELETED, deleteResponse.getResult()); } @Test - public void givenSearchRequest_whenMatchAll_thenReturnAllResults() { - SearchResponse response = client - .prepareSearch() - .execute() - .actionGet(); - SearchHit[] searchHits = response - .getHits() - .getHits(); + public void givenSearchRequest_whenMatchAll_thenReturnAllResults() throws Exception { + SearchRequest searchRequest = new SearchRequest(); + SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); + SearchHit[] searchHits = response.getHits() + .getHits(); List results = Arrays.stream(searchHits) - .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) - .collect(Collectors.toList()); + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); + + results.forEach(System.out::println); } @Test - public void givenSearchParameters_thenReturnResults() { - SearchResponse response = client - .prepareSearch() - .setTypes() - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setPostFilter(QueryBuilders - .rangeQuery("age") + public void givenSearchParameters_thenReturnResults() throws Exception { + SearchSourceBuilder builder = new SearchSourceBuilder().postFilter(QueryBuilders.rangeQuery("age") .from(5) - .to(15)) - .setFrom(0) - .setSize(60) - .setExplain(true) - .execute() - .actionGet(); + .to(15)); - SearchResponse response2 = client - .prepareSearch() - .setTypes() - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setPostFilter(QueryBuilders.simpleQueryStringQuery("+John -Doe OR Janette")) - .setFrom(0) - .setSize(60) - .setExplain(true) - .execute() - .actionGet(); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH); + searchRequest.source(builder); + + SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); + + builder = new SearchSourceBuilder().postFilter(QueryBuilders.simpleQueryStringQuery("+John -Doe OR Janette")); + + searchRequest = new SearchRequest(); + searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH); + searchRequest.source(builder); + + SearchResponse response2 = client.search(searchRequest, RequestOptions.DEFAULT); + + builder = new SearchSourceBuilder().postFilter(QueryBuilders.matchQuery("John", "Name*")); + searchRequest = new SearchRequest(); + searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH); + searchRequest.source(builder); + + SearchResponse response3 = client.search(searchRequest, RequestOptions.DEFAULT); - SearchResponse response3 = client - .prepareSearch() - .setTypes() - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setPostFilter(QueryBuilders.matchQuery("John", "Name*")) - .setFrom(0) - .setSize(60) - .setExplain(true) - .execute() - .actionGet(); response2.getHits(); response3.getHits(); - final List results = Arrays.stream(response.getHits().getHits()) - .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) - .collect(Collectors.toList()); + final List results = Stream.of(response.getHits() + .getHits(), + response2.getHits() + .getHits(), + response3.getHits() + .getHits()) + .flatMap(Arrays::stream) + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); + + results.forEach(System.out::println); } @Test public void givenContentBuilder_whenHelpers_thanIndexJson() throws IOException { - XContentBuilder builder = XContentFactory - .jsonBuilder() - .startObject() - .field("fullName", "Test") - .field("salary", "11500") - .field("age", "10") - .endObject(); - IndexResponse response = client - .prepareIndex("people", "Doe") - .setSource(builder) - .get(); + XContentBuilder builder = XContentFactory.jsonBuilder() + .startObject() + .field("fullName", "Test") + .field("salary", "11500") + .field("age", "10") + .endObject(); - assertEquals(Result.CREATED, response.getResult()); + IndexRequest indexRequest = new IndexRequest("people"); + indexRequest.source(builder); + + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); + + assertEquals(Result.CREATED, response.getResult()); } } diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java index f9a42050b6..64b2ea2437 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java @@ -1,4 +1,5 @@ package com.baeldung.elasticsearch; + import static org.junit.Assert.assertTrue; import java.io.IOException; @@ -7,162 +8,169 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import com.baeldung.spring.data.es.config.Config; + +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.Client; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.ShapeRelation; -import org.elasticsearch.common.geo.builders.ShapeBuilders; +import org.elasticsearch.common.geo.builders.EnvelopeBuilder; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.builder.SearchSourceBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.locationtech.jts.geom.Coordinate; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.vividsolutions.jts.geom.Coordinate; - /** + * This Manual test requires: Elasticsearch instance running on localhost:9200. * - * This Manual test requires: - * * Elasticsearch instance running on host - * * with cluster name = elasticsearch - * * and further configurations - * + * The following docker command can be used: docker run -d --name es762 -p + * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class GeoQueriesManualTest { private static final String WONDERS_OF_WORLD = "wonders-of-world"; - private static final String WONDERS = "Wonders"; @Autowired - private ElasticsearchTemplate elasticsearchTemplate; - - @Autowired - private Client client; + private RestHighLevelClient client; @Before - public void setUp() { - String jsonObject = "{\"Wonders\":{\"properties\":{\"name\":{\"type\":\"string\",\"index\":\"not_analyzed\"},\"region\":{\"type\":\"geo_shape\",\"tree\":\"quadtree\",\"precision\":\"1m\"},\"location\":{\"type\":\"geo_point\"}}}}"; + public void setUp() throws Exception { + String jsonObject = "{\"properties\":{\"name\":{\"type\":\"text\",\"index\":false},\"region\":{\"type\":\"geo_shape\"},\"location\":{\"type\":\"geo_point\"}}}"; + CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); - req.mapping(WONDERS, jsonObject, XContentType.JSON); - client.admin() - .indices() - .create(req) - .actionGet(); + req.mapping(jsonObject, XContentType.JSON); + + client.indices() + .create(req, RequestOptions.DEFAULT); } @Test - public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException{ + public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException { String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,30.2],[80.1, 25]]}}"; - IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject, XContentType.JSON) - .get(); - + IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); + indexRequest.source(jsonObject, XContentType.JSON); + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); + String tajMahalId = response.getId(); - client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); - Coordinate topLeft =new Coordinate(74, 31.2); - Coordinate bottomRight =new Coordinate(81.1, 24); - QueryBuilder qb = QueryBuilders - .geoShapeQuery("region", ShapeBuilders.newEnvelope(topLeft, bottomRight)) - .relation(ShapeRelation.WITHIN); + RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD); + client.indices() + .refresh(refreshRequest, RequestOptions.DEFAULT); + Coordinate topLeft = new Coordinate(74, 31.2); + Coordinate bottomRight = new Coordinate(81.1, 24); - SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region", new EnvelopeBuilder(topLeft, bottomRight).buildGeometry()); + qb.relation(ShapeRelation.INTERSECTS); + + SearchSourceBuilder source = new SearchSourceBuilder().query(qb); + SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); + searchRequest.source(source); + + SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(tajMahalId)); } @Test - public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() { + public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() throws Exception { String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}"; - IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject, XContentType.JSON) - .get(); + + IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); + indexRequest.source(jsonObject, XContentType.JSON); + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); + String pyramidsOfGizaId = response.getId(); - client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + + RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD); + client.indices() + .refresh(refreshRequest, RequestOptions.DEFAULT); QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location") - .setCorners(31,30,28,32); - - SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setCorners(31, 30, 28, 32); + + SearchSourceBuilder source = new SearchSourceBuilder().query(qb); + SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); + searchRequest.source(source); + + SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); + List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(pyramidsOfGizaId)); } @Test - public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() { + public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() throws Exception { String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}"; - IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject, XContentType.JSON) - .get(); + + IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); + indexRequest.source(jsonObject, XContentType.JSON); + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); + String lighthouseOfAlexandriaId = response.getId(); - client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + + RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD); + client.indices() + .refresh(refreshRequest, RequestOptions.DEFAULT); QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") - .point(29.976, 31.131) - .distance(10, DistanceUnit.MILES); + .point(29.976, 31.131) + .distance(10, DistanceUnit.MILES); + + SearchSourceBuilder source = new SearchSourceBuilder().query(qb); + SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); + searchRequest.source(source); + + SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); - SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(lighthouseOfAlexandriaId)); } @Test - public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() { + public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() throws Exception { String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}"; - IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject, XContentType.JSON) - .get(); + + IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); + indexRequest.source(jsonObject, XContentType.JSON); + IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT); + String greatRannOfKutchid = response.getId(); - client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + + RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD); + client.indices() + .refresh(refreshRequest, RequestOptions.DEFAULT); List allPoints = new ArrayList(); allPoints.add(new GeoPoint(22.733, 68.859)); @@ -170,20 +178,23 @@ public class GeoQueriesManualTest { allPoints.add(new GeoPoint(23, 70.859)); QueryBuilder qb = QueryBuilders.geoPolygonQuery("location", allPoints); - SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + SearchSourceBuilder source = new SearchSourceBuilder().query(qb); + SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); + searchRequest.source(source); + + SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); + List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(greatRannOfKutchid)); } @After - public void destroy() { - elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD); + public void destroy() throws Exception { + DeleteIndexRequest deleteIndex = new DeleteIndexRequest(WONDERS_OF_WORLD); + client.indices() + .delete(deleteIndex, RequestOptions.DEFAULT); } } diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java index bed2e2ff25..412cd04e09 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java @@ -10,68 +10,71 @@ import static org.junit.Assert.assertNotNull; import java.util.List; +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.repository.ArticleRepository; + +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; -import org.springframework.data.elasticsearch.core.query.SearchQuery; +import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; - /** + * This Manual test requires: Elasticsearch instance running on localhost:9200. * - * This Manual test requires: - * * Elasticsearch instance running on host - * * with cluster name = elasticsearch - * + * The following docker command can be used: docker run -d --name es762 -p + * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class ElasticSearchManualTest { @Autowired - private ElasticsearchTemplate elasticsearchTemplate; + private ElasticsearchRestTemplate elasticsearchTemplate; @Autowired - private ArticleService articleService; + private ArticleRepository articleRepository; private final Author johnSmith = new Author("John Smith"); private final Author johnDoe = new Author("John Doe"); @Before public void before() { - elasticsearchTemplate.deleteIndex(Article.class); - elasticsearchTemplate.createIndex(Article.class); - // don't call putMapping() to test the default mappings - Article article = new Article("Spring Data Elasticsearch"); article.setAuthors(asList(johnSmith, johnDoe)); article.setTags("elasticsearch", "spring data"); - articleService.save(article); + articleRepository.save(article); article = new Article("Search engines"); article.setAuthors(asList(johnDoe)); article.setTags("search engines", "tutorial"); - articleService.save(article); + articleRepository.save(article); article = new Article("Second Article About Elasticsearch"); article.setAuthors(asList(johnSmith)); article.setTags("elasticsearch", "spring data"); - articleService.save(article); + articleRepository.save(article); article = new Article("Elasticsearch Tutorial"); article.setAuthors(asList(johnDoe)); article.setTags("elasticsearch"); - articleService.save(article); + articleRepository.save(article); + } + + @After + public void after() { + articleRepository.deleteAll(); } @Test @@ -81,82 +84,85 @@ public class ElasticSearchManualTest { Article article = new Article("Making Search Elastic"); article.setAuthors(authors); - article = articleService.save(article); + article = articleRepository.save(article); assertNotNull(article.getId()); } @Test public void givenPersistedArticles_whenSearchByAuthorsName_thenRightFound() { - - final Page
articleByAuthorName = articleService - .findByAuthorName(johnSmith.getName(), PageRequest.of(0, 10)); + final Page
articleByAuthorName = articleRepository.findByAuthorsName(johnSmith.getName(), PageRequest.of(0, 10)); assertEquals(2L, articleByAuthorName.getTotalElements()); } @Test public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() { - final Page
articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("Smith", PageRequest.of(0, 10)); + final Page
articleByAuthorName = articleRepository.findByAuthorsNameUsingCustomQuery("Smith", PageRequest.of(0, 10)); assertEquals(2L, articleByAuthorName.getTotalElements()); } @Test public void givenTagFilterQuery_whenSearchByTag_thenArticleIsFound() { - final Page
articleByAuthorName = articleService.findByFilteredTagQuery("elasticsearch", PageRequest.of(0, 10)); + final Page
articleByAuthorName = articleRepository.findByFilteredTagQuery("elasticsearch", PageRequest.of(0, 10)); assertEquals(3L, articleByAuthorName.getTotalElements()); } @Test public void givenTagFilterQuery_whenSearchByAuthorsName_thenArticleIsFound() { - final Page
articleByAuthorName = articleService.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", PageRequest.of(0, 10)); + final Page
articleByAuthorName = articleRepository.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", PageRequest.of(0, 10)); assertEquals(2L, articleByAuthorName.getTotalElements()); } @Test public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() { + final Query searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")) + .build(); - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")) - .build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); - assertEquals(1, articles.size()); + assertEquals(1, articles.getTotalHits()); } @Test public void givenSavedDoc_whenTitleUpdated_thenCouldFindByUpdatedTitle() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(fuzzyQuery("title", "serch")).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); + final Query searchQuery = new NativeSearchQueryBuilder().withQuery(fuzzyQuery("title", "serch")) + .build(); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); - assertEquals(1, articles.size()); + assertEquals(1, articles.getTotalHits()); - final Article article = articles.get(0); + final Article article = articles.getSearchHit(0) + .getContent(); final String newTitle = "Getting started with Search Engines"; article.setTitle(newTitle); - articleService.save(article); + articleRepository.save(article); - assertEquals(newTitle, articleService.findOne(article.getId()).get().getTitle()); + assertEquals(newTitle, articleRepository.findById(article.getId()) + .get() + .getTitle()); } @Test public void givenSavedDoc_whenDelete_thenRemovedFromIndex() { - final String articleTitle = "Spring Data Elasticsearch"; - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); - final long count = articleService.count(); + final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")) + .build(); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); - articleService.delete(articles.get(0)); + assertEquals(1, articles.getTotalHits()); + final long count = articleRepository.count(); - assertEquals(count - 1, articleService.count()); + articleRepository.delete(articles.getSearchHit(0) + .getContent()); + + assertEquals(count - 1, articleRepository.count()); } @Test public void givenSavedDoc_whenOneTermMatches_thenFindByTitle() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); + final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)) + .build(); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + assertEquals(1, articles.getTotalHits()); } } diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java index 5e24d8398c..aaf0c80097 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java @@ -2,7 +2,6 @@ package com.baeldung.spring.data.es; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; -import static org.elasticsearch.index.query.Operator.AND; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; @@ -14,190 +13,225 @@ import static org.junit.Assert.assertEquals; import java.util.List; import java.util.Map; +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.repository.ArticleRepository; + import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.Client; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.index.query.MultiMatchQueryBuilder; +import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; -import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; -import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; - /** + * This Manual test requires: Elasticsearch instance running on localhost:9200. * - * This Manual test requires: - * * Elasticsearch instance running on host - * * with cluster name = elasticsearch - * + * The following docker command can be used: docker run -d --name es762 -p + * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class ElasticSearchQueryManualTest { @Autowired - private ElasticsearchTemplate elasticsearchTemplate; + private ElasticsearchRestTemplate elasticsearchTemplate; @Autowired - private ArticleService articleService; + private ArticleRepository articleRepository; @Autowired - private Client client; + private RestHighLevelClient client; private final Author johnSmith = new Author("John Smith"); private final Author johnDoe = new Author("John Doe"); @Before public void before() { - elasticsearchTemplate.deleteIndex(Article.class); - elasticsearchTemplate.createIndex(Article.class); - elasticsearchTemplate.putMapping(Article.class); - elasticsearchTemplate.refresh(Article.class); - Article article = new Article("Spring Data Elasticsearch"); article.setAuthors(asList(johnSmith, johnDoe)); article.setTags("elasticsearch", "spring data"); - articleService.save(article); + articleRepository.save(article); article = new Article("Search engines"); article.setAuthors(asList(johnDoe)); article.setTags("search engines", "tutorial"); - articleService.save(article); + articleRepository.save(article); article = new Article("Second Article About Elasticsearch"); article.setAuthors(asList(johnSmith)); article.setTags("elasticsearch", "spring data"); - articleService.save(article); + articleRepository.save(article); article = new Article("Elasticsearch Tutorial"); article.setAuthors(asList(johnDoe)); article.setTags("elasticsearch"); - articleService.save(article); + articleRepository.save(article); + } + + @After + public void after() { + articleRepository.deleteAll(); } @Test public void givenFullTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(Operator.AND)) + .build(); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + assertEquals(1, articles.getTotalHits()); } @Test public void givenOneTermFromTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", "Engines Solutions")).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); - assertEquals("Search engines", articles.get(0).getTitle()); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Engines Solutions")) + .build(); + + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(1, articles.getTotalHits()); + assertEquals("Search engines", articles.getSearchHit(0) + .getContent() + .getTitle()); } @Test public void givenPartTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", "elasticsearch data")).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(3, articles.size()); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "elasticsearch data")) + .build(); + + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(3, articles.getTotalHits()); } @Test public void givenFullTitle_whenRunMatchQueryOnVerbatimField_thenDocIsFound() { - SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build(); - List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); + NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")) + .build(); + + SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(1, articles.getTotalHits()); searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")) - .build(); - articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(0, articles.size()); + .build(); + + articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + assertEquals(0, articles.getTotalHits()); } @Test public void givenNestedObject_whenQueryByAuthorsName_thenFoundArticlesByThatAuthor() { final QueryBuilder builder = nestedQuery("authors", boolQuery().must(termQuery("authors.name", "smith")), ScoreMode.None); - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) + .build(); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); - assertEquals(2, articles.size()); + assertEquals(2, articles.getTotalHits()); } @Test - public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() { - final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags").field("title"); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) - .execute().actionGet(); + public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() throws Exception { + final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags") + .field("title"); - final Map results = response.getAggregations().asMap(); - final StringTerms topTags = (StringTerms) results.get("top_tags"); + final SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation); + final SearchRequest searchRequest = new SearchRequest("blog").source(builder); - final List keys = topTags.getBuckets().stream() - .map(MultiBucketsAggregation.Bucket::getKeyAsString) - .sorted() - .collect(toList()); + final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); + + final Map results = response.getAggregations() + .asMap(); + final ParsedStringTerms topTags = (ParsedStringTerms) results.get("top_tags"); + + final List keys = topTags.getBuckets() + .stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .sorted() + .collect(toList()); assertEquals(asList("about", "article", "data", "elasticsearch", "engines", "search", "second", "spring", "tutorial"), keys); } @Test - public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() { - final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags") - .order(Terms.Order.count(false)); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) - .execute().actionGet(); + public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() throws Exception { + final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags") + .field("tags") + .order(BucketOrder.count(false)); - final Map results = response.getAggregations().asMap(); - final StringTerms topTags = (StringTerms) results.get("top_tags"); + final SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation); + final SearchRequest searchRequest = new SearchRequest().indices("blog") + .source(builder); - final List keys = topTags.getBuckets().stream() - .map(MultiBucketsAggregation.Bucket::getKeyAsString) - .collect(toList()); + final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); + + final Map results = response.getAggregations() + .asMap(); + final ParsedStringTerms topTags = (ParsedStringTerms) results.get("top_tags"); + + final List keys = topTags.getBuckets() + .stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .collect(toList()); assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys); } @Test public void givenNotExactPhrase_whenUseSlop_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)) + .build(); + + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(1, articles.getTotalHits()); } @Test public void givenPhraseWithType_whenUseFuzziness_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE) - .prefixLength(3)).build(); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "spring date elasticserch").operator(Operator.AND) + .fuzziness(Fuzziness.ONE) + .prefixLength(3)) + .build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(1, articles.size()); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(1, articles.getTotalHits()); } @Test public void givenMultimatchQuery_whenDoSearch_thenAllProvidedFieldsMatch() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder() - .withQuery(multiMatchQuery("tutorial").field("title").field("tags") - .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build(); + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery("tutorial").field("title") + .field("tags") + .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)) + .build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); - assertEquals(2, articles.size()); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + + assertEquals(2, articles.getTotalHits()); } @Test @@ -205,10 +239,10 @@ public class ElasticSearchQueryManualTest { final QueryBuilder builder = boolQuery().must(nestedQuery("authors", boolQuery().must(termQuery("authors.name", "doe")), ScoreMode.None)) .filter(termQuery("tags", "elasticsearch")); - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) + final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) .build(); - final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); + final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); - assertEquals(2, articles.size()); + assertEquals(2, articles.getTotalHits()); } } diff --git a/persistence-modules/spring-data-jpa-5/pom.xml b/persistence-modules/spring-data-jpa-5/pom.xml index 3053384559..6a5cdc86c2 100644 --- a/persistence-modules/spring-data-jpa-5/pom.xml +++ b/persistence-modules/spring-data-jpa-5/pom.xml @@ -1,6 +1,7 @@ + 4.0.0 spring-data-jpa-5 spring-data-jpa-5 @@ -11,7 +12,7 @@ 0.0.1-SNAPSHOT ../../parent-boot-2 - + org.springframework.boot @@ -28,10 +29,43 @@ spring-boot-starter-data-jdbc + + org.springframework.boot + spring-boot-starter-cache + + com.h2database h2 + + + org.mapstruct + mapstruct-jdk8 + 1.3.1.Final + provided + + - + + src/main/java + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + 1.3.1.Final + + + + + + + diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java new file mode 100644 index 0000000000..a750fcadf7 --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.partialupdate; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PartialUpdateApplication { + + public static void main(String[] args) { + SpringApplication.run(PartialUpdateApplication.class, args); + } +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java new file mode 100644 index 0000000000..352e361bd9 --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java @@ -0,0 +1,22 @@ +package com.baeldung.partialupdate.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class ContactPhone { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public long id; + @Column(nullable=false) + public long customerId; + public String phone; + + @Override + public String toString() { + return phone; + } +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java new file mode 100644 index 0000000000..b19d0b7952 --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java @@ -0,0 +1,23 @@ +package com.baeldung.partialupdate.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public long id; + public String name; + public String phone; + //... + public String phone99; + + @Override public String toString() { + return String.format("Customer %s, Phone: %s", + this.name, this.phone); + } +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java new file mode 100644 index 0000000000..0ecf206d9a --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java @@ -0,0 +1,31 @@ +package com.baeldung.partialupdate.model; + +public class CustomerDto { + private long id; + public String name; + public String phone; + //... + private String phone99; + + public CustomerDto(long id) { + this.id = id; + } + + public CustomerDto(Customer c) { + this.id = c.id; + this.name = c.name; + this.phone = c.phone; + } + + public long getId() { + return this.id; + } + + public Customer convertToEntity() { + Customer c = new Customer(); + c.id = id; + c.name = name; + c.phone = phone; + return c; + } +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java new file mode 100644 index 0000000000..dd053a963d --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java @@ -0,0 +1,27 @@ +package com.baeldung.partialupdate.model; + +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity +public class CustomerStructured { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public long id; + public String name; + @OneToMany(fetch = FetchType.EAGER, targetEntity = ContactPhone.class, mappedBy = "customerId") + public List contactPhones; + + @Override public String toString() { + return String.format("Customer %s, Phone: %s", + this.name, this.contactPhones.stream() + .map(e -> e.toString()).reduce("", String::concat)); + } +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java new file mode 100644 index 0000000000..4668181e05 --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java @@ -0,0 +1,12 @@ +package com.baeldung.partialupdate.repository; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import com.baeldung.partialupdate.model.ContactPhone; + +@Repository +public interface ContactPhoneRepository extends CrudRepository { + ContactPhone findById(long id); + ContactPhone findByCustomerId(long id); +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java new file mode 100644 index 0000000000..43e61df8ab --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java @@ -0,0 +1,18 @@ +package com.baeldung.partialupdate.repository; + +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 com.baeldung.partialupdate.model.Customer; + +@Repository +public interface CustomerRepository extends CrudRepository { + Customer findById(long id); + + @Modifying + @Query("update Customer u set u.phone = :phone where u.id = :id") + void updatePhone(@Param(value = "id") long id, @Param(value = "phone") String phone); +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java new file mode 100644 index 0000000000..0f9fd1e92e --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.partialupdate.repository; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import com.baeldung.partialupdate.model.CustomerStructured; + +@Repository +public interface CustomerStructuredRepository extends CrudRepository { + CustomerStructured findById(long id); +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java new file mode 100644 index 0000000000..9da97a7775 --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java @@ -0,0 +1,87 @@ +package com.baeldung.partialupdate.service; + +import javax.transaction.Transactional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baeldung.partialupdate.model.ContactPhone; +import com.baeldung.partialupdate.model.Customer; +import com.baeldung.partialupdate.model.CustomerDto; +import com.baeldung.partialupdate.model.CustomerStructured; +import com.baeldung.partialupdate.repository.ContactPhoneRepository; +import com.baeldung.partialupdate.repository.CustomerRepository; +import com.baeldung.partialupdate.repository.CustomerStructuredRepository; +import com.baeldung.partialupdate.util.CustomerMapper; + +@Service +@Transactional +public class CustomerService { + + @Autowired + CustomerRepository repo; + @Autowired + CustomerStructuredRepository repo2; + @Autowired + ContactPhoneRepository repo3; + @Autowired + CustomerMapper mapper; + + public Customer getCustomer(long id) { + return repo.findById(id); + } + + public void updateCustomerWithCustomQuery(long id, String phone) { + repo.updatePhone(id, phone); + } + + public Customer addCustomer(String name) { + Customer myCustomer = new Customer(); + myCustomer.name = name; + repo.save(myCustomer); + return myCustomer; + } + + public Customer updateCustomer(long id, String phone) { + Customer myCustomer = repo.findById(id); + myCustomer.phone = phone; + repo.save(myCustomer); + return myCustomer; + } + + public Customer addCustomer(CustomerDto dto) { + Customer myCustomer = new Customer(); + mapper.updateCustomerFromDto(dto, myCustomer); + repo.save(myCustomer); + return myCustomer; + } + + public Customer updateCustomer(CustomerDto dto) { + Customer myCustomer = repo.findById(dto.getId()); + mapper.updateCustomerFromDto(dto, myCustomer); + repo.save(myCustomer); + return myCustomer; + } + + public CustomerStructured addCustomerStructured(String name) { + CustomerStructured myCustomer = new CustomerStructured(); + myCustomer.name = name; + repo2.save(myCustomer); + return myCustomer; + } + + public void addCustomerPhone(long customerId, String phone) { + ContactPhone myPhone = new ContactPhone(); + myPhone.phone = phone; + myPhone.customerId = customerId; + repo3.save(myPhone); + } + + public CustomerStructured updateCustomerStructured(long id, String name) { + CustomerStructured myCustomer = repo2.findById(id); + myCustomer.name = name; + repo2.save(myCustomer); + return myCustomer; + } + +} diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java new file mode 100644 index 0000000000..8a666e3e6c --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java @@ -0,0 +1,15 @@ +package com.baeldung.partialupdate.util; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; + +import com.baeldung.partialupdate.model.Customer; +import com.baeldung.partialupdate.model.CustomerDto; + +@Mapper(componentModel = "spring") +public interface CustomerMapper { + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void updateCustomerFromDto(CustomerDto dto, @MappingTarget Customer entity); +} diff --git a/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java b/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java new file mode 100644 index 0000000000..874e18c4ad --- /dev/null +++ b/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java @@ -0,0 +1,63 @@ +package com.baeldung.partialupdate; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +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.test.context.junit4.SpringRunner; + +import com.baeldung.partialupdate.model.Customer; +import com.baeldung.partialupdate.model.CustomerDto; +import com.baeldung.partialupdate.model.CustomerStructured; +import com.baeldung.partialupdate.service.CustomerService; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PartialUpdateApplication.class) +public class PartialUpdateUnitTest { + + @Autowired + CustomerService service; + + @Test + public void givenCustomer_whenUpdate_thenSuccess() { + Customer myCustomer = service.addCustomer("John"); + myCustomer = service.updateCustomer(myCustomer.id, "+00"); + assertEquals("+00", myCustomer.phone); + } + + @Test + public void givenCustomer_whenUpdateWithQuery_thenSuccess() { + Customer myCustomer = service.addCustomer("John"); + service.updateCustomerWithCustomQuery(myCustomer.id, "+88"); + myCustomer = service.getCustomer(myCustomer.id); + assertEquals("+88", myCustomer.phone); + } + + @Test + public void givenCustomerDto_whenUpdateWithMapper_thenSuccess() { + CustomerDto dto = new CustomerDto(new Customer()); + dto.name = "Johnny"; + Customer entity = service.addCustomer(dto); + + CustomerDto dto2 = new CustomerDto(entity.id); + dto2.phone = "+44"; + entity = service.updateCustomer(dto2); + + assertEquals("Johnny", entity.name); + } + + @Test + public void givenCustomerStructured_whenUpdateCustomerPhone_thenSuccess() { + CustomerStructured myCustomer = service.addCustomerStructured("John"); + assertEquals(null, myCustomer.contactPhones); + + service.addCustomerPhone(myCustomer.id, "+44"); + myCustomer = service.updateCustomerStructured(myCustomer.id, "Mr. John"); + + assertNotEquals(null, myCustomer.contactPhones); + assertEquals(1, myCustomer.contactPhones.size()); + } +} diff --git a/persistence-modules/spring-data-redis/README.md b/persistence-modules/spring-data-redis/README.md index 175634376b..95cba2c159 100644 --- a/persistence-modules/spring-data-redis/README.md +++ b/persistence-modules/spring-data-redis/README.md @@ -4,7 +4,6 @@ - [Introduction to Spring Data Redis](https://www.baeldung.com/spring-data-redis-tutorial) - [PubSub Messaging with Spring Data Redis](https://www.baeldung.com/spring-data-redis-pub-sub) - [An Introduction to Spring Data Redis Reactive](https://www.baeldung.com/spring-data-redis-reactive) -- [Delete Everything in Redis](https://www.baeldung.com/redis-delete-data) ### Build the Project with Tests Running ``` @@ -15,4 +14,3 @@ mvn clean install ``` mvn test ``` - diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java index fdc279be42..497e1506bd 100644 --- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java +++ b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java @@ -35,18 +35,6 @@ public class RedisConfig { template.setValueSerializer(new GenericToStringSerializer(Object.class)); return template; } - - @Bean - public LettuceConnectionFactory lettuceConnectionFactory() { - return new LettuceConnectionFactory(); - } - - @Bean(name = "flushRedisTemplate") - public RedisTemplate flushRedisTemplate() { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(lettuceConnectionFactory()); - return template; - } @Bean MessageListenerAdapter messageListener() { diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/delete/RedisFlushDatabaseIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/delete/RedisFlushDatabaseIntegrationTest.java deleted file mode 100644 index 1f56cbb25d..0000000000 --- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/delete/RedisFlushDatabaseIntegrationTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.baeldung.spring.data.redis.delete; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.dao.DataAccessException; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.baeldung.spring.data.redis.config.RedisConfig; - -import redis.embedded.RedisServer; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { RedisConfig.class }) -@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) -public class RedisFlushDatabaseIntegrationTest { - - private RedisServer redisServer; - - @Autowired - @Qualifier("flushRedisTemplate") - private RedisTemplate flushRedisTemplate; - - @Before - public void setup() throws IOException { - redisServer = new RedisServer(6390); - redisServer.start(); - } - - @After - public void tearDown() { - redisServer.stop(); - } - - @Test - public void whenFlushDB_thenAllKeysInDatabaseAreCleared() { - - ValueOperations simpleValues = flushRedisTemplate.opsForValue(); - String key = "key"; - String value = "value"; - simpleValues.set(key, value); - assertThat(simpleValues.get(key)).isEqualTo(value); - - flushRedisTemplate.execute(new RedisCallback() { - - @Override - public Void doInRedis(RedisConnection connection) throws DataAccessException { - connection.flushDb(); - return null; - } - }); - - assertThat(simpleValues.get(key)).isNull(); - - } - - @Test - public void whenFlushAll_thenAllKeysInDatabasesAreCleared() { - - ValueOperations simpleValues = flushRedisTemplate.opsForValue(); - String key = "key"; - String value = "value"; - simpleValues.set(key, value); - assertThat(simpleValues.get(key)).isEqualTo(value); - - flushRedisTemplate.execute(new RedisCallback() { - - @Override - public Void doInRedis(RedisConnection connection) throws DataAccessException { - connection.flushAll(); - return null; - } - }); - - assertThat(simpleValues.get(key)).isNull(); - - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2efea40c91..20adc4bfef 100644 --- a/pom.xml +++ b/pom.xml @@ -69,12 +69,6 @@ ${hamcrest-all.version} test - - net.bytebuddy - byte-buddy - ${byte-buddy.version} - test - org.mockito mockito-core @@ -513,6 +507,7 @@ libraries-rpc libraries-security libraries-server + libraries-server-2 libraries-testing linkrest logging-modules @@ -1032,6 +1027,7 @@ libraries-primitive libraries-security libraries-server + libraries-server-2 libraries-testing linkrest logging-modules diff --git a/quarkus-extension/quarkus-liquibase/deployment/pom.xml b/quarkus-extension/quarkus-liquibase/deployment/pom.xml index 5c6b56e152..d7f1f995ff 100644 --- a/quarkus-extension/quarkus-liquibase/deployment/pom.xml +++ b/quarkus-extension/quarkus-liquibase/deployment/pom.xml @@ -8,7 +8,7 @@ com.baeldung.quarkus.liquibase - quarkus-liquibase-parent + quarkus-liquibase 1.0-SNAPSHOT diff --git a/quarkus-extension/quarkus-liquibase/pom.xml b/quarkus-extension/quarkus-liquibase/pom.xml index 8ed6555ed7..fdede2000e 100644 --- a/quarkus-extension/quarkus-liquibase/pom.xml +++ b/quarkus-extension/quarkus-liquibase/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung.quarkus.liquibase - quarkus-liquibase-parent + quarkus-liquibase quarkus-liquibase pom diff --git a/quarkus-extension/quarkus-liquibase/runtime/pom.xml b/quarkus-extension/quarkus-liquibase/runtime/pom.xml index 760e6ab719..5d3b05ef92 100644 --- a/quarkus-extension/quarkus-liquibase/runtime/pom.xml +++ b/quarkus-extension/quarkus-liquibase/runtime/pom.xml @@ -7,7 +7,7 @@ com.baeldung.quarkus.liquibase - quarkus-liquibase-parent + quarkus-liquibase 1.0-SNAPSHOT diff --git a/spring-5-mvc/pom.xml b/spring-5-mvc/pom.xml index 4b42528d0f..945ddef5e1 100644 --- a/spring-5-mvc/pom.xml +++ b/spring-5-mvc/pom.xml @@ -42,11 +42,6 @@ org.slf4j jcl-over-slf4j - - net.bytebuddy - byte-buddy - ${byte-buddy.version} - org.jetbrains.kotlin diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java index 428c8bf532..effc750940 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java @@ -5,11 +5,18 @@ import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Repository; @Repository("userRepository") public class CustomUserRepository implements UserRepository { + private PasswordEncoder passwordEncoder; + + public CustomUserRepository(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + @Override public User findUser(String username, String domain) { if (StringUtils.isAnyBlank(username, domain)) { @@ -17,7 +24,7 @@ public class CustomUserRepository implements UserRepository { } else { Collection authorities = new ArrayList<>(); User user = new User(username, domain, - "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + passwordEncoder.encode("secret"), true, true, true, true, authorities); return user; } 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 index def85ab978..88380f1ed6 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java @@ -1,6 +1,7 @@ package com.baeldung.loginextrafieldscustom; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -56,6 +57,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); } + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java index d8c5ea8147..48ca53c0b2 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java @@ -1,6 +1,7 @@ package com.baeldung.loginextrafieldssimple; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; @@ -59,6 +60,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); } + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } 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 index e8aaa774a1..44929c6189 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java @@ -5,11 +5,18 @@ import java.util.Collection; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Repository; @Repository("userRepository") public class SimpleUserRepository implements UserRepository { + private PasswordEncoder passwordEncoder; + + public SimpleUserRepository(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + @Override public User findUser(String username, String domain) { if (StringUtils.isAnyBlank(username, domain)) { @@ -17,7 +24,7 @@ public class SimpleUserRepository implements UserRepository { } else { Collection authorities = new ArrayList<>(); User user = new User(username, domain, - "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + passwordEncoder.encode("secret"), true, true, true, true, authorities); return user; } diff --git a/spring-5-security/src/main/java/com/baeldung/securityprofile/Application.java b/spring-5-security/src/main/java/com/baeldung/securityprofile/Application.java new file mode 100644 index 0000000000..5f17227777 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityprofile/Application.java @@ -0,0 +1,14 @@ +package com.baeldung.securityprofile; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationNoSecurity.java b/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationNoSecurity.java new file mode 100644 index 0000000000..c899eb9268 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationNoSecurity.java @@ -0,0 +1,17 @@ +package com.baeldung.securityprofile; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@Profile("test") +public class ApplicationNoSecurity extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) { + web.ignoring().antMatchers("/**"); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationSecurity.java b/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationSecurity.java new file mode 100644 index 0000000000..51a8d6aa11 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityprofile/ApplicationSecurity.java @@ -0,0 +1,16 @@ +package com.baeldung.securityprofile; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@Profile("prod") +public class ApplicationSecurity extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityprofile/EmployeeController.java b/spring-5-security/src/main/java/com/baeldung/securityprofile/EmployeeController.java new file mode 100644 index 0000000000..a28a5129ca --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityprofile/EmployeeController.java @@ -0,0 +1,16 @@ +package com.baeldung.securityprofile; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; + +@RestController +public class EmployeeController { + + @GetMapping("/employees") + public List getEmployees() { + return Collections.singletonList("Adam Johnson"); + } +} diff --git a/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerNoSecurityUnitTest.java b/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerNoSecurityUnitTest.java new file mode 100644 index 0000000000..7112392412 --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerNoSecurityUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.securityprofile; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = EmployeeController.class) +@ActiveProfiles("test") +public class EmployeeControllerNoSecurityUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void whenSecurityDisabled_shouldBeOk() throws Exception { + this.mockMvc.perform(get("/employees")) + .andExpect(status().isOk()); + } + +} \ No newline at end of file diff --git a/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerUnitTest.java b/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerUnitTest.java new file mode 100644 index 0000000000..b8c8b79eb5 --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/securityprofile/EmployeeControllerUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.securityprofile; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = EmployeeController.class) +@ActiveProfiles("prod") +public class EmployeeControllerUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void whenSecurityEnabled_shouldBeForbidden() throws Exception { + this.mockMvc.perform(get("/employees")) + .andExpect(status().isForbidden()); + } + +} \ No newline at end of file diff --git a/spring-boot-groovy/pom.xml b/spring-boot-groovy/pom.xml index f61398c5d6..9ea8d7b2a9 100644 --- a/spring-boot-groovy/pom.xml +++ b/spring-boot-groovy/pom.xml @@ -41,11 +41,6 @@ h2 runtime - - net.bytebuddy - byte-buddy-dep - 1.10.9 - diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 6caa93158a..7992c0ce12 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -15,6 +15,7 @@ spring-boot + spring-boot-1 spring-boot-admin spring-boot-angular spring-boot-annotations diff --git a/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties b/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..9dda3b659b --- /dev/null +++ b/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip diff --git a/spring-boot-modules/spring-boot-1/README.md b/spring-boot-modules/spring-boot-1/README.md new file mode 100644 index 0000000000..a818f60fb5 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/README.md @@ -0,0 +1,6 @@ +## Spring Boot 1.x Actuator + +This module contains articles about Spring Boot Actuator in Spring Boot version 1.x. + +## Relevant articles: +- [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators) diff --git a/spring-boot-modules/spring-boot-1/mvnw b/spring-boot-modules/spring-boot-1/mvnw new file mode 100755 index 0000000000..b74391fdf4 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/mvnw @@ -0,0 +1,234 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="$(/usr/libexec/java_home)" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +if [ -z "$M2_HOME" ]; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG="$(dirname "$PRG")/$link" + fi + done + + saveddir=$(pwd) + + M2_HOME=$(dirname "$PRG")/.. + + # make it fully qualified + M2_HOME=$(cd "$M2_HOME" && pwd) + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --unix "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$M2_HOME" ] && + M2_HOME="$( ( + cd "$M2_HOME" + pwd + ))" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="$( ( + cd "$JAVA_HOME" + pwd + ))" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then + if $darwin; then + javaHome="$(dirname \"$javaExecutable\")" + javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac" + else + javaExecutable="$(readlink -f \"$javaExecutable\")" + fi + javaHome="$(dirname \"$javaExecutable\")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(which java)" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." + pwd + ) + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' <"$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(pwd)") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=$(cygpath --path --windows "$M2_HOME") + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-boot-modules/spring-boot-1/mvnw.cmd b/spring-boot-modules/spring-boot-1/mvnw.cmd new file mode 100644 index 0000000000..019bd74d76 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-boot-modules/spring-boot-1/pom.xml b/spring-boot-modules/spring-boot-1/pom.xml new file mode 100644 index 0000000000..145bb221e0 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + spring-boot-1 + jar + Module for Spring Boot version 1.x + + + com.baeldung + parent-boot-1 + 0.0.1-SNAPSHOT + ../../parent-boot-1 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java new file mode 100644 index 0000000000..f48fc87640 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java @@ -0,0 +1,35 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class CustomEndpoint implements Endpoint> { + + @Override + public String getId() { + return "customEndpoint"; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean isSensitive() { + return true; + } + + @Override + public List invoke() { + // Custom logic to build the output + List messages = new ArrayList<>(); + messages.add("This is message 1"); + messages.add("This is message 2"); + return messages; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java new file mode 100644 index 0000000000..45db408465 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java @@ -0,0 +1,23 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component("myHealthCheck") +public class HealthCheck implements HealthIndicator { + + @Override + public Health health() { + int errorCode = check(); // perform some specific health check + if (errorCode != 0) { + return Health.down().withDetail("Error Code", errorCode).build(); + } + return Health.up().build(); + } + + public int check() { + // Our logic to check health + return 0; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java new file mode 100644 index 0000000000..925ce69a39 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java @@ -0,0 +1,28 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; + +@Service +public class LoginServiceImpl { + + private final CounterService counterService; + + public LoginServiceImpl(CounterService counterService) { + this.counterService = counterService; + } + + public boolean login(String userName, char[] password) { + boolean success; + if (userName.equals("admin") && Arrays.equals("secret".toCharArray(), password)) { + counterService.increment("counter.login.success"); + success = true; + } else { + counterService.increment("counter.login.failure"); + success = false; + } + return success; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java new file mode 100644 index 0000000000..bdf28e49cb --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java @@ -0,0 +1,13 @@ +package com.baeldung.actuator; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBoot { + + public static void main(String[] args) { + SpringApplication.run(SpringBoot.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-1/src/main/resources/application.properties b/spring-boot-modules/spring-boot-1/src/main/resources/application.properties new file mode 100644 index 0000000000..ac095e1cab --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/main/resources/application.properties @@ -0,0 +1,19 @@ +### server port +server.port=8080 +#port used to expose actuator +management.port=8081 +#CIDR allowed to hit actuator +management.address=127.0.0.1 +# Actuator Configuration +# customize /beans endpoint +endpoints.beans.id=springbeans +endpoints.beans.sensitive=false +endpoints.beans.enabled=true +# for the Spring Boot version 1.5.0 and above, we have to disable security to expose the health endpoint fully for unauthorized access. +# see: https://docs.spring.io/spring-boot/docs/1.5.x/reference/html/production-ready-monitoring.html +management.security.enabled=false +endpoints.health.sensitive=false +# customize /info endpoint +info.app.name=Spring Sample Application +info.app.description=This is my first spring boot application +info.app.version=1.0.0 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java new file mode 100644 index 0000000000..663e6055c7 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java @@ -0,0 +1,46 @@ +package com.baeldung.actuator; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0") +public class CustomEndpointIntegrationTest { + + @LocalManagementPort + private int port; + + private RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void whenSpringContextIsBootstrapped_thenActuatorCustomEndpointWorks() throws IOException { + ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/customEndpoint", String.class); + + assertThat(entity.getStatusCode(), is(HttpStatus.OK)); + + List response = objectMapper.readValue(entity.getBody(), new TypeReference>() { + }); + + assertThat(response, hasItems("This is message 1", "This is message 2")); + } +} diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java new file mode 100644 index 0000000000..f80e2745a0 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java @@ -0,0 +1,49 @@ +package com.baeldung.actuator; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsMapContaining.hasEntry; +import static org.hamcrest.collection.IsMapContaining.hasKey; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0") +public class HealthCheckIntegrationTest { + + @LocalManagementPort + private int port; + + private RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void whenSpringContextIsBootstrapped_thenActuatorHealthEndpointWorks() throws IOException { + ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/health", String.class); + + assertThat(entity.getStatusCode(), is(HttpStatus.OK)); + + Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() { + }); + + assertThat(response, hasEntry("status", "UP")); + assertThat(response, hasKey("myHealthCheck")); + assertThat(response, hasKey("diskSpace")); + } +} diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java new file mode 100644 index 0000000000..a464e51b1f --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java @@ -0,0 +1,35 @@ +package com.baeldung.actuator; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class HealthCheckUnitTest { + + @Test + public void whenCheckMethodReturnsZero_thenHealthMethodReturnsStatusUP() { + HealthCheck healthCheck = Mockito.spy(new HealthCheck()); + when(healthCheck.check()).thenReturn(0); + Health health = healthCheck.health(); + + assertThat(health.getStatus(), is(Status.UP)); + } + + @Test + public void whenCheckMethodReturnsOtherThanZero_thenHealthMethodReturnsStatusDOWN() { + HealthCheck healthCheck = Mockito.spy(new HealthCheck()); + when(healthCheck.check()).thenReturn(-1); + Health health = healthCheck.health(); + + assertThat(health.getStatus(), is(Status.DOWN)); + } + +} diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java new file mode 100644 index 0000000000..851de81d7f --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java @@ -0,0 +1,61 @@ +package com.baeldung.actuator; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0") +public class LoginServiceIntegrationTest { + + @LocalManagementPort + private int port; + + @Autowired + private LoginServiceImpl loginService; + + private RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private ObjectMapper objectMapper; + + @Test + public void whenLoginIsAdmin_thenSuccessCounterIsIncremented() throws IOException { + boolean success = loginService.login("admin", "secret".toCharArray()); + ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class); + Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() { + }); + + assertThat(success, is(true)); + assertThat(entity.getStatusCode(), is(HttpStatus.OK)); + assertThat(response, hasEntry("counter.login.success", 1)); + } + + @Test + public void whenLoginIsNotAdmin_thenFailureCounterIsIncremented() throws IOException { + boolean success = loginService.login("user", "notsecret".toCharArray()); + ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class); + Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() { + }); + + assertThat(success, is(false)); + assertThat(entity.getStatusCode(), is(HttpStatus.OK)); + assertThat(response, hasEntry("counter.login.failure", 1)); + } +} diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java new file mode 100644 index 0000000000..489d005782 --- /dev/null +++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java @@ -0,0 +1,41 @@ +package com.baeldung.actuator; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = LoginServiceImpl.class) +public class LoginServiceUnitTest { + + @MockBean + CounterService counterService; + + @Autowired + LoginServiceImpl loginService; + + @Test + public void whenLoginUserIsAdmin_thenSuccessCounterIsIncremented() { + boolean loginResult = loginService.login("admin", "secret".toCharArray()); + assertThat(loginResult, is(true)); + verify(counterService, times(1)).increment("counter.login.success"); + } + + @Test + public void whenLoginUserIsNotAdmin_thenFailureCounterIsIncremented() { + boolean loginResult = loginService.login("user", "notsecret".toCharArray()); + assertThat(loginResult, is(false)); + verify(counterService, times(1)).increment("counter.login.failure"); + } + +} diff --git a/spring-boot-modules/spring-boot-crud/src/main/java/com/baeldung/crud/controllers/UserController.java b/spring-boot-modules/spring-boot-crud/src/main/java/com/baeldung/crud/controllers/UserController.java index 726be6a384..8a7ef96f1e 100644 --- a/spring-boot-modules/spring-boot-crud/src/main/java/com/baeldung/crud/controllers/UserController.java +++ b/spring-boot-modules/spring-boot-crud/src/main/java/com/baeldung/crud/controllers/UserController.java @@ -36,7 +36,7 @@ public class UserController { userRepository.save(user); model.addAttribute("users", userRepository.findAll()); - return "index"; + return "redirect:/index"; } @GetMapping("/edit/{id}") @@ -55,7 +55,7 @@ public class UserController { userRepository.save(user); model.addAttribute("users", userRepository.findAll()); - return "index"; + return "redirect:/index"; } @GetMapping("/delete/{id}") diff --git a/spring-boot-modules/spring-boot-crud/src/test/java/com/baeldung/crud/UserControllerUnitTest.java b/spring-boot-modules/spring-boot-crud/src/test/java/com/baeldung/crud/UserControllerUnitTest.java index 033108c195..f1455f7a73 100644 --- a/spring-boot-modules/spring-boot-crud/src/test/java/com/baeldung/crud/UserControllerUnitTest.java +++ b/spring-boot-modules/spring-boot-crud/src/test/java/com/baeldung/crud/UserControllerUnitTest.java @@ -41,7 +41,7 @@ public class UserControllerUnitTest { when(mockedBindingResult.hasErrors()).thenReturn(false); - assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("index"); + assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("redirect:/index"); } @Test @@ -64,7 +64,7 @@ public class UserControllerUnitTest { when(mockedBindingResult.hasErrors()).thenReturn(false); - assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("index"); + assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("redirect:/index"); } @Test diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml index 8e917df2b7..68d4ec4b8f 100644 --- a/spring-boot-modules/spring-boot-keycloak/pom.xml +++ b/spring-boot-modules/spring-boot-keycloak/pom.xml @@ -41,10 +41,6 @@ org.springframework.boot spring-boot-starter-data-jpa - - net.bytebuddy - byte-buddy - org.springframework.boot diff --git a/spring-boot-modules/spring-boot-kotlin/pom.xml b/spring-boot-modules/spring-boot-kotlin/pom.xml index 79d62645da..7ee048546a 100644 --- a/spring-boot-modules/spring-boot-kotlin/pom.xml +++ b/spring-boot-modules/spring-boot-kotlin/pom.xml @@ -14,38 +14,6 @@ ../../parent-kotlin - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - org.jetbrains.kotlin @@ -142,9 +110,9 @@ 1.3.31 - 1.0.0.M1 - 1.0.0.M7 - 1.0.0.BUILD-SNAPSHOT + 1.0.0.RELEASE + 0.8.2.RELEASE + 0.8.4.RELEASE 1.2.1 diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt index 363090abac..464ed2773a 100644 --- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt +++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt @@ -2,12 +2,11 @@ package com.baeldung.nonblockingcoroutines.controller import com.baeldung.nonblockingcoroutines.model.Product import com.baeldung.nonblockingcoroutines.repository.ProductRepositoryCoroutines +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType.APPLICATION_JSON @@ -15,7 +14,6 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.awaitBody -import org.springframework.web.reactive.function.client.awaitExchange class ProductControllerCoroutines { @Autowired @@ -38,7 +36,7 @@ class ProductControllerCoroutines { webClient.get() .uri("/stock-service/product/$id/quantity") .accept(APPLICATION_JSON) - .awaitExchange().awaitBody() + .retrieve().awaitBody() } ProductStockView(product.await()!!, quantity.await()) } diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt index 41c4510e0d..e05b718e64 100644 --- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt +++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt @@ -12,7 +12,6 @@ import org.springframework.http.MediaType import org.springframework.stereotype.Component import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.awaitBody -import org.springframework.web.reactive.function.client.awaitExchange import org.springframework.web.reactive.function.server.ServerRequest import org.springframework.web.reactive.function.server.ServerResponse import org.springframework.web.reactive.function.server.bodyAndAwait @@ -37,7 +36,7 @@ class ProductsHandler( webClient.get() .uri("/stock-service/product/$id/quantity") .accept(MediaType.APPLICATION_JSON) - .awaitExchange().awaitBody() + .retrieve().awaitBody() } return ServerResponse.ok().json().bodyAndAwait(ProductStockView(product.await()!!, quantity.await())) } diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt index 20c3827c26..64ffd014ad 100644 --- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt +++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt @@ -1,7 +1,7 @@ package com.baeldung.nonblockingcoroutines.repository import com.baeldung.nonblockingcoroutines.model.Product -import org.springframework.data.r2dbc.function.DatabaseClient +import org.springframework.data.r2dbc.core.DatabaseClient import org.springframework.stereotype.Repository import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -10,7 +10,7 @@ import reactor.core.publisher.Mono class ProductRepository(private val client: DatabaseClient) { fun getProductById(id: Int): Mono { - return client.execute().sql("SELECT * FROM products WHERE id = $1") + return client.execute("SELECT * FROM products WHERE id = $1") .bind(0, id) .`as`(Product::class.java) .fetch() @@ -18,8 +18,7 @@ class ProductRepository(private val client: DatabaseClient) { } fun addNewProduct(name: String, price: Float): Mono { - return client.execute() - .sql("INSERT INTO products (name, price) VALUES($1, $2)") + return client.execute("INSERT INTO products (name, price) VALUES($1, $2)") .bind(0, name) .bind(1, price) .then() diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt index 60a19d4d00..f2667ec033 100644 --- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt +++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt @@ -6,14 +6,14 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.reactive.awaitFirstOrNull import kotlinx.coroutines.reactive.flow.asFlow -import org.springframework.data.r2dbc.function.DatabaseClient +import org.springframework.data.r2dbc.core.DatabaseClient import org.springframework.stereotype.Repository @Repository class ProductRepositoryCoroutines(private val client: DatabaseClient) { suspend fun getProductById(id: Int): Product? = - client.execute().sql("SELECT * FROM products WHERE id = $1") + client.execute("SELECT * FROM products WHERE id = $1") .bind(0, id) .`as`(Product::class.java) .fetch() @@ -21,8 +21,7 @@ class ProductRepositoryCoroutines(private val client: DatabaseClient) { .awaitFirstOrNull() suspend fun addNewProduct(name: String, price: Float) = - client.execute() - .sql("INSERT INTO products (name, price) VALUES($1, $2)") + client.execute("INSERT INTO products (name, price) VALUES($1, $2)") .bind(0, name) .bind(1, price) .then() diff --git a/spring-boot-modules/spring-boot-mvc-3/README.md b/spring-boot-modules/spring-boot-mvc-3/README.md new file mode 100644 index 0000000000..c220c6c405 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/README.md @@ -0,0 +1,7 @@ +## Spring Boot MVC + +This module contains articles about Spring Web MVC in Spring Boot projects. + +### Relevant Articles: + +- More articles: [[prev -->]](/spring-boot-modules/spring-boot-mvc-2) \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc-3/pom.xml b/spring-boot-modules/spring-boot-mvc-3/pom.xml new file mode 100644 index 0000000000..64e8a99c6c --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + spring-boot-mvc-3 + spring-boot-mvc-3 + jar + Module For Spring Boot MVC Web + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/circularviewpath/CircularViewPathApplication.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/circularviewpath/CircularViewPathApplication.java new file mode 100644 index 0000000000..deb50e2ed7 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/circularviewpath/CircularViewPathApplication.java @@ -0,0 +1,21 @@ +package com.baeldung.circularviewpath; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Spring Boot launcher for an application + * + */ +@SpringBootApplication(scanBasePackages = "com.baeldung.controller.circularviewpath") +public class CircularViewPathApplication { + + /** + * Launches a Spring Boot application + * + * @param args null + */ + public static void main(String[] args) { + SpringApplication.run(CircularViewPathApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/controller/circularviewpath/CircularViewPathController.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/controller/circularviewpath/CircularViewPathController.java new file mode 100644 index 0000000000..a4d6a97f7d --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/controller/circularviewpath/CircularViewPathController.java @@ -0,0 +1,16 @@ +package com.baeldung.controller.circularviewpath; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class CircularViewPathController { + + /** + * A request mapping which may cause circular view path exception + */ + @GetMapping("/path") + public String path() { + return "path"; + } +} diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/resources/logback.xml b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/path.html b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/path.html new file mode 100644 index 0000000000..b329797275 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/path.html @@ -0,0 +1,13 @@ + + + + + + + path.html + + + +

path.html

+ + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java new file mode 100644 index 0000000000..60ba4cc108 --- /dev/null +++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java @@ -0,0 +1,88 @@ +package com.baeldung.properties.lists; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SpringListPropertiesApplication.class) +public class ListsPropertiesUnitTest { + + @Value("${arrayOfStrings}") + private String[] arrayOfStrings; + + @Value("${arrayOfStrings}") + private List unexpectedListOfStrings; + + @Value("#{'${arrayOfStrings}'.split(',')}") + private List listOfStrings; + + @Value("#{${listOfStrings}}") + private List listOfStringsV2; + + @Value("#{'${listOfStringsWithCustomDelimiter}'.split(';')}") + private List listOfStringsWithCustomDelimiter; + + @Value("#{'${listOfBooleans}'.split(',')}") + private List listOfBooleans; + + @Value("#{'${listOfIntegers}'.split(',')}") + private List listOfIntegers; + + @Value("#{'${listOfCharacters}'.split(',')}") + private List listOfCharacters; + + @Autowired + private Environment environment; + + @Test + public void whenContextIsInitialized_thenInjectedArrayContainsExpectedValues() { + assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings); + } + + @Test + public void whenContextIsInitialized_thenInjectedListContainsUnexpectedValues() { + assertEquals(Collections.singletonList("Baeldung,dot,com"), unexpectedListOfStrings); + } + + @Test + public void whenContextIsInitialized_thenInjectedListContainsExpectedValues() { + assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings); + } + + @Test + public void whenContextIsInitialized_thenInjectedListV2ContainsExpectedValues() { + assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsV2); + } + + @Test + public void whenContextIsInitialized_thenInjectedListWithCustomDelimiterContainsExpectedValues() { + assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsWithCustomDelimiter); + } + + @Test + public void whenContextIsInitialized_thenInjectedListOfBasicTypesContainsExpectedValues() { + assertEquals(Arrays.asList(false, false, true), listOfBooleans); + assertEquals(Arrays.asList(1, 2, 3, 4), listOfIntegers); + assertEquals(Arrays.asList('a', 'b', 'c'), listOfCharacters); + } + + @Test + public void whenReadingFromSpringEnvironment_thenPropertiesHaveExpectedValues() { + String[] arrayOfStrings = environment.getProperty("arrayOfStrings", String[].class); + List listOfStrings = (List)environment.getProperty("arrayOfStrings", List.class); + + assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings); + assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings); + } +} diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java new file mode 100644 index 0000000000..8a66079201 --- /dev/null +++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java @@ -0,0 +1,10 @@ +package com.baeldung.properties.lists; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource(value = "lists.properties") +public class SpringListPropertiesApplication { + +} diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties b/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties new file mode 100644 index 0000000000..cc54d699a7 --- /dev/null +++ b/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties @@ -0,0 +1,6 @@ +arrayOfStrings=Baeldung,dot,com +listOfStrings={'Baeldung','dot','com'} +listOfStringsWithCustomDelimiter=Baeldung;dot;com +listOfBooleans=false,false,true +listOfIntegers=1,2,3,4 +listOfCharacters=a,b,c \ No newline at end of file diff --git a/spring-boot-rest/pom.xml b/spring-boot-rest/pom.xml index a8500d50f2..10dacf99e8 100644 --- a/spring-boot-rest/pom.xml +++ b/spring-boot-rest/pom.xml @@ -79,11 +79,6 @@ modelmapper ${modelmapper.version}
- - net.bytebuddy - byte-buddy - ${byte-buddy.version} -
diff --git a/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java similarity index 97% rename from spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java rename to spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java index e02e5da246..c83d4f9e96 100644 --- a/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java +++ b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java @@ -17,7 +17,7 @@ import com.baeldung.multiplecachemanager.repository.OrderDetailRepository; @SpringBootApplication @SpringBootTest -public class MultipleCacheManagerIntegrationUnitTest { +public class MultipleCacheManagerIntegrationTest { @MockBean private OrderDetailRepository orderDetailRepository; diff --git a/spring-cloud-bus/pom.xml b/spring-cloud-bus/pom.xml index 513c8bade6..ec56e23ac7 100644 --- a/spring-cloud-bus/pom.xml +++ b/spring-cloud-bus/pom.xml @@ -11,9 +11,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../parent-boot-1 + ../parent-boot-2 @@ -34,7 +34,7 @@ - Brixton.SR7 + Hoxton.SR4 diff --git a/spring-cloud-bus/spring-cloud-config-client/pom.xml b/spring-cloud-bus/spring-cloud-config-client/pom.xml index 7e1185415b..cc1c237646 100644 --- a/spring-cloud-bus/spring-cloud-config-client/pom.xml +++ b/spring-cloud-bus/spring-cloud-config-client/pom.xml @@ -33,6 +33,11 @@ org.springframework.boot spring-boot-actuator
+ + + org.springframework.boot + spring-boot-actuator-autoconfigure + org.springframework.cloud diff --git a/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml b/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml index 547e0284f3..fbbc6d138f 100644 --- a/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml +++ b/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml @@ -4,4 +4,14 @@ spring: host: localhost port: 5672 username: guest - password: guest \ No newline at end of file + password: guest + cloud: + bus: + enabled: true + refresh: + enabled: true +management: + endpoints: + web: + exposure: + include: "*" \ No newline at end of file diff --git a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties index 4c18c192c0..6d7a945612 100644 --- a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties +++ b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties @@ -1,11 +1,8 @@ server.port=8888 spring.cloud.config.server.git.uri= -security.user.name=root -security.user.password=s3cr3t -encrypt.key-store.location=classpath:/config-server.jks -encrypt.key-store.password=my-s70r3-s3cr3t -encrypt.key-store.alias=config-server-key -encrypt.key-store.secret=my-k34-s3cr3t +spring.cloud.bus.enabled=true +spring.security.user.name=root +spring.security.user.password=s3cr3t spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest diff --git a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties new file mode 100644 index 0000000000..b0c35c72a6 --- /dev/null +++ b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties @@ -0,0 +1,4 @@ +encrypt.key-store.location=classpath:/config-server.jks +encrypt.key-store.password=my-s70r3-s3cr3t +encrypt.key-store.alias=config-server-key +encrypt.key-store.secret=my-k34-s3cr3t \ No newline at end of file diff --git a/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/data-flow-server/pom.xml b/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/data-flow-server/pom.xml index ba108dc5c7..34da489cd1 100644 --- a/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/data-flow-server/pom.xml +++ b/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/data-flow-server/pom.xml @@ -50,6 +50,12 @@ hibernate-entitymanager ${hibernate.compatible.version} + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml index c4e606e190..6fddb1693f 100644 --- a/spring-cloud/pom.xml +++ b/spring-cloud/pom.xml @@ -32,6 +32,7 @@ spring-cloud-zuul-eureka-integration spring-cloud-contract spring-cloud-kubernetes + spring-cloud-open-service-broker spring-cloud-archaius spring-cloud-functions spring-cloud-vault @@ -39,6 +40,7 @@ spring-cloud-task spring-cloud-zuul spring-cloud-zuul-fallback + spring-cloud-ribbon-retry
diff --git a/spring-cloud/spring-cloud-connectors-heroku/pom.xml b/spring-cloud/spring-cloud-connectors-heroku/pom.xml index 1dad3ddcb7..c09a282197 100644 --- a/spring-cloud/spring-cloud-connectors-heroku/pom.xml +++ b/spring-cloud/spring-cloud-connectors-heroku/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../parent-boot-1 + ../../parent-boot-2 @@ -35,6 +35,11 @@ org.postgresql postgresql + + net.bytebuddy + byte-buddy-dep + ${bytebuddy.version} + com.h2database h2 @@ -55,8 +60,9 @@ - Brixton.SR7 - 9.4-1201-jdbc4 + Hoxton.SR4 + 42.2.10 + 1.10.10 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java index eb2972f35a..f998059028 100644 --- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java +++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java @@ -1,5 +1,7 @@ package com.baeldung.spring.cloud.connectors.heroku.book; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -15,7 +17,7 @@ public class BookController { } @GetMapping("/{bookId}") - public Book findBook(@PathVariable Long bookId) { + public Optional findBook(@PathVariable Long bookId) { return bookService.findBookById(bookId); } diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java index 4978ded65f..a83dfe64b7 100644 --- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java +++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java @@ -1,5 +1,7 @@ package com.baeldung.spring.cloud.connectors.heroku.book; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -15,8 +17,8 @@ public class BookService { this.bookRepository = bookRepository; } - public Book findBookById(Long bookId) { - return bookRepository.findOne(bookId); + public Optional findBookById(Long bookId) { + return bookRepository.findById(bookId); } @Transactional(propagation = Propagation.REQUIRED) diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java index 51cf4412bf..7875c712f9 100644 --- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java +++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java @@ -1,5 +1,7 @@ package com.baeldung.spring.cloud.connectors.heroku.product; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -15,7 +17,7 @@ public class ProductController { } @GetMapping("/{productId}") - public Product findProduct(@PathVariable Long productId) { + public Optional findProduct(@PathVariable Long productId) { return productService.findProductById(productId); } diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java index f25b4ecf7b..bdd13e9863 100644 --- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java +++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java @@ -1,5 +1,7 @@ package com.baeldung.spring.cloud.connectors.heroku.product; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -15,8 +17,8 @@ public class ProductService { this.productRepository = productRepository; } - public Product findProductById(Long productId) { - return productRepository.findOne(productId); + public Optional findProductById(Long productId) { + return productRepository.findById(productId); } @Transactional(propagation = Propagation.REQUIRED) diff --git a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml index 66d8f096ce..f0d34d2231 100644 --- a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml +++ b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml @@ -8,10 +8,10 @@ 1.0-SNAPSHOT - com.baeldung - parent-boot-1 - 0.0.1-SNAPSHOT - ../../../../parent-boot-1 + com.baeldung.spring.cloud + spring-cloud-kubernetes + 1.0-SNAPSHOT + ../../../spring-cloud-kubernetes @@ -44,7 +44,6 @@ UTF-8 UTF-8 - 1.5.17.RELEASE \ No newline at end of file diff --git a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml index fbb9e09d07..8bfd4d305d 100644 --- a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml +++ b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml @@ -8,10 +8,10 @@ 1.0-SNAPSHOT - com.baeldung - parent-boot-1 - 0.0.1-SNAPSHOT - ../../../../parent-boot-1 + com.baeldung.spring.cloud + spring-cloud-kubernetes + 1.0-SNAPSHOT + ../../../spring-cloud-kubernetes @@ -44,7 +44,6 @@ UTF-8 UTF-8 - 1.5.17.RELEASE \ No newline at end of file diff --git a/spring-cloud/spring-cloud-open-service-broker/README.md b/spring-cloud/spring-cloud-open-service-broker/README.md new file mode 100644 index 0000000000..4084e8ebb2 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Quick Guide to Spring Cloud Open Service Broker](https://www.baeldung.com/spring-cloud-open-service-broker) diff --git a/spring-cloud/spring-cloud-open-service-broker/pom.xml b/spring-cloud/spring-cloud-open-service-broker/pom.xml new file mode 100644 index 0000000000..7acd302dc1 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + com.baeldung + spring-cloud-open-service-broker + jar + + + com.baeldung.spring.cloud + spring-cloud + 1.0.0-SNAPSHOT + + + + 3.1.1.RELEASE + 2.2.7.RELEASE + 3.3.5.RELEASE + + + + + org.springframework.cloud + spring-cloud-starter-open-service-broker + ${spring-cloud-starter-open-service-broker.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot-starter-web.version} + + + io.projectreactor + reactor-test + ${reactor-test.version} + test + + + + diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java new file mode 100644 index 0000000000..2dbb4bc546 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.spring.cloud.openservicebroker; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ServiceBrokerApplication { + + public static void main(String[] args) { + SpringApplication.run(ServiceBrokerApplication.class, args); + } + +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java new file mode 100644 index 0000000000..e9e9452785 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java @@ -0,0 +1,34 @@ +package com.baeldung.spring.cloud.openservicebroker.config; + +import org.springframework.cloud.servicebroker.model.catalog.Catalog; +import org.springframework.cloud.servicebroker.model.catalog.Plan; +import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CatalogConfiguration { + + @Bean + public Catalog catalog() { + Plan mailFreePlan = Plan.builder() + .id("fd81196c-a414-43e5-bd81-1dbb082a3c55") + .name("mail-free-plan") + .description("Mail Service Free Plan") + .free(true) + .build(); + + ServiceDefinition serviceDefinition = ServiceDefinition.builder() + .id("b92c0ca7-c162-4029-b567-0d92978c0a97") + .name("mail-service") + .description("Mail Service") + .bindable(true) + .tags("mail", "service") + .plans(mailFreePlan) + .build(); + + return Catalog.builder() + .serviceDefinitions(serviceDefinition) + .build(); + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java new file mode 100644 index 0000000000..e0c0b36428 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.cloud.openservicebroker.mail; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MailController { + + @GetMapping("/mail-dashboard/{mailSystemId}") + public String dashboard(@PathVariable("mailSystemId") String mailSystemId) { + return "Mail Dashboard - " + mailSystemId; + } + + @GetMapping("/mail-system/{mailSystemId}") + public String mailSystem(@PathVariable("mailSystemId") String mailSystemId) { + return "Mail System - " + mailSystemId; + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java new file mode 100644 index 0000000000..07b7ad9a38 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java @@ -0,0 +1,95 @@ +package com.baeldung.spring.cloud.openservicebroker.mail; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Service +public class MailService { + + public static final String URI_KEY = "uri"; + public static final String USERNAME_KEY = "username"; + public static final String PASSWORD_KEY = "password"; + + private final String mailDashboardBaseURL; + private final String mailSystemBaseURL; + + private Map mailServices = new HashMap<>(); + + private Map mailServiceBindings = new HashMap<>(); + + public MailService(@Value("${mail.system.dashboard.base-url}") String mailDashboardBaseURL, + @Value("${mail.system.base-url}") String mailSystemBaseURL) { + this.mailDashboardBaseURL = mailDashboardBaseURL; + this.mailSystemBaseURL = mailSystemBaseURL; + } + + public Mono createServiceInstance(String instanceId, String serviceDefinitionId, String planId) { + MailServiceInstance mailServiceInstance = new MailServiceInstance( + instanceId, serviceDefinitionId, planId, mailDashboardBaseURL + instanceId); + mailServices.put(instanceId, mailServiceInstance); + return Mono.just(mailServiceInstance); + } + + public Mono serviceInstanceExists(String instanceId) { + return Mono.just(mailServices.containsKey(instanceId)); + } + + public Mono getServiceInstance(String instanceId) { + if (mailServices.containsKey(instanceId)) { + return Mono.just(mailServices.get(instanceId)); + } + return Mono.empty(); + } + + public Mono deleteServiceInstance(String instanceId) { + mailServices.remove(instanceId); + mailServiceBindings.remove(instanceId); + return Mono.empty(); + } + + public Mono createServiceBinding(String instanceId, String bindingId) { + return this.serviceInstanceExists(instanceId) + .flatMap(exists -> { + if (exists) { + MailServiceBinding mailServiceBinding = + new MailServiceBinding(bindingId, buildCredentials(instanceId, bindingId)); + mailServiceBindings.put(instanceId, mailServiceBinding); + return Mono.just(mailServiceBinding); + } else { + return Mono.empty(); + } + }); + } + + public Mono serviceBindingExists(String instanceId, String bindingId) { + return Mono.just(mailServiceBindings.containsKey(instanceId) && + mailServiceBindings.get(instanceId).getBindingId().equalsIgnoreCase(bindingId)); + } + + public Mono getServiceBinding(String instanceId, String bindingId) { + if (mailServiceBindings.containsKey(instanceId) && + mailServiceBindings.get(instanceId).getBindingId().equalsIgnoreCase(bindingId)) { + return Mono.just(mailServiceBindings.get(instanceId)); + } + return Mono.empty(); + } + + public Mono deleteServiceBinding(String instanceId) { + mailServiceBindings.remove(instanceId); + return Mono.empty(); + } + + private Map buildCredentials(String instanceId, String bindingId) { + Map credentials = new HashMap<>(); + credentials.put(URI_KEY, mailSystemBaseURL + instanceId); + credentials.put(USERNAME_KEY, bindingId); + credentials.put(PASSWORD_KEY, UUID.randomUUID().toString()); + return credentials; + } + +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java new file mode 100644 index 0000000000..a72d4f7372 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java @@ -0,0 +1,22 @@ +package com.baeldung.spring.cloud.openservicebroker.mail; + +import java.util.Map; + +public class MailServiceBinding { + + private String bindingId; + private Map credentials; + + public MailServiceBinding(String bindingId, Map credentials) { + this.bindingId = bindingId; + this.credentials = credentials; + } + + public String getBindingId() { + return bindingId; + } + + public Map getCredentials() { + return credentials; + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java new file mode 100644 index 0000000000..d4dbbe5657 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java @@ -0,0 +1,32 @@ +package com.baeldung.spring.cloud.openservicebroker.mail; + +public class MailServiceInstance { + + private String instanceId; + private String serviceDefinitionId; + private String planId; + private String dashboardUrl; + + public MailServiceInstance(String instanceId, String serviceDefinitionId, String planId, String dashboardUrl) { + this.instanceId = instanceId; + this.serviceDefinitionId = serviceDefinitionId; + this.planId = planId; + this.dashboardUrl = dashboardUrl; + } + + public String getInstanceId() { + return instanceId; + } + + public String getServiceDefinitionId() { + return serviceDefinitionId; + } + + public String getPlanId() { + return planId; + } + + public String getDashboardUrl() { + return dashboardUrl; + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java new file mode 100644 index 0000000000..847b309f2c --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java @@ -0,0 +1,77 @@ +package com.baeldung.spring.cloud.openservicebroker.services; + +import com.baeldung.spring.cloud.openservicebroker.mail.MailService; +import org.springframework.cloud.servicebroker.exception.ServiceInstanceBindingDoesNotExistException; +import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException; +import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceAppBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingResponse; +import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class MailServiceInstanceBindingService implements ServiceInstanceBindingService { + + private final MailService mailService; + + public MailServiceInstanceBindingService(MailService mailService) { + this.mailService = mailService; + } + + @Override + public Mono createServiceInstanceBinding( + CreateServiceInstanceBindingRequest request) { + return Mono.just(CreateServiceInstanceAppBindingResponse.builder()) + .flatMap(responseBuilder -> mailService.serviceBindingExists( + request.getServiceInstanceId(), request.getBindingId()) + .flatMap(exists -> { + if (exists) { + return mailService.getServiceBinding( + request.getServiceInstanceId(), request.getBindingId()) + .flatMap(serviceBinding -> Mono.just(responseBuilder + .bindingExisted(true) + .credentials(serviceBinding.getCredentials()) + .build())); + } else { + return mailService.createServiceBinding( + request.getServiceInstanceId(), request.getBindingId()) + .switchIfEmpty(Mono.error( + new ServiceInstanceDoesNotExistException( + request.getServiceInstanceId()))) + .flatMap(mailServiceBinding -> Mono.just(responseBuilder + .bindingExisted(false) + .credentials(mailServiceBinding.getCredentials()) + .build())); + } + })); + } + + @Override + public Mono getServiceInstanceBinding(GetServiceInstanceBindingRequest request) { + return mailService.getServiceBinding(request.getServiceInstanceId(), request.getBindingId()) + .switchIfEmpty(Mono.error(new ServiceInstanceBindingDoesNotExistException(request.getBindingId()))) + .flatMap(mailServiceBinding -> Mono.just(GetServiceInstanceAppBindingResponse.builder() + .credentials(mailServiceBinding.getCredentials()) + .build())); + } + + @Override + public Mono deleteServiceInstanceBinding( + DeleteServiceInstanceBindingRequest request) { + return mailService.serviceBindingExists(request.getServiceInstanceId(), request.getBindingId()) + .flatMap(exists -> { + if (exists) { + return mailService.deleteServiceBinding(request.getServiceInstanceId()) + .thenReturn(DeleteServiceInstanceBindingResponse.builder().build()); + } else { + return Mono.error(new ServiceInstanceBindingDoesNotExistException(request.getBindingId())); + } + }); + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java new file mode 100644 index 0000000000..8c757c3f25 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java @@ -0,0 +1,72 @@ +package com.baeldung.spring.cloud.openservicebroker.services; + +import com.baeldung.spring.cloud.openservicebroker.mail.MailService; +import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException; +import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceRequest; +import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceResponse; +import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest; +import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceResponse; +import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceRequest; +import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceResponse; +import org.springframework.cloud.servicebroker.service.ServiceInstanceService; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class MailServiceInstanceService implements ServiceInstanceService { + + private final MailService mailService; + + public MailServiceInstanceService(MailService mailService) { + this.mailService = mailService; + } + + @Override + public Mono createServiceInstance(CreateServiceInstanceRequest request) { + return Mono.just(request.getServiceInstanceId()) + .flatMap(instanceId -> Mono.just(CreateServiceInstanceResponse.builder()) + .flatMap(responseBuilder -> mailService.serviceInstanceExists(instanceId) + .flatMap(exists -> { + if (exists) { + return mailService.getServiceInstance(instanceId) + .flatMap(mailServiceInstance -> Mono.just(responseBuilder + .instanceExisted(true) + .dashboardUrl(mailServiceInstance.getDashboardUrl()) + .build())); + } else { + return mailService.createServiceInstance( + instanceId, request.getServiceDefinitionId(), request.getPlanId()) + .flatMap(mailServiceInstance -> Mono.just(responseBuilder + .instanceExisted(false) + .dashboardUrl(mailServiceInstance.getDashboardUrl()) + .build())); + } + }))); + } + + @Override + public Mono deleteServiceInstance(DeleteServiceInstanceRequest request) { + return Mono.just(request.getServiceInstanceId()) + .flatMap(instanceId -> mailService.serviceInstanceExists(instanceId) + .flatMap(exists -> { + if (exists) { + return mailService.deleteServiceInstance(instanceId) + .thenReturn(DeleteServiceInstanceResponse.builder().build()); + } else { + return Mono.error(new ServiceInstanceDoesNotExistException(instanceId)); + } + })); + } + + @Override + public Mono getServiceInstance(GetServiceInstanceRequest request) { + return Mono.just(request.getServiceInstanceId()) + .flatMap(instanceId -> mailService.getServiceInstance(instanceId) + .switchIfEmpty(Mono.error(new ServiceInstanceDoesNotExistException(instanceId))) + .flatMap(serviceInstance -> Mono.just(GetServiceInstanceResponse.builder() + .serviceDefinitionId(serviceInstance.getServiceDefinitionId()) + .planId(serviceInstance.getPlanId()) + .dashboardUrl(serviceInstance.getDashboardUrl()) + .build()))); + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml b/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml new file mode 100644 index 0000000000..d863b513b0 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml @@ -0,0 +1,10 @@ +spring: + cloud: + openservicebroker: + base-path: /broker + +mail: + system: + base-url: http://localhost:8080/mail-system/ + dashboard: + base-url: http://localhost:8080/mail-dashboard/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java new file mode 100644 index 0000000000..5b50d44600 --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java @@ -0,0 +1,201 @@ +package com.baeldung.spring.cloud.openservicebroker.services; + +import com.baeldung.spring.cloud.openservicebroker.mail.MailService; +import com.baeldung.spring.cloud.openservicebroker.mail.MailServiceBinding; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.cloud.servicebroker.exception.ServiceInstanceBindingDoesNotExistException; +import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest; +import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceAppBindingResponse; +import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingRequest; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.HashMap; +import java.util.Map; + +import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.PASSWORD_KEY; +import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.URI_KEY; +import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.USERNAME_KEY; +import static java.util.UUID.randomUUID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class MailServiceInstanceBindingServiceUnitTest { + + private static final String MAIL_SERVICE_INSTANCE_ID = "test@baeldung.com"; + private static final String MAIL_SERVICE_BINDING_ID = "test"; + private static final String MAIL_SYSTEM_URL = "http://localhost:8080/mail-system/test@baeldung.com"; + + @Mock + private MailService mailService; + + private MailServiceInstanceBindingService mailServiceInstanceBindingService; + + @BeforeEach + public void setUp() { + initMocks(this); + + this.mailServiceInstanceBindingService = new MailServiceInstanceBindingService(mailService); + } + + @Test + public void givenServiceBindingDoesNotExist_whenCreateServiceBinding_thenNewBindingIsCreated() { + // given service binding does not exist + when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(false)); + + Map credentials = generateCredentials(); + MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials); + when(mailService.createServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)) + .thenReturn(Mono.just(serviceBinding)); + + // when create service binding + CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then a new service binding is provisioned + StepVerifier.create(mailServiceInstanceBindingService.createServiceInstanceBinding(request)) + .consumeNextWith(response -> { + assertTrue(response instanceof CreateServiceInstanceAppBindingResponse); + CreateServiceInstanceAppBindingResponse bindingResponse = (CreateServiceInstanceAppBindingResponse) response; + assertFalse(bindingResponse.isBindingExisted()); + validateBindingCredentials(bindingResponse.getCredentials()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceBindingExists_whenCreateServiceBinding_thenExistingBindingIsRetrieved() { + // given service binding exists + when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(true)); + + Map credentials = generateCredentials(); + MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials); + when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)) + .thenReturn(Mono.just(serviceBinding)); + + // when create service binding + CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then a new service binding is provisioned + StepVerifier.create(mailServiceInstanceBindingService.createServiceInstanceBinding(request)) + .consumeNextWith(response -> { + assertTrue(response instanceof CreateServiceInstanceAppBindingResponse); + CreateServiceInstanceAppBindingResponse bindingResponse = (CreateServiceInstanceAppBindingResponse) response; + assertTrue(bindingResponse.isBindingExisted()); + validateBindingCredentials(bindingResponse.getCredentials()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceBindingDoesNotExist_whenGetServiceBinding_thenException() { + // given service binding does not exist + when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.empty()); + + // when get service binding + GetServiceInstanceBindingRequest request = GetServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then ServiceInstanceBindingDoesNotExistException is thrown + StepVerifier.create(mailServiceInstanceBindingService.getServiceInstanceBinding(request)) + .expectErrorMatches(ex -> ex instanceof ServiceInstanceBindingDoesNotExistException) + .verify(); + } + + @Test + public void givenServiceBindingExists_whenGetServiceBinding_thenExistingBindingIsRetrieved() { + // given service binding exists + Map credentials = generateCredentials(); + MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials); + when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)) + .thenReturn(Mono.just(serviceBinding)); + + // when get service binding + GetServiceInstanceBindingRequest request = GetServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then the existing service binding is retrieved + StepVerifier.create(mailServiceInstanceBindingService.getServiceInstanceBinding(request)) + .consumeNextWith(response -> { + assertTrue(response instanceof GetServiceInstanceAppBindingResponse); + GetServiceInstanceAppBindingResponse bindingResponse = (GetServiceInstanceAppBindingResponse) response; + validateBindingCredentials(bindingResponse.getCredentials()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceBindingDoesNotExist_whenDeleteServiceBinding_thenException() { + // given service binding does not exist + when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(false)); + + // when delete service binding + DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then ServiceInstanceBindingDoesNotExistException is thrown + StepVerifier.create(mailServiceInstanceBindingService.deleteServiceInstanceBinding(request)) + .expectErrorMatches(ex -> ex instanceof ServiceInstanceBindingDoesNotExistException) + .verify(); + } + + @Test + public void givenServiceBindingExists_whenDeleteServiceBinding_thenExistingBindingIsDeleted() { + // given service binding exists + when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(true)); + when(mailService.deleteServiceBinding(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty()); + + // when delete service binding + DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .bindingId(MAIL_SERVICE_BINDING_ID) + .build(); + + // then the existing service binding is retrieved + StepVerifier.create(mailServiceInstanceBindingService.deleteServiceInstanceBinding(request)) + .consumeNextWith(response -> { + assertFalse(response.isAsync()); + assertNull(response.getOperation()); + }) + .verifyComplete(); + } + + private void validateBindingCredentials(Map bindingCredentials) { + assertNotNull(bindingCredentials); + assertEquals(3, bindingCredentials.size()); + assertTrue(bindingCredentials.containsKey(URI_KEY)); + assertTrue(bindingCredentials.containsKey(USERNAME_KEY)); + assertTrue(bindingCredentials.containsKey(PASSWORD_KEY)); + assertEquals(MAIL_SYSTEM_URL, bindingCredentials.get(URI_KEY)); + assertEquals(MAIL_SERVICE_BINDING_ID, bindingCredentials.get(USERNAME_KEY)); + assertNotNull(bindingCredentials.get(PASSWORD_KEY)); + } + + private Map generateCredentials() { + Map credentials = new HashMap<>(); + credentials.put(URI_KEY, MAIL_SYSTEM_URL); + credentials.put(USERNAME_KEY, MAIL_SERVICE_BINDING_ID); + credentials.put(PASSWORD_KEY, randomUUID().toString()); + return credentials; + } +} diff --git a/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java new file mode 100644 index 0000000000..1302cad42e --- /dev/null +++ b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java @@ -0,0 +1,166 @@ +package com.baeldung.spring.cloud.openservicebroker.services; + +import com.baeldung.spring.cloud.openservicebroker.mail.MailService; +import com.baeldung.spring.cloud.openservicebroker.mail.MailServiceInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException; +import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceRequest; +import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest; +import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceRequest; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class MailServiceInstanceServiceUnitTest { + + private static final String MAIL_SERVICE_INSTANCE_ID = "test@baeldung.com"; + private static final String MAIL_SERVICE_DEFINITION_ID = "mock-service-definition-id"; + private static final String MAIL_SERVICE_PLAN_ID = "mock-service-plan-id"; + private static final String MAIL_DASHBOARD_URL = "http://localhost:8080/mail-dashboard/test@baeldung.com"; + + @Mock + private MailService mailService; + + private MailServiceInstanceService mailServiceInstanceService; + + @BeforeEach + public void setUp() { + initMocks(this); + + this.mailServiceInstanceService = new MailServiceInstanceService(mailService); + } + + @Test + public void givenServiceInstanceDoesNotExist_whenCreateServiceInstance_thenProvisionNewService() { + // given service instance does not exist + when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(false)); + + MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID, + MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL); + when(mailService.createServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID, MAIL_SERVICE_PLAN_ID)) + .thenReturn(Mono.just(serviceInstance)); + + // when create service instance + CreateServiceInstanceRequest request = CreateServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .serviceDefinitionId(MAIL_SERVICE_DEFINITION_ID) + .planId(MAIL_SERVICE_PLAN_ID) + .build(); + + // then a new service instance is provisioned + StepVerifier.create(mailServiceInstanceService.createServiceInstance(request)) + .consumeNextWith(response -> { + assertFalse(response.isInstanceExisted()); + assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceInstanceExists_whenCreateServiceInstance_thenExistingServiceInstanceIsRetrieved() { + // given service instance exists + when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(true)); + + MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID, + MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL); + when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(serviceInstance)); + + // when create service instance + CreateServiceInstanceRequest request = CreateServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .serviceDefinitionId(MAIL_SERVICE_DEFINITION_ID) + .planId(MAIL_SERVICE_PLAN_ID) + .build(); + + // then the existing one is retrieved + StepVerifier.create(mailServiceInstanceService.createServiceInstance(request)) + .consumeNextWith(response -> { + assertTrue(response.isInstanceExisted()); + assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceInstanceDoesNotExist_whenDeleteServiceInstance_thenException() { + // given service instance does not exist + when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(false)); + + // when delete service instance + DeleteServiceInstanceRequest request = DeleteServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .build(); + + // then ServiceInstanceDoesNotExistException is thrown + StepVerifier.create(mailServiceInstanceService.deleteServiceInstance(request)) + .expectErrorMatches(ex -> ex instanceof ServiceInstanceDoesNotExistException) + .verify(); + } + + @Test + public void givenServiceInstanceExists_whenDeleteServiceInstance_thenSuccess() { + // given service instance exists + when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(true)); + + // when delete service instance + when(mailService.deleteServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty()); + + DeleteServiceInstanceRequest request = DeleteServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .build(); + + // then success + StepVerifier.create(mailServiceInstanceService.deleteServiceInstance(request)) + .consumeNextWith(response -> { + assertFalse(response.isAsync()); + assertNull(response.getOperation()); + }) + .verifyComplete(); + } + + @Test + public void givenServiceInstanceDoesNotExist_whenGetServiceInstance_thenException() { + // given service instance does not exist + when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty()); + + // when get service instance + GetServiceInstanceRequest request = GetServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .build(); + + // then ServiceInstanceDoesNotExistException is thrown + StepVerifier.create(mailServiceInstanceService.getServiceInstance(request)) + .expectErrorMatches(ex -> ex instanceof ServiceInstanceDoesNotExistException) + .verify(); + } + + @Test + public void givenServiceInstanceExists_whenGetServiceInstance_thenExistingServiceInstanceIsRetrieved() { + // given service instance exists + MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID, + MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL); + when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(serviceInstance)); + + // when get service instance + GetServiceInstanceRequest request = GetServiceInstanceRequest.builder() + .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID) + .build(); + + // then the existing service instance is retrieved + StepVerifier.create(mailServiceInstanceService.getServiceInstance(request)) + .consumeNextWith(response -> { + assertEquals(MAIL_SERVICE_DEFINITION_ID, response.getServiceDefinitionId()); + assertEquals(MAIL_SERVICE_PLAN_ID, response.getPlanId()); + assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl()); + }) + .verifyComplete(); + } +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/pom.xml b/spring-cloud/spring-cloud-ribbon-retry/pom.xml new file mode 100644 index 0000000000..5318ea6913 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + com.baeldung.spring.cloud + spring-cloud-ribbon-retry + 0.0.1-SNAPSHOT + spring-cloud-ribbon-retry + pom + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + ribbon-client-service + ribbon-weather-service + + + + + + org.springframework.cloud + spring-cloud-starter-parent + ${spring-cloud.version} + pom + import + + + + + + Hoxton.SR3 + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/pom.xml b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/pom.xml new file mode 100644 index 0000000000..ad47eb6c84 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + ribbon-client-service + + + com.baeldung.spring.cloud + spring-cloud-ribbon-retry + 0.0.1-SNAPSHOT + + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.retry + spring-retry + + + com.baeldung.spring.cloud + ribbon-weather-service + 0.0.1-SNAPSHOT + test + + + + + Hoxton.SR3 + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonClientApp.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonClientApp.java new file mode 100644 index 0000000000..e06d4a93a1 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonClientApp.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.cloud.ribbon.retry; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RibbonClientApp { + + public static void main(String[] args) { + SpringApplication.run(RibbonClientApp.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialBackoffRetryFactory.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialBackoffRetryFactory.java new file mode 100644 index 0000000000..c70ee71b7d --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialBackoffRetryFactory.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.cloud.ribbon.retry.backoff; + +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.retry.backoff.BackOffPolicy; +import org.springframework.retry.backoff.ExponentialBackOffPolicy; +import org.springframework.stereotype.Component; + +@Component +@Profile("exponential-backoff") +class ExponentialBackoffRetryFactory extends RibbonLoadBalancedRetryFactory { + + public ExponentialBackoffRetryFactory(SpringClientFactory clientFactory) { + super(clientFactory); + } + + @Override + public BackOffPolicy createBackOffPolicy(String service) { + ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy(); + exponentialBackOffPolicy.setInitialInterval(1000); + exponentialBackOffPolicy.setMultiplier(2); + exponentialBackOffPolicy.setMaxInterval(10000); + return exponentialBackOffPolicy; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialRandomBackoffRetryFactory.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialRandomBackoffRetryFactory.java new file mode 100644 index 0000000000..c1fad4d1a0 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/ExponentialRandomBackoffRetryFactory.java @@ -0,0 +1,26 @@ +package com.baeldung.spring.cloud.ribbon.retry.backoff; + +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.retry.backoff.BackOffPolicy; +import org.springframework.retry.backoff.ExponentialRandomBackOffPolicy; +import org.springframework.stereotype.Component; + +@Component +@Profile("exponential-random-backoff") +class ExponentialRandomBackoffRetryFactory extends RibbonLoadBalancedRetryFactory { + + public ExponentialRandomBackoffRetryFactory(SpringClientFactory clientFactory) { + super(clientFactory); + } + + @Override + public BackOffPolicy createBackOffPolicy(String service) { + ExponentialRandomBackOffPolicy exponentialRandomBackOffPolicy = new ExponentialRandomBackOffPolicy(); + exponentialRandomBackOffPolicy.setInitialInterval(1000); + exponentialRandomBackOffPolicy.setMultiplier(2); + exponentialRandomBackOffPolicy.setMaxInterval(10000); + return exponentialRandomBackOffPolicy; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/FixedBackoffRetryFactory.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/FixedBackoffRetryFactory.java new file mode 100644 index 0000000000..6dab5d15b4 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/backoff/FixedBackoffRetryFactory.java @@ -0,0 +1,24 @@ +package com.baeldung.spring.cloud.ribbon.retry.backoff; + +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.retry.backoff.BackOffPolicy; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.stereotype.Component; + +@Component +@Profile("fixed-backoff") +class FixedBackoffRetryFactory extends RibbonLoadBalancedRetryFactory { + + public FixedBackoffRetryFactory(SpringClientFactory clientFactory) { + super(clientFactory); + } + + @Override + public BackOffPolicy createBackOffPolicy(String service) { + FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); + fixedBackOffPolicy.setBackOffPeriod(2000); + return fixedBackOffPolicy; + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/RibbonConfiguration.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/RibbonConfiguration.java new file mode 100644 index 0000000000..c493b4dbe2 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/RibbonConfiguration.java @@ -0,0 +1,20 @@ +package com.baeldung.spring.cloud.ribbon.retry.config; + +import com.netflix.loadbalancer.IPing; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.PingUrl; +import com.netflix.loadbalancer.WeightedResponseTimeRule; +import org.springframework.context.annotation.Bean; + +public class RibbonConfiguration { + + @Bean + public IPing ribbonPing() { + return new PingUrl(); + } + + @Bean + public IRule ribbonRule() { + return new WeightedResponseTimeRule(); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/WeatherClientRibbonConfiguration.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/WeatherClientRibbonConfiguration.java new file mode 100644 index 0000000000..88955db025 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/config/WeatherClientRibbonConfiguration.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.cloud.ribbon.retry.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +@RibbonClient(name = "weather-service", configuration = RibbonConfiguration.class) +public class WeatherClientRibbonConfiguration { + + @LoadBalanced + @Bean + RestTemplate getRestTemplate() { + return new RestTemplate(); + } + +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/controller/RibbonClientController.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/controller/RibbonClientController.java new file mode 100644 index 0000000000..ebe5b58386 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/controller/RibbonClientController.java @@ -0,0 +1,21 @@ +package com.baeldung.spring.cloud.ribbon.retry.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class RibbonClientController { + + private static final String WEATHER_SERVICE = "weather-service"; + + @Autowired + private RestTemplate restTemplate; + + @GetMapping("/client/weather") + public String weather() { + String result = restTemplate.getForObject("http://" + WEATHER_SERVICE + "/weather", String.class); + return "Weather Service Response: " + result; + } +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/resources/application.yml b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/resources/application.yml new file mode 100644 index 0000000000..3199f38dce --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/main/resources/application.yml @@ -0,0 +1,17 @@ +spring: + profiles: + # fixed-backoff, exponential-backoff, exponential-random-backoff + active: fixed-backoff + application: + name: ribbon-client + +weather-service: + ribbon: + eureka: + enabled: false + listOfServers: http://localhost:8081, http://localhost:8082 + ServerListRefreshInterval: 5000 + MaxAutoRetries: 3 + MaxAutoRetriesNextServer: 1 + OkToRetryOnAllOperations: true + retryableStatusCodes: 503, 408 diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetryFailureIntegrationTest.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetryFailureIntegrationTest.java new file mode 100644 index 0000000000..0e72bdbb86 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetryFailureIntegrationTest.java @@ -0,0 +1,50 @@ +package com.baeldung.spring.cloud.ribbon.retry; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RibbonClientApp.class) +public class RibbonRetryFailureIntegrationTest { + + private static ConfigurableApplicationContext weatherServiceInstance1; + private static ConfigurableApplicationContext weatherServiceInstance2; + + @LocalServerPort + private int port; + private TestRestTemplate restTemplate = new TestRestTemplate(); + + @BeforeAll + public static void setup() { + weatherServiceInstance1 = startApp(8081); + weatherServiceInstance2 = startApp(8082); + } + + @AfterAll + public static void cleanup() { + weatherServiceInstance1.close(); + weatherServiceInstance2.close(); + } + + private static ConfigurableApplicationContext startApp(int port) { + return SpringApplication.run(RibbonWeatherServiceApp.class, "--server.port=" + port, "--successful.call.divisor=6"); + } + + @Test + public void whenRibbonClientIsCalledAndServiceUnavailable_thenFailure() { + String url = "http://localhost:" + port + "/client/weather"; + + ResponseEntity response = restTemplate.getForEntity(url, String.class); + + assertTrue(response.getStatusCode().is5xxServerError()); + } + +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetrySuccessIntegrationTest.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetrySuccessIntegrationTest.java new file mode 100644 index 0000000000..2055159117 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-client-service/src/test/java/com/baeldung/spring/cloud/ribbon/retry/RibbonRetrySuccessIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.cloud.ribbon.retry; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RibbonClientApp.class) +public class RibbonRetrySuccessIntegrationTest { + + private static ConfigurableApplicationContext weatherServiceInstance1; + private static ConfigurableApplicationContext weatherServiceInstance2; + + @LocalServerPort + private int port; + private TestRestTemplate restTemplate = new TestRestTemplate(); + + @BeforeAll + public static void setup() { + weatherServiceInstance1 = startApp(8081); + weatherServiceInstance2 = startApp(8082); + } + + private static ConfigurableApplicationContext startApp(int port) { + return SpringApplication.run(RibbonWeatherServiceApp.class, "--server.port=" + port, "--successful.call.divisor=3"); + } + + @AfterAll + public static void cleanup() { + weatherServiceInstance1.close(); + weatherServiceInstance2.close(); + } + + @Test + public void whenRibbonClientIsCalledAndServiceAvailable_thenSuccess() { + String url = "http://localhost:" + port + "/client/weather"; + + ResponseEntity response = restTemplate.getForEntity(url, String.class); + + assertTrue(response.getStatusCode().is2xxSuccessful()); + assertEquals(response.getBody(), "Weather Service Response: Today's a sunny day"); + } + +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/pom.xml b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/pom.xml new file mode 100644 index 0000000000..f091341025 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + ribbon-weather-service + + + com.baeldung.spring.cloud + spring-cloud-ribbon-retry + 0.0.1-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonWeatherServiceApp.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonWeatherServiceApp.java new file mode 100644 index 0000000000..ceeacbd426 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/RibbonWeatherServiceApp.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.cloud.ribbon.retry; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RibbonWeatherServiceApp { + + public static void main(String[] args) { + SpringApplication.run(RibbonWeatherServiceApp.class, args); + } +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/WeatherController.java b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/WeatherController.java new file mode 100644 index 0000000000..ec0b94e505 --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/java/com/baeldung/spring/cloud/ribbon/retry/WeatherController.java @@ -0,0 +1,39 @@ +package com.baeldung.spring.cloud.ribbon.retry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WeatherController { + + private static final Logger LOGGER = LoggerFactory.getLogger(WeatherController.class); + + private int nrOfCalls = 0; + + @Value("${successful.call.divisor}") + private int divisor; + + @GetMapping("/") + public String health() { + return "I am Ok"; + } + + @GetMapping("/weather") + public ResponseEntity weather() { + LOGGER.info("Providing today's weather information"); + if (isServiceUnavailable()) { + return new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE); + } + LOGGER.info("Today's a sunny day"); + return new ResponseEntity<>("Today's a sunny day", HttpStatus.OK); + } + + private boolean isServiceUnavailable() { + return ++nrOfCalls % divisor != 0; + } +} diff --git a/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/resources/application.properties b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/resources/application.properties new file mode 100644 index 0000000000..ea25e8f2da --- /dev/null +++ b/spring-cloud/spring-cloud-ribbon-retry/ribbon-weather-service/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.application.name=weather-service +successful.call.divisor=3 diff --git a/spring-cloud/spring-cloud-task/pom.xml b/spring-cloud/spring-cloud-task/pom.xml index 377d16a999..e2006ee9d3 100644 --- a/spring-cloud/spring-cloud-task/pom.xml +++ b/spring-cloud/spring-cloud-task/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../parent-boot-1 + ../../parent-boot-2 @@ -40,8 +40,8 @@ - Brixton.SR7 - 1.2.2.RELEASE + Hoxton.SR4 + 2.2.3.RELEASE diff --git a/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml b/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml index fd10322efb..4e6b8b8b6c 100644 --- a/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml +++ b/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml @@ -45,6 +45,13 @@ org.springframework.cloud spring-cloud-task-batch + + + net.bytebuddy + byte-buddy-dep + ${bytebuddy.version} + + com.h2database h2 @@ -63,6 +70,7 @@ com.baeldung.TaskDemo + 1.10.10 diff --git a/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml b/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml index 794ac4d247..8a6e4fc172 100644 --- a/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml +++ b/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml @@ -1,6 +1,6 @@ spring: datasource: - url: jdbc:h2:mem:springcloud + url: jdbc:h2:mem:springcloud;DB_CLOSE_ON_EXIT=FALSE username: sa password: jpa: diff --git a/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml b/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml index 93255959e4..33f6ccde74 100644 --- a/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml +++ b/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml @@ -50,8 +50,7 @@ - 1.2.2.RELEASE - 1.3.0.RELEASE + 2.3.1.RELEASE diff --git a/spring-core-4/README.md b/spring-core-4/README.md index 592f4cd011..11a966f23d 100644 --- a/spring-core-4/README.md +++ b/spring-core-4/README.md @@ -6,4 +6,5 @@ This module contains articles about core Spring functionality - [Creating Spring Beans Through Factory Methods](https://www.baeldung.com/spring-beans-factory-methods) - [How to dynamically Autowire a Bean in Spring](https://www.baeldung.com/spring-dynamic-autowire) +- [Spring @Import Annotation](https://www.baeldung.com/spring-import-annotation) - More articles: [[<-- prev]](/spring-core-3) diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/GlobalEventBus.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java similarity index 96% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/GlobalEventBus.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java index 8b95ea7c6f..8b3c528c4d 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/GlobalEventBus.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanFactoryPostProcessor.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java similarity index 98% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanFactoryPostProcessor.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java index fba31fde6a..e0108655cf 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanFactoryPostProcessor.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import com.google.common.eventbus.EventBus; diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanPostProcessor.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java similarity index 98% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanPostProcessor.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java index 677c839444..be3800c40a 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/GuavaEventBusBeanPostProcessor.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import com.google.common.eventbus.EventBus; diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTrade.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java similarity index 94% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/StockTrade.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java index 7711cf7101..f27f9e7c9b 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTrade.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import java.util.Date; diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradeListener.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java similarity index 73% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradeListener.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java index bf34d66f24..a0ee293293 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradeListener.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; @FunctionalInterface public interface StockTradeListener { diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradePublisher.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java similarity index 96% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradePublisher.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java index bf339872d9..0058944b53 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/StockTradePublisher.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.Subscribe; diff --git a/spring-core-4/src/main/java/com/baeldung/postprocessor/Subscriber.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java similarity index 93% rename from spring-core-4/src/main/java/com/baeldung/postprocessor/Subscriber.java rename to spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java index bef38333d6..1aca507555 100644 --- a/spring-core-4/src/main/java/com/baeldung/postprocessor/Subscriber.java +++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java new file mode 100644 index 0000000000..94f22788b8 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java @@ -0,0 +1,9 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({ MammalConfiguration.class, BirdConfig.class }) +class AnimalConfiguration { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java new file mode 100644 index 0000000000..9b4310b6d3 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java @@ -0,0 +1,9 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +public class AnimalScanConfiguration { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java new file mode 100644 index 0000000000..a785cf7641 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java @@ -0,0 +1,4 @@ +package com.baeldung.importannotation.animal; + +class Bird { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java new file mode 100644 index 0000000000..c5cefe8b22 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java @@ -0,0 +1,13 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class BirdConfig { + + @Bean + Bird bird() { + return new Bird(); + } +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java new file mode 100644 index 0000000000..6abe08e393 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java @@ -0,0 +1,7 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.stereotype.Component; + +@Component(value = "bug") +class Bug { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java new file mode 100644 index 0000000000..9bea16413a --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java @@ -0,0 +1,9 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import(Bug.class) +class BugConfig { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java new file mode 100644 index 0000000000..7eb36c81ce --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java @@ -0,0 +1,4 @@ +package com.baeldung.importannotation.animal; + +class Cat { +} \ No newline at end of file diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java new file mode 100644 index 0000000000..ebb35ffc11 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java @@ -0,0 +1,13 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class CatConfig { + + @Bean + Cat cat() { + return new Cat(); + } +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java new file mode 100644 index 0000000000..00374c1bc0 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java @@ -0,0 +1,4 @@ +package com.baeldung.importannotation.animal; + +class Dog { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java new file mode 100644 index 0000000000..c11ee44623 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java @@ -0,0 +1,13 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class DogConfig { + + @Bean + Dog dog() { + return new Dog(); + } +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java new file mode 100644 index 0000000000..3d77ac878c --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java @@ -0,0 +1,9 @@ +package com.baeldung.importannotation.animal; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({ DogConfig.class, CatConfig.class }) +class MammalConfiguration { +} diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java b/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java new file mode 100644 index 0000000000..01aa36a796 --- /dev/null +++ b/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.importannotation.zoo; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import com.baeldung.importannotation.animal.AnimalScanConfiguration; + +@Configuration +@Import(AnimalScanConfiguration.class) +class ZooApplication { +} diff --git a/spring-core-4/src/test/java/com/baeldung/postprocessor/PostProcessorConfiguration.java b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java similarity index 92% rename from spring-core-4/src/test/java/com/baeldung/postprocessor/PostProcessorConfiguration.java rename to spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java index b28e36663a..842283f563 100644 --- a/spring-core-4/src/test/java/com/baeldung/postprocessor/PostProcessorConfiguration.java +++ b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/spring-core-4/src/test/java/com/baeldung/postprocessor/StockTradeIntegrationTest.java b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java similarity index 97% rename from spring-core-4/src/test/java/com/baeldung/postprocessor/StockTradeIntegrationTest.java rename to spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java index ae3cd968dc..74d6765ecd 100644 --- a/spring-core-4/src/test/java/com/baeldung/postprocessor/StockTradeIntegrationTest.java +++ b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java @@ -1,4 +1,4 @@ -package com.baeldung.postprocessor; +package com.baeldung.beanpostprocessor; import java.time.Duration; import java.util.Date; diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java new file mode 100644 index 0000000000..7f4795da25 --- /dev/null +++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.importannotation.animal; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { AnimalConfiguration.class }) +class AnimalConfigUnitTest { + + @Autowired + ApplicationContext context; + + @Test + void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() { + assertThatBeanExists("dog", Dog.class); + assertThatBeanExists("cat", Cat.class); + assertThatBeanExists("bird", Cat.class); + } + + private void assertThatBeanExists(String beanName, Class beanClass) { + assertTrue(context.containsBean(beanName)); + assertNotNull(context.getBean(beanClass)); + } +} diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java new file mode 100644 index 0000000000..2a2e0b332a --- /dev/null +++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.importannotation.animal; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = BugConfig.class) +class BugConfigUnitTest { + + @Autowired + ApplicationContext context; + + @Test + void givenImportInComponent_whenLookForBean_shallFindIt() { + assertTrue(context.containsBean("bug")); + assertNotNull(context.getBean(Bug.class)); + } +} diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java new file mode 100644 index 0000000000..dadd2abae6 --- /dev/null +++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.importannotation.animal; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { BirdConfig.class, CatConfig.class, DogConfig.class }) +class ConfigUnitTest { + + @Autowired + ApplicationContext context; + + @Test + void givenImportedBeans_whenGettingEach_shallFindIt() { + assertThatBeanExists("dog", Dog.class); + assertThatBeanExists("cat", Cat.class); + assertThatBeanExists("bird", Bird.class); + } + + private void assertThatBeanExists(String beanName, Class beanClass) { + assertTrue(context.containsBean(beanName)); + assertNotNull(context.getBean(beanClass)); + } +} diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java new file mode 100644 index 0000000000..5e1596253c --- /dev/null +++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java @@ -0,0 +1,33 @@ +package com.baeldung.importannotation.animal; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { MammalConfiguration.class }) +class MammalConfigUnitTest { + + @Autowired + ApplicationContext context; + + @Test + void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() { + assertThatBeanExists("dog", Dog.class); + assertThatBeanExists("cat", Cat.class); + + assertFalse(context.containsBean("bird")); + } + + private void assertThatBeanExists(String beanName, Class beanClass) { + assertTrue(context.containsBean(beanName)); + assertNotNull(context.getBean(beanClass)); + } +} diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java new file mode 100644 index 0000000000..e832e27b28 --- /dev/null +++ b/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java @@ -0,0 +1,26 @@ +package com.baeldung.importannotation.zoo; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ZooApplication.class) +class ZooApplicationUnitTest { + + @Autowired + ApplicationContext context; + + @Test + void givenTheScanInTheAnimalPackage_whenGettingAnyAnimal_shallFindItInTheContext() { + assertNotNull(context.getBean("dog")); + assertNotNull(context.getBean("bird")); + assertNotNull(context.getBean("cat")); + assertNotNull(context.getBean("bug")); + } +} diff --git a/spring-jinq/pom.xml b/spring-jinq/pom.xml index 073808823c..647c0907a7 100644 --- a/spring-jinq/pom.xml +++ b/spring-jinq/pom.xml @@ -37,12 +37,6 @@ spring-boot-starter-data-jpa - - net.bytebuddy - byte-buddy-dep - ${bytebuddy.version} - - org.springframework @@ -73,7 +67,6 @@ 1.8.29 - 1.10.10 diff --git a/spring-rest-hal-browser/pom.xml b/spring-rest-hal-browser/pom.xml index 32a0b52875..7b629dba44 100644 --- a/spring-rest-hal-browser/pom.xml +++ b/spring-rest-hal-browser/pom.xml @@ -35,11 +35,6 @@ com.h2database h2 - - net.bytebuddy - byte-buddy-dep - ${bytebuddy.version} - @@ -56,7 +51,6 @@ - 1.10.10 1.8 1.8 diff --git a/spring-rest-http/README.md b/spring-rest-http/README.md index 35793cb281..f78f8784b0 100644 --- a/spring-rest-http/README.md +++ b/spring-rest-http/README.md @@ -13,3 +13,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Spring RequestMapping](https://www.baeldung.com/spring-requestmapping) - [Guide to DeferredResult in Spring](https://www.baeldung.com/spring-deferred-result) - [Using JSON Patch in Spring REST APIs](https://www.baeldung.com/spring-rest-json-patch) +- [Using OpenAPI and JSON Request Parameters](https://www.baeldung.com/openapi-json-query-parameters) diff --git a/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml new file mode 100644 index 0000000000..53272c9cdb --- /dev/null +++ b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml @@ -0,0 +1,75 @@ +swagger: "2.0" +info: + description: "This is a sample server." + version: "1.0.0" + title: "Sample API to send JSON objects as query parameters using OpenAPI 2" +tags: +- name: "tickets" + description: "Send Tickets as JSON Objects" +schemes: +- "https" +- "http" +paths: + /tickets: + get: + tags: + - "tickets" + summary: "Send an JSON Object as a query param" + parameters: + - name: "params" + in: "path" + description: "{\"type\":\"foo\",\"color\":\"green\"}" + required: true + type: "string" + responses: + "200": + description: "Successful operation" + "401": + description: "Unauthorized" + "403": + description: "Forbidden" + "404": + description: "Not found" + post: + tags: + - "tickets" + summary: "Send an JSON Object in body" + parameters: + - name: "params" + in: "body" + description: "Parameter is an JSON object with the `type` and `color` properties that should be serialized as JSON {\"type\":\"foo\",\"color\":\"green\"}" + required: true + schema: + type: string + responses: + "200": + description: "Successful operation" + "401": + description: "Unauthorized" + "403": + description: "Forbidden" + "404": + description: "Not found" + "405": + description: "Invalid input" + /tickets2: + get: + tags: + - "tickets" + summary: "Send an JSON Object in body of get reqest" + parameters: + - name: "params" + in: "body" + description: "Parameter is an JSON object with the `type` and `color` properties that should be serialized as JSON {\"type\":\"foo\",\"color\":\"green\"}" + required: true + schema: + type: string + responses: + "200": + description: "Successful operation" + "401": + description: "Unauthorized" + "403": + description: "Forbidden" + "404": + description: "Not found" diff --git a/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml new file mode 100644 index 0000000000..a0ed147b9d --- /dev/null +++ b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml @@ -0,0 +1,37 @@ +openapi: 3.0.1 +info: + title: Sample API to send JSON objects as query parameters using OpenAPI 3 + description: This is a sample server. + version: 1.0.0 +servers: +- url: /api +tags: +- name: tickets + description: Send Tickets as JSON Objects +paths: + /tickets: + get: + tags: + - tickets + summary: Send an JSON Object as a query param + parameters: + - name: params + in: query + description: '{"type":"foo","color":"green"}' + required: true + schema: + type: object + properties: + type: + type: "string" + color: + type: "string" + responses: + 200: + description: Successful operation + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: Not found diff --git a/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java new file mode 100644 index 0000000000..93949e52a3 --- /dev/null +++ b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java @@ -0,0 +1,50 @@ +package com.baeldung.resttemplate.proxy; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Proxy.Type; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * This class is used to test a request using {@link RestTemplate} with {@link Proxy} + * using a {@link SimpleClientHttpRequestFactory} as configuration. + *
+ *
+ * + * Before running the test we should change the PROXY_SERVER_HOST + * and PROXY_SERVER_PORT constants in our class to match our preferred proxy configuration. + */ +public class RequestFactoryLiveTest { + + private static final String PROXY_SERVER_HOST = "127.0.0.1"; + private static final int PROXY_SERVER_PORT = 8080; + + RestTemplate restTemplate; + + @Before + public void setUp() { + Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress(PROXY_SERVER_HOST, PROXY_SERVER_PORT)); + + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setProxy(proxy); + + restTemplate = new RestTemplate(requestFactory); + } + + @Test + public void givenRestTemplate_whenRequestedWithProxy_thenResponseBodyIsOk() { + ResponseEntity responseEntity = restTemplate.getForEntity("http://httpbin.org/get", String.class); + + assertThat(responseEntity.getStatusCode(), is(equalTo(HttpStatus.OK))); + } +} diff --git a/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java new file mode 100644 index 0000000000..faeb49537a --- /dev/null +++ b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java @@ -0,0 +1,69 @@ +package com.baeldung.resttemplate.proxy; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.net.Proxy; + +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * This class is used to test a request using {@link RestTemplate} with {@link Proxy} + * using a {@link RestTemplateCustomizer} as configuration. + *
+ *
+ * + * Before running the test we should change the PROXY_SERVER_HOST + * and PROXY_SERVER_PORT constants in our class to match our preferred proxy configuration. + */ +public class RestTemplateCustomizerLiveTest { + + private static final String PROXY_SERVER_HOST = "127.0.0.1"; + private static final int PROXY_SERVER_PORT = 8080; + + RestTemplate restTemplate; + + @Before + public void setUp() { + restTemplate = new RestTemplateBuilder(new ProxyCustomizer()).build(); + } + + @Test + public void givenRestTemplate_whenRequestedWithProxy_thenResponseBodyIsOk() { + ResponseEntity responseEntity = restTemplate.getForEntity("http://httpbin.org/get", String.class); + + assertThat(responseEntity.getStatusCode(), is(equalTo(HttpStatus.OK))); + } + + private static class ProxyCustomizer implements RestTemplateCustomizer { + + @Override + public void customize(RestTemplate restTemplate) { + HttpHost proxy = new HttpHost(PROXY_SERVER_HOST, PROXY_SERVER_PORT); + HttpClient httpClient = HttpClientBuilder.create() + .setRoutePlanner(new DefaultProxyRoutePlanner(proxy) { + @Override + public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException { + return super.determineProxy(target, request, context); + } + }) + .build(); + restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); + } + } +} diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 60a662781f..954b9335e4 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -20,7 +20,6 @@ spring-security-cache-control spring-security-core spring-security-cors - spring-security-kerberos spring-security-mvc spring-security-mvc-boot-1 spring-security-mvc-boot-2 diff --git a/spring-security-modules/spring-security-kerberos/README.md b/spring-security-modules/spring-security-kerberos/README.md deleted file mode 100644 index a868fb86b7..0000000000 --- a/spring-security-modules/spring-security-kerberos/README.md +++ /dev/null @@ -1,13 +0,0 @@ -## Spring Security Kerberos - -This module contains articles about Spring Security Kerberos - -### Relevant Articles: - -- [Introduction to SPNEGO/Kerberos Authentication in Spring](https://www.baeldung.com/spring-security-kerberos) - -### @PreFilter and @PostFilter annotations - -### Build the Project ### - -`mvn clean install` \ No newline at end of file diff --git a/spring-security-modules/spring-security-kerberos/pom.xml b/spring-security-modules/spring-security-kerberos/pom.xml deleted file mode 100644 index 51a48a78c6..0000000000 --- a/spring-security-modules/spring-security-kerberos/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - 4.0.0 - spring-security-kerberos - 0.1-SNAPSHOT - spring-security-kerberos - war - - - com.baeldung - parent-boot-2 - 0.0.1-SNAPSHOT - ../../parent-boot-2 - - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-security - - - - org.springframework.security.kerberos - spring-security-kerberos-core - ${spring-security-kerberos.version} - - - org.springframework.security.kerberos - spring-security-kerberos-web - ${spring-security-kerberos.version} - - - org.springframework.security.kerberos - spring-security-kerberos-client - ${spring-security-kerberos.version} - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - - - - - org.apache.maven.plugins - maven-war-plugin - - - - - - 1.0.1.RELEASE - - - diff --git a/spring-security-modules/spring-security-mvc-boot-1/pom.xml b/spring-security-modules/spring-security-mvc-boot-1/pom.xml index b00b7bab32..7ad18376ec 100644 --- a/spring-security-modules/spring-security-mvc-boot-1/pom.xml +++ b/spring-security-modules/spring-security-mvc-boot-1/pom.xml @@ -106,10 +106,6 @@ ${ehcache-core.version} jar - - net.bytebuddy - byte-buddy - diff --git a/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md index 3aa092edb8..4bb0eea16c 100644 --- a/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md +++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md @@ -1,3 +1,4 @@ ## Relevant articles: - [Spring Security Kerberos Integration](https://www.baeldung.com/spring-security-kerberos-integration) +- [Introduction to SPNEGO/Kerberos Authentication in Spring](https://www.baeldung.com/spring-security-kerberos) diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java similarity index 90% rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java rename to spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java index 37dbe7dab8..2cddbf0f22 100644 --- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java +++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.intro; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java similarity index 97% rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java rename to spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java index c1c206e5c9..cc694a3b83 100644 --- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java +++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java @@ -1,6 +1,5 @@ -package com.baeldung.config; +package com.baeldung.intro.config; -import com.baeldung.security.DummyUserDetailsService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileSystemResource; @@ -16,6 +15,8 @@ import org.springframework.security.kerberos.web.authentication.SpnegoAuthentica import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import com.baeldung.intro.security.DummyUserDetailsService; + @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java similarity index 94% rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java rename to spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java index 6ddd6c8969..f564c9f756 100644 --- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java +++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java @@ -1,4 +1,4 @@ -package com.baeldung.security; +package com.baeldung.intro.security; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; diff --git a/spring-social-login/pom.xml b/spring-social-login/pom.xml index 628f439cc0..0de20cd087 100644 --- a/spring-social-login/pom.xml +++ b/spring-social-login/pom.xml @@ -62,12 +62,6 @@ h2 - - net.bytebuddy - byte-buddy-dep - ${bytebuddy.version} - - @@ -102,7 +96,6 @@ - 1.10.9 2.0.3.RELEASE diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md index 329228186f..1a013f5de3 100644 --- a/testing-modules/mockito-2/README.md +++ b/testing-modules/mockito-2/README.md @@ -5,4 +5,4 @@ - [Mockito Strict Stubbing and The UnnecessaryStubbingException](https://www.baeldung.com/mockito-unnecessary-stubbing-exception) - [Mockito and Fluent APIs](https://www.baeldung.com/mockito-fluent-apis) - [Mocking the ObjectMapper readValue() Method](https://www.baeldung.com/mockito-mock-jackson-read-value) -- [Introduction to Mockito’s AdditionalAnswers](https://www.baeldung.com/mockito-additionalanswers) +- [Introduction to Mockito’s AdditionalAnswers](https://www.baeldung.com/mockito-additionalanswers) \ No newline at end of file diff --git a/twitter4j/pom.xml b/twitter4j/pom.xml index 274b5c75c3..0c36e72892 100644 --- a/twitter4j/pom.xml +++ b/twitter4j/pom.xml @@ -2,7 +2,7 @@ 4.0.0 - twitter4J + twitter4j twitter4J jar