diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameBoard.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameBoard.java new file mode 100644 index 0000000000..b10bd95a19 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameBoard.java @@ -0,0 +1,129 @@ +package com.baeldung.algorithms.connect4; + +import java.util.ArrayList; +import java.util.List; + +public class GameBoard { + private final List> columns; + + private final int rows; + + public GameBoard(int columns, int rows) { + this.rows = rows; + this.columns = new ArrayList<>(); + + for (int i = 0; i < columns; ++i) { + this.columns.add(new ArrayList<>()); + } + } + + public int getRows() { + return rows; + } + + public int getColumns() { + return columns.size(); + } + + public Piece getCell(int x, int y) { + assert(x >= 0 && x < getColumns()); + assert(y >= 0 && y < getRows()); + + List column = columns.get(x); + + if (column.size() > y) { + return column.get(y); + } else { + return null; + } + } + + public boolean move(int x, Piece player) { + assert(x >= 0 && x < getColumns()); + + List column = columns.get(x); + + if (column.size() >= this.rows) { + throw new IllegalArgumentException("That column is full"); + } + + column.add(player); + + return checkWin(x, column.size() - 1, player); + } + + private boolean checkWin(int x, int y, Piece player) { + // Vertical line + if (checkLine(x, y, 0, -1, player)) { + return true; + } + + for (int offset = 0; offset < 4; ++offset) { + // Horizontal line + if (checkLine(x - 3 + offset, y, 1, 0, player)) { + return true; + } + + // Leading diagonal + if (checkLine(x - 3 + offset, y + 3 - offset, 1, -1, player)) { + return true; + } + + // Trailing diagonal + if (checkLine(x - 3 + offset, y - 3 + offset, 1, 1, player)) { + return true; + } + } + + return false; + } + + private boolean checkLine(int x1, int y1, int xDiff, int yDiff, Piece player) { + for (int i = 0; i < 4; ++i) { + int x = x1 + (xDiff * i); + int y = y1 + (yDiff * i); + + if (x < 0 || x > columns.size() - 1) { + return false; + } + + if (y < 0 || y > rows - 1) { + return false; + } + + if (player != getCell(x, y)) { + return false; + } + } + + return true; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + + for (int y = getRows() - 1; y >= 0; --y) { + for (int x = 0; x < getColumns(); ++x) { + Piece piece = getCell(x, y); + + result.append("|"); + if (piece == null) { + result.append(" "); + } else if (piece == Piece.PLAYER_1) { + result.append("X"); + } else if (piece == Piece.PLAYER_2) { + result.append("O"); + } + } + + result.append("|\n"); + for (int i = 0; i < getColumns(); ++i) { + result.append("+-"); + } + result.append("+\n"); + } + + return result.toString(); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameUnitTest.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameUnitTest.java new file mode 100644 index 0000000000..f340644950 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/GameUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.algorithms.connect4; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class GameUnitTest { + @Test + public void blankGame() { + GameBoard gameBoard = new GameBoard(8, 6); + + System.out.println(gameBoard); + } + + @Test + public void playedGame() { + GameBoard gameBoard = new GameBoard(8, 6); + + assertFalse(gameBoard.move(3, Piece.PLAYER_1)); + assertFalse(gameBoard.move(2, Piece.PLAYER_2)); + + assertFalse(gameBoard.move(4, Piece.PLAYER_1)); + assertFalse(gameBoard.move(3, Piece.PLAYER_2)); + + assertFalse(gameBoard.move(5, Piece.PLAYER_1)); + assertFalse(gameBoard.move(6, Piece.PLAYER_2)); + + assertFalse(gameBoard.move(5, Piece.PLAYER_1)); + assertFalse(gameBoard.move(4, Piece.PLAYER_2)); + + assertFalse(gameBoard.move(5, Piece.PLAYER_1)); + assertFalse(gameBoard.move(5, Piece.PLAYER_2)); + + assertFalse(gameBoard.move(6, Piece.PLAYER_1)); + assertTrue(gameBoard.move(4, Piece.PLAYER_2)); + + System.out.println(gameBoard); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/Piece.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/Piece.java new file mode 100644 index 0000000000..5a8724c09b --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/connect4/Piece.java @@ -0,0 +1,6 @@ +package com.baeldung.algorithms.connect4; + +public enum Piece { + PLAYER_1, + PLAYER_2 +} diff --git a/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/rounddate/RoundDate.java b/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/rounddate/RoundDate.java new file mode 100644 index 0000000000..6fb7cf87b7 --- /dev/null +++ b/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/rounddate/RoundDate.java @@ -0,0 +1,113 @@ +package com.baeldung.rounddate; + +import java.time.DayOfWeek; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAdjusters; +import java.util.Calendar; +import java.util.Date; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + +public class RoundDate { + public static Date getDate(int year, int month, int day, int hour, int minute) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + return calendar.getTime(); + } + + public static Date roundToDay(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + public static Date roundToNearestUnit(Date date, int unit) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + + switch (unit) { + case Calendar.HOUR: + int minute = calendar.get(Calendar.MINUTE); + if (minute >= 0 && minute < 15) { + calendar.set(Calendar.MINUTE, 0); + } else if (minute >= 15 && minute < 45) { + calendar.set(Calendar.MINUTE, 30); + } else { + calendar.set(Calendar.MINUTE, 0); + calendar.add(Calendar.HOUR_OF_DAY, 1); + } + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + break; + + case Calendar.DAY_OF_MONTH: + int hour = calendar.get(Calendar.HOUR_OF_DAY); + if (hour >= 12) { + calendar.add(Calendar.DAY_OF_MONTH, 1); + } + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + break; + + case Calendar.MONTH: + int day = calendar.get(Calendar.DAY_OF_MONTH); + if (day >= 15) { + calendar.add(Calendar.MONTH, 1); + } + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + break; + } + + return calendar.getTime(); + } + + public static LocalDateTime roundToStartOfMonthUsingLocalDateTime(LocalDateTime dateTime) { + return dateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0); + } + + public static LocalDateTime roundToEndOfWeekUsingLocalDateTime(LocalDateTime dateTime) { + return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY)) + .withHour(23) + .withMinute(59) + .withSecond(59) + .withNano(999); + } + + public static ZonedDateTime roundToStartOfMonthUsingZonedDateTime(ZonedDateTime dateTime) { + return dateTime.withDayOfMonth(1) + .withHour(0) + .withMinute(0) + .withSecond(0) + .with(ChronoField.MILLI_OF_SECOND, 0) + .with(ChronoField.MICRO_OF_SECOND, 0) + .with(ChronoField.NANO_OF_SECOND, 0); + } + + public static ZonedDateTime roundToEndOfWeekUsingZonedDateTime(ZonedDateTime dateTime) { + return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY)) + .withHour(23) + .withMinute(59) + .withSecond(59) + .with(ChronoField.MILLI_OF_SECOND, 999) + .with(ChronoField.MICRO_OF_SECOND, 999) + .with(ChronoField.NANO_OF_SECOND, 999); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/rounddate/DateRoundingUnitTest.java b/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/rounddate/DateRoundingUnitTest.java new file mode 100644 index 0000000000..bbe49d8340 --- /dev/null +++ b/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/rounddate/DateRoundingUnitTest.java @@ -0,0 +1,63 @@ +package com.baeldung.rounddate; + +import org.junit.Test; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.assertEquals; + +public class DateRoundingUnitTest { + + @Test + public void givenDate_whenRoundToDay_thenBeginningOfDay() { + Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 30); + Date result = RoundDate.roundToDay(date); + assertEquals(RoundDate.getDate(2023, Calendar.JANUARY, 27, 0, 0), result); + } + + @Test + public void givenDate_whenRoundToNearestUnit_thenNearestUnit() { + Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 12); + Date result = RoundDate.roundToNearestUnit(date, Calendar.DAY_OF_MONTH); + Date expected = RoundDate.getDate(2023, Calendar.JANUARY, 28, 0, 0); + assertEquals(expected, result); + } + + @Test + public void givenLocalDateTime_whenRoundToStartOfMonth_thenStartOfMonth() { + LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12); + LocalDateTime result = RoundDate.roundToStartOfMonthUsingLocalDateTime(dateTime); + LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 0, 0, 0); + assertEquals(expected, result); + } + + + @Test + public void givenZonedDateTime_whenRoundToStartOfMonth_thenStartOfMonth() { + ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault()); + ZonedDateTime result = RoundDate.roundToStartOfMonthUsingZonedDateTime(dateTime); + ZonedDateTime expected = ZonedDateTime.of(2023, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()); + assertEquals(expected, result); + } + + @Test + public void givenLocalDateTime_whenRoundToEndOfWeek_thenEndOfWeek() { + LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12); + LocalDateTime result = RoundDate.roundToEndOfWeekUsingLocalDateTime(dateTime); + LocalDateTime expected = LocalDateTime.of(2023, 1, 28, 23, 59, 59, 999); + assertEquals(expected, result); + } + + @Test + public void givenZonedDateTime_whenRoundToEndOfWeek_thenEndOfWeek() { + ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault()); + ZonedDateTime result = RoundDate.roundToEndOfWeekUsingZonedDateTime(dateTime); + ZonedDateTime expected = ZonedDateTime.of(2023, 1, 28, 23, 59, 59, 999, ZoneId.systemDefault()); + assertEquals(expected, result); + } + +} diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/CustomSpliterator.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/CustomSpliterator.java new file mode 100644 index 0000000000..2f806cccda --- /dev/null +++ b/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/CustomSpliterator.java @@ -0,0 +1,48 @@ +package com.baeldung.spliterator; + +import java.util.List; +import java.util.Spliterator; +import java.util.function.Consumer; + +public class CustomSpliterator implements Spliterator { + private final List elements; + private int currentIndex; + + public CustomSpliterator(List elements) { + this.elements = elements; + this.currentIndex = 0; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (currentIndex < elements.size()) { + action.accept(elements.get(currentIndex)); + currentIndex++; + return true; + } + return false; + } + + @Override + public Spliterator trySplit() { + int currentSize = elements.size() - currentIndex; + if (currentSize < 2) { + return null; + } + + int splitIndex = currentIndex + currentSize / 2; + CustomSpliterator newSpliterator = new CustomSpliterator(elements.subList(currentIndex, splitIndex)); + currentIndex = splitIndex; + return newSpliterator; + } + + @Override + public long estimateSize() { + return elements.size() - currentIndex; + } + + @Override + public int characteristics() { + return ORDERED | SIZED | SUBSIZED | NONNULL; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/Executor.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/Executor.java deleted file mode 100644 index 3dc2fa06bb..0000000000 --- a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/Executor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.baeldung.spliterator; - -import java.util.stream.Stream; - -public class Executor { - - public static int countAutors(Stream stream) { - RelatedAuthorCounter wordCounter = stream.reduce(new RelatedAuthorCounter(0, true), - RelatedAuthorCounter::accumulate, RelatedAuthorCounter::combine); - return wordCounter.getCounter(); - } -} \ No newline at end of file diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorCounter.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorCounter.java deleted file mode 100644 index 8800eac55b..0000000000 --- a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorCounter.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.baeldung.spliterator; - -public class RelatedAuthorCounter { - private final int counter; - private final boolean isRelated; - - public RelatedAuthorCounter(int counter, boolean isRelated) { - this.counter = counter; - this.isRelated = isRelated; - } - - public RelatedAuthorCounter accumulate(Author author) { - if (author.getRelatedArticleId() == 0) { - return isRelated ? this : new RelatedAuthorCounter(counter, true); - } else { - return isRelated ? new RelatedAuthorCounter(counter + 1, false) : this; - } - } - - public RelatedAuthorCounter combine(RelatedAuthorCounter RelatedAuthorCounter) { - return new RelatedAuthorCounter(counter + RelatedAuthorCounter.counter, RelatedAuthorCounter.isRelated); - } - - public int getCounter() { - return counter; - } -} diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorSpliterator.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorSpliterator.java deleted file mode 100644 index 93c83b8127..0000000000 --- a/core-java-modules/core-java-8/src/main/java/com/baeldung/spliterator/RelatedAuthorSpliterator.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.baeldung.spliterator; - -import java.util.List; -import java.util.Spliterator; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -public class RelatedAuthorSpliterator implements Spliterator { - private final List list; - AtomicInteger current = new AtomicInteger(); - - public RelatedAuthorSpliterator(List list) { - this.list = list; - } - - @Override - public boolean tryAdvance(Consumer action) { - - action.accept(list.get(current.getAndIncrement())); - return current.get() < list.size(); - } - - @Override - public Spliterator trySplit() { - int currentSize = list.size() - current.get(); - if (currentSize < 10) { - return null; - } - for (int splitPos = currentSize / 2 + current.intValue(); splitPos < list.size(); splitPos++) { - if (list.get(splitPos).getRelatedArticleId() == 0) { - Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current.get(), splitPos)); - current.set(splitPos); - return spliterator; - } - } - return null; - } - - @Override - public long estimateSize() { - return list.size() - current.get(); - } - - @Override - public int characteristics() { - return CONCURRENT; - } - -} diff --git a/core-java-modules/core-java-8/src/test/java/com/baeldung/spliterator/ExecutorUnitTest.java b/core-java-modules/core-java-8/src/test/java/com/baeldung/spliterator/ExecutorUnitTest.java index 4628f95481..30760ddf6f 100644 --- a/core-java-modules/core-java-8/src/test/java/com/baeldung/spliterator/ExecutorUnitTest.java +++ b/core-java-modules/core-java-8/src/test/java/com/baeldung/spliterator/ExecutorUnitTest.java @@ -6,10 +6,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Spliterator; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.stream.StreamSupport; - import org.junit.Before; import org.junit.Test; @@ -18,22 +18,50 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class ExecutorUnitTest { Article article; - Stream stream; - Spliterator spliterator; @Before public void init() { article = new Article(Arrays.asList(new Author("Ahmad", 0), new Author("Eugen", 0), new Author("Alice", 1), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Michał", 0), new Author("Loredana", 1)), 0); - - spliterator = new RelatedAuthorSpliterator(article.getListOfAuthors()); } @Test - public void givenAstreamOfAuthors_whenProcessedInParallelWithCustomSpliterator_coubtProducessRightOutput() { - Stream stream2 = StreamSupport.stream(spliterator, true); - assertThat(Executor.countAutors(stream2.parallel())).isEqualTo(9); + public void givenAStreamOfIntegers_whenProcessedSequentialCustomSpliterator_countProducesRightOutput() { + List numbers = new ArrayList<>(); + numbers.add(1); + numbers.add(2); + numbers.add(3); + numbers.add(4); + numbers.add(5); + + CustomSpliterator customSpliterator = new CustomSpliterator(numbers); + + AtomicInteger sum = new AtomicInteger(); + + customSpliterator.forEachRemaining(sum::addAndGet); + assertThat(sum.get()).isEqualTo(15); + } + + @Test + public void givenAStreamOfIntegers_whenProcessedInParallelWithCustomSpliterator_countProducesRightOutput() { + List numbers = new ArrayList<>(); + numbers.add(1); + numbers.add(2); + numbers.add(3); + numbers.add(4); + numbers.add(5); + + CustomSpliterator customSpliterator = new CustomSpliterator(numbers); + + // Create a ForkJoinPool for parallel processing + ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); + + AtomicInteger sum = new AtomicInteger(0); + + // Process elements in parallel using parallelStream + forkJoinPool.submit(() -> customSpliterator.forEachRemaining(sum::addAndGet)).join(); + assertThat(sum.get()).isEqualTo(15); } @Test @@ -43,9 +71,7 @@ public class ExecutorUnitTest { .collect(Collectors.toList()); Spliterator
spliterator = articles.spliterator(); - while (spliterator.tryAdvance(article -> article.setName(article.getName() - .concat("- published by Baeldung")))) - ; + while(spliterator.tryAdvance(article -> article.setName(article.getName().concat("- published by Baeldung")))); articles.forEach(article -> assertThat(article.getName()).isEqualTo("Java- published by Baeldung")); } diff --git a/core-java-modules/core-java-arrays-operations-advanced/pom.xml b/core-java-modules/core-java-arrays-operations-advanced/pom.xml index cbcd6ae440..52787cc0c9 100644 --- a/core-java-modules/core-java-arrays-operations-advanced/pom.xml +++ b/core-java-modules/core-java-arrays-operations-advanced/pom.xml @@ -57,5 +57,9 @@ + + + 11 + \ No newline at end of file diff --git a/core-java-modules/core-java-arrays-operations-advanced/src/test/java/com/baeldung/arraycompare/ArraysCompareUnitTest.java b/core-java-modules/core-java-arrays-operations-advanced/src/test/java/com/baeldung/arraycompare/ArraysCompareUnitTest.java new file mode 100644 index 0000000000..828560d71c --- /dev/null +++ b/core-java-modules/core-java-arrays-operations-advanced/src/test/java/com/baeldung/arraycompare/ArraysCompareUnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.arraycompare; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +class ArraysCompareUnitTest { + + @Test + void givenSameContents_whenCompare_thenCorrect() { + String[] array1 = new String[] { "A", "B", "C" }; + String[] array2 = new String[] { "A", "B", "C" }; + + assertThat(Arrays.compare(array1, array2)).isEqualTo(0); + } + + @Test + void givenDifferentContents_whenCompare_thenDifferent() { + String[] array1 = new String[] { "A", "B", "C" }; + String[] array2 = new String[] { "A", "C", "B", "D" }; + + assertThat(Arrays.compare(array1, array2)).isLessThan(0); + assertThat(Arrays.compare(array2, array1)).isGreaterThan(0); + assertThat(Arrays.compare(array1, null)).isGreaterThan(0); + } + +} diff --git a/core-java-modules/core-java-arrays-operations-basic-2/README.md b/core-java-modules/core-java-arrays-operations-basic-2/README.md index da2f17111f..07eb82ec68 100644 --- a/core-java-modules/core-java-arrays-operations-basic-2/README.md +++ b/core-java-modules/core-java-arrays-operations-basic-2/README.md @@ -3,3 +3,4 @@ This module contains articles about Java array fundamentals. They assume no previous background knowledge on working with arrays. ### Relevant Articles: +- [Arrays mismatch() Method in Java](https://www.baeldung.com/java-arrays-mismatch) diff --git a/core-java-modules/core-java-collections-5/README.md b/core-java-modules/core-java-collections-5/README.md index e478d87ad0..1939e5ca3f 100644 --- a/core-java-modules/core-java-collections-5/README.md +++ b/core-java-modules/core-java-collections-5/README.md @@ -7,4 +7,5 @@ - [Creating Custom Iterator in Java](https://www.baeldung.com/java-creating-custom-iterator) - [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods) - [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration) +- [Remove Elements From a Queue Using Loop](https://www.baeldung.com/java-remove-elements-queue) - More articles: [[<-- prev]](/core-java-modules/core-java-collections-4) diff --git a/core-java-modules/core-java-collections-maps-7/README.md b/core-java-modules/core-java-collections-maps-7/README.md index 2724df5695..5599187cd8 100644 --- a/core-java-modules/core-java-collections-maps-7/README.md +++ b/core-java-modules/core-java-collections-maps-7/README.md @@ -1,2 +1,3 @@ ## Relevant Articles - [Difference Between putIfAbsent() and computeIfAbsent() in Java’s Map](https://www.baeldung.com/java-map-putifabsent-computeifabsent) +- [How to Write Hashmap to CSV File](https://www.baeldung.com/java-write-hashmap-csv) diff --git a/core-java-modules/core-java-collections-maps-7/pom.xml b/core-java-modules/core-java-collections-maps-7/pom.xml index bcc0915073..cefee201cc 100644 --- a/core-java-modules/core-java-collections-maps-7/pom.xml +++ b/core-java-modules/core-java-collections-maps-7/pom.xml @@ -79,5 +79,18 @@ 1.5 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.util=ALL-UNNAMED + + + + + diff --git a/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/linkedhashmapfirstandlastentry/GetFirstAndLastEntryFromLinkedHashMapUnitTest.java b/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/linkedhashmapfirstandlastentry/GetFirstAndLastEntryFromLinkedHashMapUnitTest.java new file mode 100644 index 0000000000..b872e8bdda --- /dev/null +++ b/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/linkedhashmapfirstandlastentry/GetFirstAndLastEntryFromLinkedHashMapUnitTest.java @@ -0,0 +1,85 @@ +package com.baeldung.map.linkedhashmapfirstandlastentry; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class GetFirstAndLastEntryFromLinkedHashMapUnitTest { + private static final LinkedHashMap THE_MAP = new LinkedHashMap<>(); + + static { + THE_MAP.put("key one", "a1 b1 c1"); + THE_MAP.put("key two", "a2 b2 c2"); + THE_MAP.put("key three", "a3 b3 c3"); + THE_MAP.put("key four", "a4 b4 c4"); + } + + @Test + void whenIteratingEntrySet_thenGetExpectedResult() { + Entry firstEntry = THE_MAP.entrySet().iterator().next(); + assertEquals("key one", firstEntry.getKey()); + assertEquals("a1 b1 c1", firstEntry.getValue()); + + Entry lastEntry = null; + Iterator> it = THE_MAP.entrySet().iterator(); + while (it.hasNext()) { + lastEntry = it.next(); + } + + assertNotNull(lastEntry); + assertEquals("key four", lastEntry.getKey()); + assertEquals("a4 b4 c4", lastEntry.getValue()); + + } + + @Test + void whenConvertingEntriesToArray_thenGetExpectedResult() { + + Entry[] theArray = new Entry[THE_MAP.size()]; + THE_MAP.entrySet().toArray(theArray); + + Entry firstEntry = theArray[0]; + assertEquals("key one", firstEntry.getKey()); + assertEquals("a1 b1 c1", firstEntry.getValue()); + + Entry lastEntry = theArray[THE_MAP.size() - 1]; + assertEquals("key four", lastEntry.getKey()); + assertEquals("a4 b4 c4", lastEntry.getValue()); + } + + @Test + void whenUsingStreamAPI_thenGetExpectedResult() { + Entry firstEntry = THE_MAP.entrySet().stream().findFirst().get(); + assertEquals("key one", firstEntry.getKey()); + assertEquals("a1 b1 c1", firstEntry.getValue()); + + Entry lastEntry = THE_MAP.entrySet().stream().skip(THE_MAP.size() - 1).findFirst().get(); + + assertNotNull(lastEntry); + assertEquals("key four", lastEntry.getKey()); + assertEquals("a4 b4 c4", lastEntry.getValue()); + } + + @Test + void whenUsingReflection_thenGetExpectedResult() throws NoSuchFieldException, IllegalAccessException { + Field head = THE_MAP.getClass().getDeclaredField("head"); + head.setAccessible(true); + Entry firstEntry = (Entry) head.get(THE_MAP); + assertEquals("key one", firstEntry.getKey()); + assertEquals("a1 b1 c1", firstEntry.getValue()); + + Field tail = THE_MAP.getClass().getDeclaredField("tail"); + tail.setAccessible(true); + Entry lastEntry = (Entry) tail.get(THE_MAP); + assertEquals("key four", lastEntry.getKey()); + assertEquals("a4 b4 c4", lastEntry.getValue()); + } + + +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-basic-3/README.md b/core-java-modules/core-java-concurrency-basic-3/README.md index b9c66b279c..179a69495c 100644 --- a/core-java-modules/core-java-concurrency-basic-3/README.md +++ b/core-java-modules/core-java-concurrency-basic-3/README.md @@ -10,4 +10,5 @@ This module contains articles about basic Java concurrency. - [Returning a Value After Finishing Thread’s Job in Java](https://www.baeldung.com/java-return-value-after-thread-finish) - [CompletableFuture and ThreadPool in Java](https://www.baeldung.com/java-completablefuture-threadpool) - [CompletableFuture allOf().join() vs. CompletableFuture.join()](https://www.baeldung.com/java-completablefuture-allof-join) +- [Retry Logic with CompletableFuture](https://www.baeldung.com/java-completablefuture-retry-logic) - [[<-- Prev]](../core-java-concurrency-basic-2) diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/completablefuturelist/Application.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/completablefuturelist/Application.java new file mode 100644 index 0000000000..be5d9e8479 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/completablefuturelist/Application.java @@ -0,0 +1,100 @@ +package com.baeldung.concurrent.completablefuturelist; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; + +public class Application { + + ScheduledExecutorService asyncOperationEmulation; + + Application initialize() { + asyncOperationEmulation = Executors.newScheduledThreadPool(10); + return this; + } + + CompletableFuture asyncOperation(String operationId) { + CompletableFuture cf = new CompletableFuture<>(); + asyncOperationEmulation.submit(() -> { + // The following lines simulate an exception happening on the 567th operation + // if (operationId.endsWith("567")) { + // cf.completeExceptionally(new Exception("Error on operation " + operationId)); + // return; + // } + try { + Thread.sleep(100); + cf.complete(operationId); + } catch (InterruptedException e) { + System.err.println("Thread interrupted error"); + cf.completeExceptionally(e); + } + }); + return cf; + } + + void startNaive() { + List> futures = new ArrayList<>(); + for (int i = 1; i <= 1000; i++) { + String operationId = "Naive-Operation-" + i; + futures.add(asyncOperation(operationId)); + } + + CompletableFuture> aggregate = CompletableFuture.completedFuture(new ArrayList<>()); + for (CompletableFuture future : futures) { + aggregate = aggregate.thenCompose(list -> { + try { + list.add(future.get()); + return CompletableFuture.completedFuture(list); + } catch (Exception e) { + final CompletableFuture> excFuture = new CompletableFuture<>(); + excFuture.completeExceptionally(e); + return excFuture; + } + }); + } + + try { + final List results = aggregate.join(); + System.out.println("Printing first 10 results"); + for (int i = 0; i < 10; i++) { + System.out.println("Finished " + results.get(i)); + } + } finally { + close(); + } + } + + void start() { + List> futures = new ArrayList<>(); + for (int i = 1; i <= 1000; i++) { + String operationId = "Operation-" + i; + futures.add(asyncOperation(operationId)); + } + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture> listFuture = CompletableFuture.allOf(futuresArray).thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())); + try { + final List results = listFuture.join(); + System.out.println("Printing first 10 results"); + for (int i = 0; i < 10; i++) { + System.out.println("Finished " + results.get(i)); + } + } finally { + close(); + } + } + + void close() { + asyncOperationEmulation.shutdownNow(); + } + + public static void main(String[] args) { + new Application().initialize() + // Switch between .startNaive() and .start() to test both implementations + // .startNaive(); + .start(); + } + +} diff --git a/core-java-modules/core-java-concurrency-simple/README.md b/core-java-modules/core-java-concurrency-simple/README.md index 5cbfc67862..422d33e2c3 100644 --- a/core-java-modules/core-java-concurrency-simple/README.md +++ b/core-java-modules/core-java-concurrency-simple/README.md @@ -12,6 +12,7 @@ This module contains articles about Java Concurrency that are also part of an Eb - [Guide to the Volatile Keyword in Java](https://www.baeldung.com/java-volatile) - [A Guide to the Java ExecutorService](https://www.baeldung.com/java-executor-service-tutorial) - [Guide To CompletableFuture](https://www.baeldung.com/java-completablefuture) +- [How To Manage Timeout for CompletableFuture](https://www.baeldung.com/java-completablefuture-timeout) ### NOTE: diff --git a/core-java-modules/core-java-concurrency-simple/pom.xml b/core-java-modules/core-java-concurrency-simple/pom.xml index a16df4fc97..720ab5a0ed 100644 --- a/core-java-modules/core-java-concurrency-simple/pom.xml +++ b/core-java-modules/core-java-concurrency-simple/pom.xml @@ -12,6 +12,15 @@ 0.0.1-SNAPSHOT + + + org.wiremock + wiremock + 3.1.0 + test + + + core-java-concurrency-simple diff --git a/core-java-modules/core-java-concurrency-simple/src/test/java/com/baeldung/concurrent/completablefuture/CompletableFutureTimeoutUnitTest.java b/core-java-modules/core-java-concurrency-simple/src/test/java/com/baeldung/concurrent/completablefuture/CompletableFutureTimeoutUnitTest.java new file mode 100644 index 0000000000..d04865026b --- /dev/null +++ b/core-java-modules/core-java-concurrency-simple/src/test/java/com/baeldung/concurrent/completablefuture/CompletableFutureTimeoutUnitTest.java @@ -0,0 +1,182 @@ +package com.baeldung.concurrent.completablefuture; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.*; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class CompletableFutureTimeoutUnitTest { + private WireMockServer wireMockServer; + private ScheduledExecutorService executorService; + private static final int DEFAULT_TIMEOUT = 1000; //1 seconds + private static final String DEFAULT_PRODUCT = """ + { + "products": [ + { + "id": 2, + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 899, + "discountPercentage": 0.0, + "rating": 4.44, + "stock": 34, + "brand": "Apple", + "category": "smartphones" + }, + { + "id": 3, + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 0.0, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones" + } + ], + "total": 2 + }"""; + private static final String PRODUCT_OFFERS = """ + { + "products": [ + { + "id": 1, + "title": "iPhone 9", + "description": "An apple mobile which is nothing like apple", + "price": 549, + "discountPercentage": 12.96, + "rating": 4.69, + "stock": 94, + "brand": "Apple", + "category": "smartphones" + }, + { + "id": 2, + "title": "iPhone X", + "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", + "price": 899, + "discountPercentage": 17.94, + "rating": 4.44, + "stock": 34, + "brand": "Apple", + "category": "smartphones" + }, + { + "id": 3, + "title": "Samsung Universe 9", + "description": "Samsung's new variant which goes beyond Galaxy to the Universe", + "price": 1249, + "discountPercentage": 15.46, + "rating": 4.09, + "stock": 36, + "brand": "Samsung", + "category": "smartphones" + }, + { + "id": 4, + "title": "OPPOF19", + "description": "OPPO F19 is officially announced on April 2021.", + "price": 280, + "discountPercentage": 17.91, + "rating": 4.3, + "stock": 123, + "brand": "OPPO", + "category": "smartphones" + }, + { + "id": 5, + "title": "Huawei P30", + "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", + "price": 499, + "discountPercentage": 10.58, + "rating": 4.09, + "stock": 32, + "brand": "Huawei", + "category": "smartphones" + } + ], + "total": 5 + }"""; + + @BeforeAll + void setUp() { + wireMockServer = new WireMockServer(8080); + wireMockServer.start(); + WireMock.configureFor("localhost", 8080); + + stubFor(get(urlEqualTo("/api/dummy")) + .willReturn(aResponse() + .withFixedDelay(5000) // must be > DEFAULT_TIMEOUT for a timeout to occur. + .withBody(PRODUCT_OFFERS))); + + executorService = Executors.newScheduledThreadPool(1); + } + + @AfterAll + void tearDown() { + executorService.shutdown(); + wireMockServer.stop(); + } + + private CompletableFuture fetchProductData() { + return CompletableFuture.supplyAsync(() -> { + try { + URL url = new URL("http://localhost:8080/api/dummy"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + + return response.toString(); + } finally { + connection.disconnect(); + } + } catch (IOException e) { + return ""; + } + }); + } + + + @Test + void whenorTimeout_thenGetThrow() { + CompletableFuture productDataFuture = fetchProductData(); + productDataFuture.orTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + assertThrows(ExecutionException.class, productDataFuture::get); + } + + @Test + void whencompleteOnTimeout_thenReturnValue() throws ExecutionException, InterruptedException { + CompletableFuture productDataFuture = fetchProductData(); + productDataFuture.completeOnTimeout(DEFAULT_PRODUCT, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + assertEquals(DEFAULT_PRODUCT, productDataFuture.get()); + } + + @Test + void whencompleteExceptionally_thenGetThrow() { + CompletableFuture productDataFuture = fetchProductData(); + executorService.schedule(() -> productDataFuture + .completeExceptionally(new TimeoutException("Timeout occurred")), DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + assertThrows(ExecutionException.class, productDataFuture::get); + } +} diff --git a/core-java-modules/core-java-date-operations-3/src/test/java/com/baeldung/firstdaymonth/FirstDayOfMonthUnitTest.java b/core-java-modules/core-java-date-operations-3/src/test/java/com/baeldung/firstdaymonth/FirstDayOfMonthUnitTest.java new file mode 100644 index 0000000000..9edc3e4a8d --- /dev/null +++ b/core-java-modules/core-java-date-operations-3/src/test/java/com/baeldung/firstdaymonth/FirstDayOfMonthUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.firstdaymonth; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDate; +import java.time.YearMonth; +import java.time.temporal.TemporalAdjusters; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.junit.jupiter.api.Test; + +public class FirstDayOfMonthUnitTest { + + @Test + void givenMonth_whenUsingCalendar_thenReturnFirstDay() { + Date currentDate = new GregorianCalendar(2023, Calendar.NOVEMBER, 23).getTime(); + Date expectedDate = new GregorianCalendar(2023, Calendar.NOVEMBER, 1).getTime(); + + Calendar cal = Calendar.getInstance(); + cal.setTime(currentDate); + cal.set(Calendar.DAY_OF_MONTH, 1); + + assertEquals(expectedDate, cal.getTime()); + } + + @Test + void givenMonth_whenUsingLocalDate_thenReturnFirstDay() { + LocalDate currentDate = LocalDate.of(2023, 9, 6); + LocalDate expectedDate = LocalDate.of(2023, 9, 1); + + assertEquals(expectedDate, currentDate.withDayOfMonth(1)); + } + + @Test + void givenMonth_whenUsingTemporalAdjusters_thenReturnFirstDay() { + LocalDate currentDate = LocalDate.of(2023, 7, 19); + LocalDate expectedDate = LocalDate.of(2023, 7, 1); + + assertEquals(expectedDate, currentDate.with(TemporalAdjusters.firstDayOfMonth())); + } + + @Test + void givenMonth_whenUsingYearMonth_thenReturnFirstDay() { + YearMonth currentDate = YearMonth.of(2023, 4); + LocalDate expectedDate = LocalDate.of(2023, 4, 1); + + assertEquals(expectedDate, currentDate.atDay(1)); + } + + @Test + void givenMonth_whenUsingJodaTime_thenReturnFirstDay() { + org.joda.time.LocalDate currentDate = org.joda.time.LocalDate.parse("2023-5-10"); + org.joda.time.LocalDate expectedDate = org.joda.time.LocalDate.parse("2023-5-1"); + + assertEquals(expectedDate, currentDate.dayOfMonth() + .withMinimumValue()); + } + +} diff --git a/core-java-modules/core-java-documentation/src/main/java/com/baeldung/generictype/Pair.java b/core-java-modules/core-java-documentation/src/main/java/com/baeldung/generictype/Pair.java new file mode 100644 index 0000000000..f85adcfdab --- /dev/null +++ b/core-java-modules/core-java-documentation/src/main/java/com/baeldung/generictype/Pair.java @@ -0,0 +1,23 @@ +package com.baeldung.generictype; + +/** + * @param The type of the first value in the {@code Pair}. + * @param The type of the second value in the {@code Pair}. + */ + +public class Pair { + public T first; + public S second; + + /** + * Constructs a new Pair object with the specified values. + * + * @param a The first value. + * @param b The second value. + */ + + public Pair(T a, S b) { + first = a; + second = b; + } +} diff --git a/core-java-modules/core-java-io-5/pom.xml b/core-java-modules/core-java-io-5/pom.xml index 11116b071c..5c987a82e1 100644 --- a/core-java-modules/core-java-io-5/pom.xml +++ b/core-java-modules/core-java-io-5/pom.xml @@ -35,6 +35,21 @@ simplemagic ${simplemagic.version} + + commons-io + commons-io + ${commons-io.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + @@ -62,5 +77,6 @@ 0.1.5 6.2.1 1.17 + 1.37 \ No newline at end of file diff --git a/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipBenchmark.java b/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipBenchmark.java new file mode 100644 index 0000000000..30ec1522a2 --- /dev/null +++ b/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipBenchmark.java @@ -0,0 +1,112 @@ +package com.baeldung.zip; + +import java.io.*; +import java.util.concurrent.TimeUnit; +import java.util.*; +import java.util.zip.*; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 1, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 1) +public class ZipBenchmark { + + public static final int NUM_OF_FILES = 10; + public static final int DATA_SIZE = 204800; + + @State(Scope.Thread) + public static class SourceState { + + public File compressedFile; + + @Setup(Level.Trial) + public void setup() throws IOException { + ZipSampleFileStore sampleFileStore = new ZipSampleFileStore(NUM_OF_FILES, DATA_SIZE); + compressedFile = sampleFileStore.getFile(); + } + + @TearDown(Level.Trial) + public void cleanup() { + if (compressedFile.exists()) { + compressedFile.delete(); + } + } + } + + @Benchmark + public static void readAllEntriesByZipFile(SourceState sourceState, Blackhole blackhole) throws IOException { + + try (ZipFile zipFile = new ZipFile(sourceState.compressedFile)) { + Enumeration zipEntries = zipFile.entries(); + while (zipEntries.hasMoreElements()) { + ZipEntry zipEntry = zipEntries.nextElement(); + try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) { + blackhole.consume(ZipSampleFileStore.getString(inputStream)); + } + } + } + } + + @Benchmark + public static void readAllEntriesByZipInputStream(SourceState sourceState, Blackhole blackhole) throws IOException { + + try ( + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceState.compressedFile)); + ZipInputStream zipInputStream = new ZipInputStream(bis) + ) { + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + blackhole.consume(ZipSampleFileStore.getString(zipInputStream)); + } + } + } + + @Benchmark + public static void readLastEntryByZipFile(SourceState sourceState, Blackhole blackhole) throws IOException { + + try (ZipFile zipFile = new ZipFile(sourceState.compressedFile)) { + ZipEntry zipEntry = zipFile.getEntry(getLastEntryName()); + try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) { + blackhole.consume(ZipSampleFileStore.getString(inputStream)); + } + } + } + + @Benchmark + public static void readLastEntryByZipInputStream(SourceState sourceState, Blackhole blackhole) throws IOException { + + try ( + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceState.compressedFile)); + ZipInputStream zipInputStream = new ZipInputStream(bis) + ) { + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + if (Objects.equals(entry.getName(), getLastEntryName())){ + blackhole.consume(ZipSampleFileStore.getString(zipInputStream)); + } + } + } + } + + private static String getLastEntryName() { + return String.format(ZipSampleFileStore.ENTRY_NAME_PATTERN, NUM_OF_FILES); + } + + public static void main(String[] args) throws Exception { + Options options = new OptionsBuilder() + .include(ZipBenchmark.class.getSimpleName()).threads(1) + .shouldFailOnError(true) + .shouldDoGC(true) + .jvmArgs("-server").build(); + new Runner(options).run(); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipSampleFileStore.java b/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipSampleFileStore.java new file mode 100644 index 0000000000..389f082ff1 --- /dev/null +++ b/core-java-modules/core-java-io-5/src/main/java/com/baeldung/zip/ZipSampleFileStore.java @@ -0,0 +1,70 @@ +package com.baeldung.zip; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.io.IOUtils; + +public class ZipSampleFileStore { + + public static final String ENTRY_NAME_PATTERN = "str-data-%s.txt"; + private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; + + private final File file; + private final List dataList; + + public ZipSampleFileStore(int numOfFiles, int fileSize) throws IOException { + + dataList = new ArrayList<>(numOfFiles); + file = File.createTempFile("zip-sample", ""); + + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) { + + for (int idx=0; idx<=numOfFiles; idx++) { + + byte[] data = createRandomStringInByte(fileSize); + dataList.add(new String(data, DEFAULT_ENCODING)); + + ZipEntry entry = new ZipEntry(String.format(ENTRY_NAME_PATTERN, idx)); + zos.putNextEntry(entry); + zos.write(data); + zos.closeEntry(); + } + } + } + + public static byte[] createRandomStringInByte(int size) { + Random random = new Random(); + byte[] data = new byte[size]; + for (int n = 0; n < data.length; n++) { + char randomChar; + int choice = random.nextInt(2); // 0 for uppercase, 1 for lowercase + if (choice == 0) { + randomChar = (char) ('A' + random.nextInt(26)); // 'A' to 'Z' + } else { + randomChar = (char) ('a' + random.nextInt(26)); // 'a' to 'z' + } + data[n] = (byte) randomChar; + } + return data; + } + + public File getFile() { + return file; + } + + public List getDataList() { + return dataList; + } + + public static String getString(InputStream inputStream) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + IOUtils.copy(inputStream, byteArrayOutputStream); + return byteArrayOutputStream.toString(DEFAULT_ENCODING); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-io-5/src/test/java/com/baeldung/zip/ZipUnitTest.java b/core-java-modules/core-java-io-5/src/test/java/com/baeldung/zip/ZipUnitTest.java new file mode 100644 index 0000000000..1d4d76dc41 --- /dev/null +++ b/core-java-modules/core-java-io-5/src/test/java/com/baeldung/zip/ZipUnitTest.java @@ -0,0 +1,83 @@ +package com.baeldung.zip; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +import org.junit.*; + +public class ZipUnitTest { + + private static File compressedFile; + private static List dataList = new ArrayList<>(); + + @BeforeClass + public static void prepareData() throws IOException { + ZipSampleFileStore sampleFileStore = new ZipSampleFileStore(ZipBenchmark.NUM_OF_FILES, ZipBenchmark.DATA_SIZE); + compressedFile = sampleFileStore.getFile(); + dataList = sampleFileStore.getDataList(); + } + + @Test + public void whenCreateZipFile_thenCompressedSizeShouldBeLessThanOriginal() throws IOException { + byte[] data = ZipSampleFileStore.createRandomStringInByte(10240); + File file = File.createTempFile("zip-temp", ""); + + try ( + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); + ZipOutputStream zos = new ZipOutputStream(bos) + ) { + ZipEntry zipEntry = new ZipEntry("zip-entry.txt"); + zos.putNextEntry(zipEntry); + zos.write(data); + zos.closeEntry(); + + assertThat(file.length()).isLessThan(data.length); + } + finally { + file.delete(); + } + } + + @Test + public void whenReadAllEntriesViaZipFile_thenDataIsEqualtoTheSource() throws IOException { + + try (ZipFile zipFile = new ZipFile(compressedFile)) { + Enumeration entries = zipFile.entries(); + List entryList = Collections.list(entries); + + for (int idx=0; idx items = objectName.getAll(); + while(items.hasMoreElements()) { + System.out.println(items.nextElement()); + } + + objectName.add("New Name"); + + assertEquals("env", objectName.get(1)); + assertEquals("New Name", objectName.get(objectName.size() - 1)); + } + + @Test + void givenContext_whenLookupByName_thenReturnsValidObject() throws Exception { + DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource"); + + assertNotNull(ds); + assertNotNull(ds.getConnection()); + } + + @Test + void givenSubContext_whenLookupByName_thenReturnsValidObject() throws Exception { + Context subContext = (Context) context.lookup("java:comp/env"); + DataSource ds = (DataSource) subContext.lookup("jdbc/datasource"); + + assertNotNull(ds); + assertNotNull(ds.getConnection()); + } + + @AfterAll + static void tearDown() throws Exception { + context.close(); + } + +} diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/genericnumberscomparator/GenericNumbersComparatorUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/genericnumberscomparator/GenericNumbersComparatorUnitTest.java new file mode 100644 index 0000000000..37b971b5c5 --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/genericnumberscomparator/GenericNumbersComparatorUnitTest.java @@ -0,0 +1,122 @@ +package com.baeldung.genericnumberscomparator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; + +import org.junit.jupiter.api.Test; + +class GenericNumbersComparatorUnitTest { + + public int compareDouble(Number num1, Number num2) { + return Double.compare(num1.doubleValue(), num2.doubleValue()); + } + + @Test + void givenNumbers_whenUseCompareDouble_thenWillExecuteComparison() { + assertEquals(0, compareDouble(5, 5.0)); + } + + public int compareTo(Integer int1, Integer int2) { + return int1.compareTo(int2); + } + + @Test + void givenNumbers_whenUseCompareTo_thenWillExecuteComparison() { + assertEquals(-1, compareTo(5, 7)); + } + + Map, BiFunction> comparisonMap = new HashMap<>(); + + public int compareUsingMap(Number num1, Number num2) { + comparisonMap.put(Integer.class, (a, b) -> ((Integer) num1).compareTo((Integer) num2)); + + return comparisonMap.get(num1.getClass()) + .apply(num1, num2); + } + + @Test + void givenNumbers_whenUseCompareUsingMap_thenWillExecuteComparison() { + assertEquals(-1, compareUsingMap(5, 7)); + } + + public interface NumberComparator { + int compare(Number num1, Number num2); + } + + @Test + void givenNumbers_whenUseProxy_thenWillExecuteComparison() { + NumberComparator proxy = (NumberComparator) Proxy.newProxyInstance(NumberComparator.class.getClassLoader(), new Class[] { NumberComparator.class }, + (p, method, args) -> Double.compare(((Number) args[0]).doubleValue(), ((Number) args[1]).doubleValue())); + assertEquals(0, proxy.compare(5, 5.0)); + } + + public int compareUsingReflection(Number num1, Number num2) throws Exception { + Method method = num1.getClass() + .getMethod("compareTo", num1.getClass()); + return (int) method.invoke(num1, num2); + } + + @Test + void givenNumbers_whenUseCompareUsingReflection_thenWillExecuteComparison() throws Exception { + assertEquals(-1, compareUsingReflection(5, 7)); + } + + interface NumberComparatorFactory { + Comparator getComparator(); + } + + class IntegerComparatorFactory implements NumberComparatorFactory { + @Override + public Comparator getComparator() { + return (num1, num2) -> ((Integer) num1).compareTo((Integer) num2); + } + } + + @Test + void givenNumbers_whenUseComparatorFactory_thenWillExecuteComparison() { + NumberComparatorFactory factory = new IntegerComparatorFactory(); + Comparator comparator = factory.getComparator(); + assertEquals(-1, comparator.compare(5, 7)); + } + + Function toDouble = Number::doubleValue; + BiPredicate isEqual = (num1, num2) -> toDouble.apply(num1) + .equals(toDouble.apply(num2)); + + @Test + void givenNumbers_whenUseIsEqual_thenWillExecuteComparison() { + assertEquals(true, isEqual.test(5, 5.0)); + } + + private Number someNumber = 5; + private Number anotherNumber = 5.0; + + Optional optNum1 = Optional.ofNullable(someNumber); + Optional optNum2 = Optional.ofNullable(anotherNumber); + int comparisonResult = optNum1.flatMap(n1 -> optNum2.map(n2 -> Double.compare(n1.doubleValue(), n2.doubleValue()))) + .orElse(0); + + @Test + void givenNumbers_whenUseComparisonResult_thenWillExecuteComparison() { + assertEquals(0, comparisonResult); + } + + private boolean someCondition = true; + Function dynamicFunction = someCondition ? Number::doubleValue : Number::intValue; + Comparator dynamicComparator = (num1, num2) -> ((Comparable) dynamicFunction.apply(num1)).compareTo(dynamicFunction.apply(num2)); + + @Test + void givenNumbers_whenUseDynamicComparator_thenWillExecuteComparison() { + assertEquals(0, dynamicComparator.compare(5, 5.0)); + } + +} diff --git a/core-java-modules/core-java-records/src/main/java/com/baeldung/optionalsasparameterrecords/Product.java b/core-java-modules/core-java-records/src/main/java/com/baeldung/optionalsasparameterrecords/Product.java new file mode 100644 index 0000000000..8ede001ee1 --- /dev/null +++ b/core-java-modules/core-java-records/src/main/java/com/baeldung/optionalsasparameterrecords/Product.java @@ -0,0 +1,6 @@ +package com.baeldung.optionalsasparameterrecords; + +import java.util.Optional; + +public record Product(String name, double price, Optional description) { +} diff --git a/core-java-modules/core-java-records/src/test/java/com/baeldung/optionalsasparameterrecords/OptionalAsRecordParameterUnitTest.java b/core-java-modules/core-java-records/src/test/java/com/baeldung/optionalsasparameterrecords/OptionalAsRecordParameterUnitTest.java new file mode 100644 index 0000000000..554fb2eac3 --- /dev/null +++ b/core-java-modules/core-java-records/src/test/java/com/baeldung/optionalsasparameterrecords/OptionalAsRecordParameterUnitTest.java @@ -0,0 +1,17 @@ +package com.baeldung.optionalsasparameterrecords; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +public class OptionalAsRecordParameterUnitTest { + + @Test + public void givenRecordCreationWithOptional_thenCreateItProperly() { + var emptyDescriptionProduct = new Product("television", 1699.99, Optional.empty()); + Assertions.assertEquals("television", emptyDescriptionProduct.name()); + Assertions.assertEquals(1699.99, emptyDescriptionProduct.price()); + Assertions.assertNull(emptyDescriptionProduct.description().orElse(null)); + } +} diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/alphanumeric/AlphanumericPerformanceBenchmark.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/alphanumeric/AlphanumericPerformanceBenchmark.java index 4b2ecc4c4d..0525153ed9 100644 --- a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/alphanumeric/AlphanumericPerformanceBenchmark.java +++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/alphanumeric/AlphanumericPerformanceBenchmark.java @@ -85,7 +85,7 @@ public class AlphanumericPerformanceBenchmark { public boolean isAlphanumeric(final int codePoint) { return (codePoint >= 65 && codePoint <= 90) || - (codePoint >= 97 && codePoint <= 172) || + (codePoint >= 97 && codePoint <= 122) || (codePoint >= 48 && codePoint <= 57); } } diff --git a/core-java-modules/core-java-streams-5/README.md b/core-java-modules/core-java-streams-5/README.md index e7c58a0eb2..629d52f0a9 100644 --- a/core-java-modules/core-java-streams-5/README.md +++ b/core-java-modules/core-java-streams-5/README.md @@ -4,3 +4,4 @@ - [Aggregate Runtime Exceptions in Java Streams](https://www.baeldung.com/java-streams-aggregate-exceptions) - [Streams vs. Loops in Java](https://www.baeldung.com/java-streams-vs-loops) - [Partition a Stream in Java](https://www.baeldung.com/java-partition-stream) +- [Taking Every N-th Element from Finite and Infinite Streams in Java](https://www.baeldung.com/java-nth-element-finite-infinite-streams) diff --git a/core-java-modules/core-java-string-algorithms-3/README.md b/core-java-modules/core-java-string-algorithms-3/README.md index 3ef07129ed..abcac63ea4 100644 --- a/core-java-modules/core-java-string-algorithms-3/README.md +++ b/core-java-modules/core-java-string-algorithms-3/README.md @@ -13,3 +13,4 @@ This module contains articles about string-related algorithms. - [Find the Most Frequent Characters in a String](https://www.baeldung.com/java-string-find-most-frequent-characters) - [Checking If a String Is a Repeated Substring](https://www.baeldung.com/java-repeated-substring) - [Check if Letter Is Emoji With Java](https://www.baeldung.com/java-check-letter-emoji) +- [Wrapping a String After a Number of Characters Word-Wise](https://www.baeldung.com/java-wrap-string-number-characters-word-wise) diff --git a/core-java-modules/core-java-string-conversions-3/README.md b/core-java-modules/core-java-string-conversions-3/README.md index 4ec4771eea..ec169f71d2 100644 --- a/core-java-modules/core-java-string-conversions-3/README.md +++ b/core-java-modules/core-java-string-conversions-3/README.md @@ -3,3 +3,5 @@ - [Convert String to Int Using Encapsulation](https://www.baeldung.com/java-encapsulation-convert-string-to-int) - [HashMap with Multiple Values for the Same Key](https://www.baeldung.com/java-hashmap-multiple-values-per-key) - [Split Java String Into Key-Value Pairs](https://www.baeldung.com/java-split-string-map) +- [How to Center Text Output in Java](https://www.baeldung.com/java-center-text-output) +- [How to Convert an Object to String](https://www.baeldung.com/java-object-string-representation) diff --git a/core-java-modules/core-java-string-operations-2/pom.xml b/core-java-modules/core-java-string-operations-2/pom.xml index c6debc4f71..902e8f09b4 100644 --- a/core-java-modules/core-java-string-operations-2/pom.xml +++ b/core-java-modules/core-java-string-operations-2/pom.xml @@ -54,6 +54,11 @@ commons-codec ${commons-codec.version} + + org.springframework + spring-core + ${spring-core.version} + @@ -95,6 +100,7 @@ 3.0.0 2.2.6 1.14 + 5.3.0 \ No newline at end of file diff --git a/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/emptystrings/EmptyStringsUnitTest.java b/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/emptystrings/EmptyStringsUnitTest.java index d772c38341..9652e0e770 100644 --- a/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/emptystrings/EmptyStringsUnitTest.java +++ b/core-java-modules/core-java-string-operations-2/src/test/java/com/baeldung/emptystrings/EmptyStringsUnitTest.java @@ -3,6 +3,7 @@ package com.baeldung.emptystrings; import com.google.common.base.Strings; import org.apache.commons.lang3.StringUtils; import org.junit.Test; +import org.springframework.util.ObjectUtils; import javax.validation.ConstraintViolation; import javax.validation.Validation; @@ -109,6 +110,24 @@ public class EmptyStringsUnitTest { assertFalse(Strings.isNullOrEmpty(blankString)); } + /* + * Spring Core ObjectUtils + */ + @Test + public void givenSomeEmptyString_thenObjectUtilsIsEmptyReturnsTrue() { + assertTrue(ObjectUtils.isEmpty(emptyString)); + } + + @Test + public void givenSomeNonEmptyString_thenObjectUtilsIsEmptyReturnsFalse() { + assertFalse(ObjectUtils.isEmpty(nonEmptyString)); + } + + @Test + public void givenSomeBlankString_thenObjectUtilsIsEmptyReturnsFalse() { + assertFalse(ObjectUtils.isEmpty(blankString)); + } + /* * Bean Validation */ diff --git a/core-java-modules/core-java-string-operations-6/README.md b/core-java-modules/core-java-string-operations-6/README.md index b4b78d1ad7..9d92552dd1 100644 --- a/core-java-modules/core-java-string-operations-6/README.md +++ b/core-java-modules/core-java-string-operations-6/README.md @@ -11,4 +11,4 @@ - [Check if a String Has All Unique Characters in Java](https://www.baeldung.com/java-check-string-all-unique-chars) - [Performance Comparison Between Different Java String Concatenation Methods](https://www.baeldung.com/java-string-concatenation-methods) - [Replacing Single Quote with \’ in Java String](https://www.baeldung.com/java-replacing-single-quote-string) - +- [Check if a String Contains a Number Value in Java](https://www.baeldung.com/java-string-number-presence) diff --git a/core-java-modules/core-java-string-operations-7/README.md b/core-java-modules/core-java-string-operations-7/README.md index 7020369f02..6c4fab384b 100644 --- a/core-java-modules/core-java-string-operations-7/README.md +++ b/core-java-modules/core-java-string-operations-7/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: -[How to Center Text Output in Java](https://www.baeldung.com/java-center-text-output) +- [How to Center Text Output in Java](https://www.baeldung.com/java-center-text-output) +- [Capitalize the First Letter of Each Word in a String](https://www.baeldung.com/java-string-initial-capital-letter-every-word) diff --git a/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/CharsetUsageExample.java b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/CharsetUsageExample.java new file mode 100644 index 0000000000..5ef8783ac8 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/CharsetUsageExample.java @@ -0,0 +1,34 @@ +package com.baeldung.mutablestrings; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +public class CharsetUsageExample { + + public ByteBuffer encodeString(String inputString) { + Charset charset = Charset.forName("UTF-8"); + CharsetEncoder encoder = charset.newEncoder(); + + CharBuffer charBuffer = CharBuffer.wrap(inputString); + ByteBuffer byteBuffer = ByteBuffer.allocate(50); + + encoder.encode(charBuffer, byteBuffer, true); // true indicates the end of input + byteBuffer.flip(); + return byteBuffer; + } + + public String decodeString(ByteBuffer byteBuffer) { + Charset charset = Charset.forName("UTF-8"); + CharsetDecoder decoder = charset.newDecoder(); + CharBuffer decodedCharBuffer = CharBuffer.allocate(50); + decoder.decode(byteBuffer, decodedCharBuffer, true); + decodedCharBuffer.flip(); + + return decodedCharBuffer.toString(); + } +} diff --git a/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStringUsingCharset.java b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStringUsingCharset.java new file mode 100644 index 0000000000..3e4285c9d0 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStringUsingCharset.java @@ -0,0 +1,65 @@ +package com.baeldung.mutablestrings; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.util.concurrent.atomic.AtomicReference; + +public class MutableStringUsingCharset { + + private final AtomicReference cbRef = new AtomicReference<>(); + private final Charset myCharset = new Charset("mycharset", null) { + @Override + public boolean contains(Charset cs) { + return false; + } + + @Override + public CharsetDecoder newDecoder() { + return new CharsetDecoder(this, 1.0f, 1.0f) { + @Override + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + cbRef.set(out); + while (in.remaining() > 0) { + out.append((char) in.get()); + } + return CoderResult.UNDERFLOW; + } + }; + } + + @Override + public CharsetEncoder newEncoder() { + CharsetEncoder cd = new CharsetEncoder(this, 1.0f, 1.0f) { + @Override + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + while (in.hasRemaining()) { + if (!out.hasRemaining()) { + return CoderResult.OVERFLOW; + } + char currentChar = in.get(); + if (currentChar > 127) { + return CoderResult.unmappableForLength(1); + } + out.put((byte) currentChar); + } + return CoderResult.UNDERFLOW; + } + }; + return cd; + } + }; + + public String createModifiableString(String s) { + return new String(s.getBytes(), myCharset); + } + + public void modifyString() { + CharBuffer cb = cbRef.get(); + cb.position(0); + cb.put("xyz"); + } +} diff --git a/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStrings.java b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStrings.java new file mode 100644 index 0000000000..99994498b9 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/mutablestrings/MutableStrings.java @@ -0,0 +1,24 @@ +package com.baeldung.mutablestrings; + +import java.lang.reflect.Field; +import java.nio.charset.Charset; + +import com.google.errorprone.annotations.DoNotCall; + +public class MutableStrings { + + /** + * This involves using Reflection to change String fields and it is not encouraged to use this in programs. + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + @DoNotCall + public void mutableUsingReflection() throws NoSuchFieldException, IllegalAccessException { + String myString = "Hello World"; + String otherString = new String("Hello World"); + Field f = String.class.getDeclaredField("value"); + f.setAccessible(true); + f.set(myString, "Hi World".toCharArray()); + System.out.println(otherString); + } +} diff --git a/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/unicode/UnicodeLetterChecker.java b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/unicode/UnicodeLetterChecker.java new file mode 100644 index 0000000000..8ac8671586 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/main/java/com/baeldung/unicode/UnicodeLetterChecker.java @@ -0,0 +1,30 @@ +package com.baeldung.unicode; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; + +public class UnicodeLetterChecker { + public boolean characterClassCheck(String input) { + for (char c : input.toCharArray()) { + if (!Character.isLetter(c)) { + return false; + } + } + return true; + } + + public boolean regexCheck(String input) { + Pattern pattern = Pattern.compile("^\\p{L}+$"); + Matcher matcher = pattern.matcher(input); + return matcher.matches(); + } + + public boolean isAlphaCheck(String input) { + return StringUtils.isAlpha(input); + } + + public boolean StreamsCheck(String input) { + return input.codePoints().allMatch(Character::isLetter); + } +} diff --git a/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/CharsetUsageUnitTest.java b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/CharsetUsageUnitTest.java new file mode 100644 index 0000000000..f009b0242f --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/CharsetUsageUnitTest.java @@ -0,0 +1,17 @@ +package com.baeldung.mutablestring; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.baeldung.mutablestrings.CharsetUsageExample; + +public class CharsetUsageUnitTest { + + @Test + public void givenCharset_whenStringIsEncodedAndDecoded_thenGivesCorrectResult() { + CharsetUsageExample ch = new CharsetUsageExample(); + String inputString = "hello दुनिया"; + String result = ch.decodeString(ch.encodeString(inputString)); + Assertions.assertEquals(inputString, result); + } +} diff --git a/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/MutableStringUsingCharsetUnitTest.java b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/MutableStringUsingCharsetUnitTest.java new file mode 100644 index 0000000000..81a92038d5 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/mutablestring/MutableStringUsingCharsetUnitTest.java @@ -0,0 +1,24 @@ +package com.baeldung.mutablestring; + +import org.junit.Assert; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import com.baeldung.mutablestrings.MutableStringUsingCharset; + +public class MutableStringUsingCharsetUnitTest { + @Test + @Disabled + /** + * This test is disabled as it works well for Java 8 and below + */ + public void givenCustomCharSet_whenStringUpdated_StringGetsMutated() throws Exception { + MutableStringUsingCharset ms = new MutableStringUsingCharset(); + String s = ms.createModifiableString("Hello"); + Assert.assertEquals("Hello", s); + ms.modifyString(); + Assert.assertEquals("something", s); + } + +} + diff --git a/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/unicode/UnicodeLetterCheckerUnitTest.java b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/unicode/UnicodeLetterCheckerUnitTest.java new file mode 100644 index 0000000000..a7ed9e5db9 --- /dev/null +++ b/core-java-modules/core-java-string-operations-7/src/test/java/com/baeldung/unicode/UnicodeLetterCheckerUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.unicode; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class UnicodeLetterCheckerUnitTest { + @Test + public void givenString_whenUsingIsLetter_thenReturnTrue() { + UnicodeLetterChecker checker = new UnicodeLetterChecker(); + + boolean isUnicodeLetter = checker.characterClassCheck("HelloWorld"); + assertTrue(isUnicodeLetter); + } + + @Test + public void givenString_whenUsingRegex_thenReturnTrue() { + UnicodeLetterChecker checker = new UnicodeLetterChecker(); + + boolean isUnicodeLetter = checker.regexCheck("HelloWorld"); + assertTrue(isUnicodeLetter); + } + + @Test + public void givenString_whenUsingIsAlpha_thenReturnTrue() { + UnicodeLetterChecker checker = new UnicodeLetterChecker(); + + boolean isUnicodeLetter = checker.isAlphaCheck("HelloWorld"); + assertTrue(isUnicodeLetter); + } + + @Test + public void givenString_whenUsingStreams_thenReturnTrue() { + UnicodeLetterChecker checker = new UnicodeLetterChecker(); + + boolean isUnicodeLetter = checker.StreamsCheck("HelloWorld"); + assertTrue(isUnicodeLetter); + } +} diff --git a/core-java-modules/core-java-strings/README.md b/core-java-modules/core-java-strings/README.md index 03e980e5a5..dbbfcc79b6 100644 --- a/core-java-modules/core-java-strings/README.md +++ b/core-java-modules/core-java-strings/README.md @@ -14,3 +14,4 @@ Listed here there are only those articles that does not fit into other core-java - [Java String Interview Questions and Answers](https://www.baeldung.com/java-string-interview-questions) - [Java Multi-line String](https://www.baeldung.com/java-multiline-string) - [Reuse StringBuilder for Efficiency](https://www.baeldung.com/java-reuse-stringbuilder-for-efficiency) +- [How to Iterate Over the String Characters in Java](https://www.baeldung.com/java-iterate-string-characters) diff --git a/core-java-modules/core-java-strings/src/main/java/com/baeldung/stringIterator/StringIterator.java b/core-java-modules/core-java-strings/src/main/java/com/baeldung/stringIterator/StringIterator.java new file mode 100644 index 0000000000..0e46c7eedd --- /dev/null +++ b/core-java-modules/core-java-strings/src/main/java/com/baeldung/stringIterator/StringIterator.java @@ -0,0 +1,42 @@ +package com.baeldung.stringIterator; + +import java.text.*; +import java.util.*; + +public class StringIterator { + + public static String javaCharArray(String str){ + StringBuilder result = new StringBuilder(); + for (char c : str.toCharArray()) { + result.append(c); + } + return result.toString(); + } + + public static String javaforLoop(String str) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + result.append(c); + } + return result.toString(); + } + + public static String java8forEach(String str){ + StringBuilder result = new StringBuilder(); + str.chars().forEach(name -> { + result.append((char) name); + }); + return result.toString(); + } + + public static String javaCharacterIterator(String str){ + StringBuilder result = new StringBuilder(); + CharacterIterator it = new StringCharacterIterator(str); + while (it.current() != CharacterIterator.DONE) { + result.append(it.current()); + it.next(); + } + return result.toString(); + } +} diff --git a/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/Dummy.java b/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/Dummy.java new file mode 100644 index 0000000000..27ebf8331f --- /dev/null +++ b/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/Dummy.java @@ -0,0 +1,14 @@ +package com.baeldung.passstringbyreference; + +public class Dummy { + + String dummyString; + + public String getDummyString() { + return dummyString; + } + + public void setDummyString(String dummyString) { + this.dummyString = dummyString; + } +} diff --git a/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/PassStringUnitTest.java b/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/PassStringUnitTest.java new file mode 100644 index 0000000000..e1b284a94e --- /dev/null +++ b/core-java-modules/core-java-strings/src/test/java/com/baeldung/passstringbyreference/PassStringUnitTest.java @@ -0,0 +1,85 @@ +package com.baeldung.passstringbyreference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class PassStringUnitTest { + + @Test + void givenAString_whenPassedToVoidMethod_thenStringIsNotModified() { + String s = "hello"; + concatStringWithNoReturn(s); + assertEquals("hello", s); + } + + void concatStringWithNoReturn(String input) { + input += " world"; + assertEquals("hello world", input); + } + + @Test + void givenAString_whenPassedToMethodAndReturnNewString_thenStringIsModified() { + String s = "hello"; + assertEquals("hello world", concatStringWithReturn(s)); + } + + String concatStringWithReturn(String input) { + return input + " world"; + } + + @Test + void givenAString_whenPassStringBuilderToVoidMethod_thenConcatNewStringOk() { + StringBuilder builder = new StringBuilder("hello"); + concatWithStringBuilder(builder); + + assertEquals("hello world", builder.toString()); + } + + void concatWithStringBuilder(StringBuilder input) { + input.append(" world"); + } + + @Test + void givenAString_whenPassStringBufferToVoidMethod_thenConcatNewStringOk() { + StringBuffer builder = new StringBuffer("hello"); + concatWithStringBuffer(builder); + + assertEquals("hello world", builder.toString()); + } + + void concatWithStringBuffer(StringBuffer input) { + input.append(" world"); + } + + @Test + void givenObjectWithStringField_whenSetDifferentValue_thenObjectIsModified() { + Dummy dummy = new Dummy(); + assertNull(dummy.getDummyString()); + modifyStringValueInInputObject(dummy, "hello world"); + assertEquals("hello world", dummy.getDummyString()); + } + + void modifyStringValueInInputObject(Dummy dummy, String dummyString) { + dummy.setDummyString(dummyString); + } + + @Test + void givenObjectWithStringField_whenSetDifferentValueWithStringBuilder_thenSetStringInNewObject() { + assertEquals("hello world", getDummy("hello", "world").getDummyString()); + } + + Dummy getDummy(String hello, String world) { + StringBuilder builder = new StringBuilder(); + + builder.append(hello) + .append(" ") + .append(world); + + Dummy dummy = new Dummy(); + dummy.setDummyString(builder.toString()); + + return dummy; + } +} diff --git a/core-java-modules/core-java-strings/src/test/java/com/baeldung/stringIterator/StringIteratorTest.java b/core-java-modules/core-java-strings/src/test/java/com/baeldung/stringIterator/StringIteratorTest.java new file mode 100644 index 0000000000..585d65d4be --- /dev/null +++ b/core-java-modules/core-java-strings/src/test/java/com/baeldung/stringIterator/StringIteratorTest.java @@ -0,0 +1,39 @@ +package com.baeldung.stringIterator; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class StringIteratorTest { + + @Test + public void whenUseCharArrayMethod_thenIterate() { + String input = "Hello, Baeldung!"; + String expectedOutput = "Hello, Baeldung!"; + String result = StringIterator.javaCharArray(input); + assertEquals(expectedOutput, result); + } + + @Test + public void whenUseJavaForLoop_thenIterate() { + String input = "Hello, Baeldung!"; + String expectedOutput = "Hello, Baeldung!"; + String result = StringIterator.javaForLoop(input); + assertEquals(expectedOutput, result); + } + + @Test + public void whenUseForEachMethod_thenIterate() { + String input = "Hello, Baeldung!"; + String expectedOutput = "Hello, Baeldung!"; + String result = StringIterator.java8ForEach(input); + assertEquals(expectedOutput, result); + } + + @Test + public void whenUseCharacterIterator_thenIterate() { + String input = "Hello, Baeldung!"; + String expectedOutput = "Hello, Baeldung!"; + String result = StringIterator.javaCharacterIterator(input); + assertEquals(expectedOutput, result); + } +} diff --git a/data-structures/src/main/java/com/baeldung/avltree/AVLTree.java b/data-structures/src/main/java/com/baeldung/avltree/AVLTree.java index ca0cfce7b4..117a35e028 100644 --- a/data-structures/src/main/java/com/baeldung/avltree/AVLTree.java +++ b/data-structures/src/main/java/com/baeldung/avltree/AVLTree.java @@ -42,17 +42,17 @@ public class AVLTree { return root == null ? -1 : root.height; } - private Node insert(Node node, int key) { - if (node == null) { + private Node insert(Node root, int key) { + if (root == null) { return new Node(key); - } else if (node.key > key) { - node.left = insert(node.left, key); - } else if (node.key < key) { - node.right = insert(node.right, key); + } else if (root.key > key) { + root.left = insert(root.left, key); + } else if (root.key < key) { + root.right = insert(root.right, key); } else { throw new RuntimeException("duplicate Key!"); } - return rebalance(node); + return rebalance(root); } private Node delete(Node node, int key) { diff --git a/libraries-io/README.md b/libraries-io/README.md index 6cfe978d91..a9ca5df3d6 100644 --- a/libraries-io/README.md +++ b/libraries-io/README.md @@ -3,4 +3,4 @@ - [Transferring a File Through SFTP in Java](https://www.baeldung.com/java-file-sftp) - [How to Create Password-Protected Zip Files and Unzip Them in Java](https://www.baeldung.com/java-password-protected-zip-unzip) - +- [How to Create CSV File from POJO with Custom Column Headers and Positions](https://www.baeldung.com/java-create-csv-pojo-customize-columns) diff --git a/libraries-llms/pom.xml b/libraries-llms/pom.xml new file mode 100644 index 0000000000..3d5ed6830e --- /dev/null +++ b/libraries-llms/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + libraries-llms + libraries-llms + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + + dev.langchain4j + langchain4j + ${langchain4j.version} + + + dev.langchain4j + langchain4j-embeddings + ${langchain4j.version} + + + dev.langchain4j + langchain4j-open-ai + ${langchain4j.version} + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${langchain4j.version} + + + + + 0.23.0 + + + \ No newline at end of file diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/ChainWithDocumentLiveTest.java b/libraries-llms/src/test/java/com/baeldung/langchain/ChainWithDocumentLiveTest.java new file mode 100644 index 0000000000..39746ce810 --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/ChainWithDocumentLiveTest.java @@ -0,0 +1,65 @@ +package com.baeldung.langchain; + +import static dev.langchain4j.data.document.FileSystemDocumentLoader.loadDocument; +import static java.time.Duration.ofSeconds; +import static org.junit.Assert.assertNotNull; + +import java.nio.file.Paths; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.langchain4j.chain.ConversationalRetrievalChain; +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.splitter.DocumentSplitters; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.input.PromptTemplate; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.retriever.EmbeddingStoreRetriever; +import dev.langchain4j.store.embedding.EmbeddingStore; +import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; +import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; + +public class ChainWithDocumentLiveTest { + + private static final Logger logger = LoggerFactory.getLogger(ChainWithDocumentLiveTest.class); + + @Test + public void givenChainWithDocument_whenPrompted_thenValidResponse() { + EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); + + EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(500, 0)) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); + + Document document = loadDocument(Paths.get("src/test/resources/example-files/simpson's_adventures.txt")); + ingestor.ingest(document); + + ChatLanguageModel chatModel = OpenAiChatModel.builder() + .apiKey(Constants.OPENAI_API_KEY) + .timeout(ofSeconds(60)) + .build(); + + ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() + .chatLanguageModel(chatModel) + .retriever(EmbeddingStoreRetriever.from(embeddingStore, embeddingModel)) + .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) + .promptTemplate(PromptTemplate.from("Answer the following question to the best of your ability: {{question}}\n\nBase your answer on the following information:\n{{information}}")) + .build(); + + String answer = chain.execute("Who is Simpson?"); + + logger.info(answer); + assertNotNull(answer); + } + +} diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithDocumentLiveTest.java b/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithDocumentLiveTest.java new file mode 100644 index 0000000000..41540f4888 --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithDocumentLiveTest.java @@ -0,0 +1,81 @@ +package com.baeldung.langchain; + +import static dev.langchain4j.data.document.FileSystemDocumentLoader.loadDocument; +import static dev.langchain4j.model.openai.OpenAiModelName.GPT_3_5_TURBO; +import static java.time.Duration.ofSeconds; +import static java.util.stream.Collectors.joining; +import static org.junit.Assert.assertNotNull; + +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.DocumentSplitter; +import dev.langchain4j.data.document.splitter.DocumentSplitters; +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.input.Prompt; +import dev.langchain4j.model.input.PromptTemplate; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.model.openai.OpenAiTokenizer; +import dev.langchain4j.store.embedding.EmbeddingMatch; +import dev.langchain4j.store.embedding.EmbeddingStore; +import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; + +public class ChatWithDocumentLiveTest { + + private static final Logger logger = LoggerFactory.getLogger(ChatWithDocumentLiveTest.class); + + @Test + public void givenDocument_whenPrompted_thenValidResponse() { + Document document = loadDocument(Paths.get("src/test/resources/example-files/simpson's_adventures.txt")); + DocumentSplitter splitter = DocumentSplitters.recursive(100, 0, new OpenAiTokenizer(GPT_3_5_TURBO)); + List segments = splitter.split(document); + + EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); + List embeddings = embeddingModel.embedAll(segments) + .content(); + EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + embeddingStore.addAll(embeddings, segments); + + String question = "Who is Simpson?"; + Embedding questionEmbedding = embeddingModel.embed(question) + .content(); + int maxResults = 3; + double minScore = 0.7; + List> relevantEmbeddings = embeddingStore.findRelevant(questionEmbedding, maxResults, minScore); + + PromptTemplate promptTemplate = PromptTemplate.from("Answer the following question to the best of your ability:\n" + "\n" + "Question:\n" + "{{question}}\n" + "\n" + "Base your answer on the following information:\n" + "{{information}}"); + + String information = relevantEmbeddings.stream() + .map(match -> match.embedded() + .text()) + .collect(joining("\n\n")); + + Map variables = new HashMap<>(); + variables.put("question", question); + variables.put("information", information); + + Prompt prompt = promptTemplate.apply(variables); + ChatLanguageModel chatModel = OpenAiChatModel.builder() + .apiKey(Constants.OPENAI_API_KEY) + .timeout(ofSeconds(60)) + .build(); + AiMessage aiMessage = chatModel.generate(prompt.toUserMessage()) + .content(); + + logger.info(aiMessage.text()); + assertNotNull(aiMessage.text()); + } + +} diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithMemoryLiveTest.java b/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithMemoryLiveTest.java new file mode 100644 index 0000000000..3d265d3f3f --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/ChatWithMemoryLiveTest.java @@ -0,0 +1,43 @@ +package com.baeldung.langchain; + +import static dev.langchain4j.data.message.UserMessage.userMessage; +import static dev.langchain4j.model.openai.OpenAiModelName.GPT_3_5_TURBO; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.memory.ChatMemory; +import dev.langchain4j.memory.chat.TokenWindowChatMemory; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.model.openai.OpenAiTokenizer; + +public class ChatWithMemoryLiveTest { + + private static final Logger logger = LoggerFactory.getLogger(ChatWithMemoryLiveTest.class); + + @Test + public void givenMemory_whenPrompted_thenValidResponse() { + ChatLanguageModel model = OpenAiChatModel.withApiKey(Constants.OPENAI_API_KEY); + ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(300, new OpenAiTokenizer(GPT_3_5_TURBO)); + + chatMemory.add(userMessage("Hello, my name is Kumar")); + AiMessage answer = model.generate(chatMemory.messages()) + .content(); + logger.info(answer.text()); + chatMemory.add(answer); + assertNotNull(answer.text()); + + chatMemory.add(userMessage("What is my name?")); + AiMessage answerWithName = model.generate(chatMemory.messages()) + .content(); + logger.info(answerWithName.text()); + chatMemory.add(answerWithName); + assertThat(answerWithName.text()).contains("Kumar"); + } + +} diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/Constants.java b/libraries-llms/src/test/java/com/baeldung/langchain/Constants.java new file mode 100644 index 0000000000..380415bf9c --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/Constants.java @@ -0,0 +1,12 @@ +package com.baeldung.langchain; + +public class Constants { + + /** + * A limited access key for access to OpenAI language models can be generated by first + * registering for free at (https://platform.openai.com/signup) and then by navigating + * to "Create new secret key" page at (https://platform.openai.com/account/api-keys). + */ + public static String OPENAI_API_KEY = ""; + +} diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/PromptTemplatesLiveTest.java b/libraries-llms/src/test/java/com/baeldung/langchain/PromptTemplatesLiveTest.java new file mode 100644 index 0000000000..cd2ea642ed --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/PromptTemplatesLiveTest.java @@ -0,0 +1,41 @@ +package com.baeldung.langchain; + +import static dev.langchain4j.model.openai.OpenAiModelName.GPT_3_5_TURBO; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.input.Prompt; +import dev.langchain4j.model.input.PromptTemplate; +import dev.langchain4j.model.openai.OpenAiChatModel; + +public class PromptTemplatesLiveTest { + + private static final Logger logger = LoggerFactory.getLogger(PromptTemplatesLiveTest.class); + + @Test + public void givenPromptTemplate_whenSuppliedInput_thenValidResponse() { + PromptTemplate promptTemplate = PromptTemplate.from("Tell me a {{adjective}} joke about {{content}}.."); + Map variables = new HashMap<>(); + variables.put("adjective", "funny"); + variables.put("content", "humans"); + Prompt prompt = promptTemplate.apply(variables); + + ChatLanguageModel model = OpenAiChatModel.builder() + .apiKey(Constants.OPENAI_API_KEY) + .modelName(GPT_3_5_TURBO) + .temperature(0.3) + .build(); + + String response = model.generate(prompt.text()); + logger.info(response); + assertNotNull(response); + } + +} diff --git a/libraries-llms/src/test/java/com/baeldung/langchain/ServiceWithToolsLiveTest.java b/libraries-llms/src/test/java/com/baeldung/langchain/ServiceWithToolsLiveTest.java new file mode 100644 index 0000000000..7ebfcba4df --- /dev/null +++ b/libraries-llms/src/test/java/com/baeldung/langchain/ServiceWithToolsLiveTest.java @@ -0,0 +1,49 @@ +package com.baeldung.langchain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.langchain4j.agent.tool.Tool; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.service.AiServices; + +public class ServiceWithToolsLiveTest { + + private static final Logger logger = LoggerFactory.getLogger(ServiceWithToolsLiveTest.class); + + static class Calculator { + @Tool("Calculates the length of a string") + int stringLength(String s) { + return s.length(); + } + + @Tool("Calculates the sum of two numbers") + int add(int a, int b) { + return a + b; + } + } + + interface Assistant { + String chat(String userMessage); + } + + @Test + public void givenServiceWithTools_whenPrompted_thenValidResponse() { + Assistant assistant = AiServices.builder(Assistant.class) + .chatLanguageModel(OpenAiChatModel.withApiKey(Constants.OPENAI_API_KEY)) + .tools(new Calculator()) + .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) + .build(); + + String question = "What is the sum of the numbers of letters in the words \"language\" and \"model\"?"; + String answer = assistant.chat(question); + + logger.info(answer); + assertThat(answer).contains("13"); + } + +} diff --git a/libraries-llms/src/test/resources/example-files/simpson's_adventures.txt b/libraries-llms/src/test/resources/example-files/simpson's_adventures.txt new file mode 100644 index 0000000000..ae6de80be0 --- /dev/null +++ b/libraries-llms/src/test/resources/example-files/simpson's_adventures.txt @@ -0,0 +1,28 @@ +Once upon a time in the town of VeggieVille, there lived a cheerful carrot named Simpson. +Simpson was a radiant carrot, always beaming with joy and positivity. +His vibrant orange skin and lush green top were a sight to behold, but it was his infectious laughter and warm personality that really set him apart. + +Simpson had a diverse group of friends, each a vegetable with their own unique characteristics. +There was Bella the blushing beetroot, always ready with a riddle or two; Timmy the timid tomato, a gentle soul with a heart of gold; and Percy the prankster potato, whose jokes always brought a smile to everyone's faces. +Despite their differences, they shared a close bond, their friendship as robust as their natural goodness. + +Their lives were filled with delightful adventures, from playing hide-and-seek amidst the leafy lettuce to swimming in the dewy droplets that pooled on the cabbage leaves. +Their favorite place, though, was the sunlit corner of the vegetable patch, where they would bask in the warmth of the sun, share stories, and have hearty laughs. + +One day, a bunch of pesky caterpillars invaded VeggieVille. +The vegetables were terrified, fearing they would be nibbled to nothingness. +But Simpson, with his usual sunny disposition, had an idea. +He proposed they host a grand feast for the caterpillars, with the juiciest leaves from the outskirts of the town. +Simpson's optimism was contagious, and his friends eagerly joined in to prepare the feast. + +When the caterpillars arrived, they were pleasantly surprised. +They enjoyed the feast and were so impressed with the vegetables' hospitality that they promised not to trouble VeggieVille again. +In return, they agreed to help pollinate the flowers, contributing to a more lush and vibrant VeggieVille. + +Simpson's idea had saved the day, but he humbly attributed the success to their teamwork and friendship. +They celebrated their victory with a grand party, filled with laughter, dance, and merry games. +That night, under the twinkling stars, they made a pact to always stand by each other, come what may. + +From then on, the story of the happy carrot and his friends spread far and wide, a tale of friendship, unity, and positivity. +Simpson, Bella, Timmy, and Percy continued to live their joyful lives, their laughter echoing through VeggieVille. +And so, the tale of the happy carrot and his friends serves as a reminder that no matter the challenge, with optimism, teamwork, and a bit of creativity, anything is possible. \ No newline at end of file diff --git a/persistence-modules/hibernate-ogm/src/test/java/com/baeldung/hibernate/ogm/EditorUnitTest.java b/persistence-modules/hibernate-ogm/src/test/java/com/baeldung/hibernate/ogm/EditorUnitTest.java index d7fd49d917..3903e272de 100644 --- a/persistence-modules/hibernate-ogm/src/test/java/com/baeldung/hibernate/ogm/EditorUnitTest.java +++ b/persistence-modules/hibernate-ogm/src/test/java/com/baeldung/hibernate/ogm/EditorUnitTest.java @@ -22,6 +22,7 @@ public class EditorUnitTest { loadAndVerifyTestData(entityManagerFactory, editor); } */ + @Test public void givenNeo4j_WhenEntitiesCreated_thenCanBeRetrieved() throws Exception { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("ogm-neo4j"); diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml index 53e408c3a5..eb4b212771 100644 --- a/persistence-modules/pom.xml +++ b/persistence-modules/pom.xml @@ -106,6 +106,7 @@ spring-jpa spring-jpa-2 spring-jdbc + spring-jdbc-2 spring-jooq spring-mybatis spring-persistence-simple diff --git a/persistence-modules/spring-jdbc-2/pom.xml b/persistence-modules/spring-jdbc-2/pom.xml new file mode 100644 index 0000000000..ce79c1c615 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + org.example + spring-jdbc-2 + 1.0-SNAPSHOT + spring-jdbc-2 + Demo project for Spring Jdbc + + + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + 3.2.0-SNAPSHOT + 5.10.0 + UTF-8 + + + \ No newline at end of file diff --git a/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/JdbcClientDemoApplication.java b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/JdbcClientDemoApplication.java new file mode 100644 index 0000000000..d10124fdef --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/JdbcClientDemoApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.jdbcclient; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = "com.baledung.jdbcclient") +public class JdbcClientDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(JdbcClientDemoApplication.class, args); + } +} diff --git a/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/dao/StudentDao.java b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/dao/StudentDao.java new file mode 100644 index 0000000000..ba5bfcee11 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/dao/StudentDao.java @@ -0,0 +1,95 @@ +package com.baeldung.jdbcclient.dao; + +import com.baeldung.jdbcclient.model.Student; +import com.baeldung.jdbcclient.model.StudentResultExtractor; +import com.baeldung.jdbcclient.model.StudentRowMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.RowCountCallbackHandler; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; + +import java.sql.Types; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Repository +public class StudentDao { + + private static final Logger logger = LoggerFactory.getLogger(StudentDao.class); + @Autowired + private JdbcClient jdbcClient; + + public Integer insertWithSetParamWithNamedParamAndSqlType(Student student) { + String sql = "INSERT INTO student (student_name, age, grade, gender, state)" + + "VALUES (:name, :age, :grade, :gender, :state)"; + Integer noOfrowsAffected = this.jdbcClient.sql(sql) + .param("name", student.getStudentName(), Types.VARCHAR) + .param("age", student.getAge(), Types.INTEGER) + .param("grade", student.getGrade(), Types.INTEGER) + .param("gender", student.getStudentGender(), Types.VARCHAR) + .param("state", student.getState(), Types.VARCHAR) + .update(); + logger.info("No. of rows affected: " + noOfrowsAffected); + return noOfrowsAffected; + } + + public List getStudentsOfGradeStateAndGenderWithPositionalParams(int grade, String state, String gender) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = ? and state = ? and gender = ?"; + return jdbcClient.sql(sql) + .param(grade) + .param(state) + .param(gender) + .query(new StudentRowMapper()).list(); + } + + public List getStudentsOfGradeStateAndGenderWithParamIndex(int grade, String state, String gender) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = ? and state = ? and gender = ?"; + return jdbcClient.sql(sql) + .param(1,grade) + .param(2, state) + .param(3, gender) + .query(new StudentResultExtractor()); + } + + public Student getStudentsOfGradeStateAndGenderWithParamsInVarargs(int grade, String state, String gender) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = ? and state = ? and gender = ? limit 1"; + return jdbcClient.sql(sql) + .params(grade, state, gender) + .query(new StudentRowMapper()).single(); + } + + public Optional getStudentsOfGradeStateAndGenderWithParamsInList(List params) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = ? and state = ? and gender = ? limit 1"; + return jdbcClient.sql(sql) + .params(params) + .query(new StudentRowMapper()).optional(); + } + + + public int getCountOfStudentsOfGradeStateAndGenderWithNamedParam(int grade, String state, String gender) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = :grade and state = :state and gender = :gender"; + RowCountCallbackHandler countCallbackHandler = new RowCountCallbackHandler(); + jdbcClient.sql(sql) + .param("grade", grade) + .param("state", state) + .param("gender", gender) + .query(countCallbackHandler); + return countCallbackHandler.getRowCount(); + } + + public List getStudentsOfGradeStateAndGenderWithParamMap(Map paramMap) { + String sql = "select student_id, student_name, age, grade, gender, state from student" + + " where grade = :grade and state = :state and gender = :gender"; + return jdbcClient.sql(sql) + .params(paramMap) + .query(new StudentRowMapper()).list(); + } +} diff --git a/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/Student.java b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/Student.java new file mode 100644 index 0000000000..3de8a6e1f9 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/Student.java @@ -0,0 +1,60 @@ +package com.baeldung.jdbcclient.model; + +public class Student { + private Integer studentId; + private String studentName; + private String studentGender; + private Integer age; + private Integer grade; + + public Integer getStudentId() { + return studentId; + } + + public void setStudentId(Integer studentId) { + this.studentId = studentId; + } + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + + public String getStudentGender() { + return studentGender; + } + + public void setStudentGender(String studentGender) { + this.studentGender = studentGender; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Integer getGrade() { + return grade; + } + + public void setGrade(Integer grade) { + this.grade = grade; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + private String state; + +} diff --git a/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentResultExtractor.java b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentResultExtractor.java new file mode 100644 index 0000000000..29cb60cee9 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentResultExtractor.java @@ -0,0 +1,26 @@ +package com.baeldung.jdbcclient.model; + +import org.springframework.jdbc.core.ResultSetExtractor; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class StudentResultExtractor implements ResultSetExtractor> { + @Override + public List extractData(ResultSet rs) throws SQLException { + List students = new ArrayList(); + while(rs.next()) { + Student student = new Student(); + student.setStudentId(rs.getInt("student_id")); + student.setStudentName(rs.getString("student_name")); + student.setAge(rs.getInt("age")); + student.setStudentGender(rs.getString("gender")); + student.setGrade(rs.getInt("grade")); + student.setState(rs.getString("state")); + students.add(student); + } + return students; + } +} diff --git a/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentRowMapper.java b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentRowMapper.java new file mode 100644 index 0000000000..c387a6c485 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/java/com/baeldung/jdbcclient/model/StudentRowMapper.java @@ -0,0 +1,20 @@ +package com.baeldung.jdbcclient.model; + +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class StudentRowMapper implements RowMapper { + @Override + public Student mapRow(ResultSet rs, int rowNum) throws SQLException { + Student student = new Student(); + student.setStudentId(rs.getInt("student_id")); + student.setStudentName(rs.getString("student_name")); + student.setAge(rs.getInt("age")); + student.setStudentGender(rs.getString("gender")); + student.setGrade(rs.getInt("grade")); + student.setState(rs.getString("state")); + return student; + } +} diff --git a/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/application.properties b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/application.properties new file mode 100644 index 0000000000..04c963ebf4 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/application.properties @@ -0,0 +1,5 @@ +# DataSource Configuration +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=user +spring.datasource.password= # Leave this empty \ No newline at end of file diff --git a/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/drop_student.sql b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/drop_student.sql new file mode 100644 index 0000000000..954545a862 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/drop_student.sql @@ -0,0 +1 @@ +DROP TABLE student; \ No newline at end of file diff --git a/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/student.sql b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/student.sql new file mode 100644 index 0000000000..0e137f5445 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/main/resources/jdbcclient/student.sql @@ -0,0 +1,98 @@ + +CREATE TABLE student ( + student_id INT AUTO_INCREMENT PRIMARY KEY, + student_name VARCHAR(255) NOT NULL, + age INT, + grade INT NOT NULL, + gender VARCHAR(10) NOT NULL, + state VARCHAR(100) NOT NULL +); +-- Student 1 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('John Smith', 18, 3, 'Male', 'California'); + +-- Student 2 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Emily Johnson', 17, 2, 'Female', 'New York'); + +-- Student 3 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Michael Davis', 4, 1, 'Male', 'Texas'); + +-- Student 4 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Sophia Martinez', 2, 1, 'Female', 'Florida'); + +-- Student 5 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('William Brown', 5, 5, 'Male', 'California'); + +-- Student 6 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Olivia Garcia', 4, 2, 'Female', 'Texas'); + +-- Student 7 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Ethan Rodriguez', 3, 1, 'Male', 'New York'); + +-- Student 8 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Ava Hernandez', 2, 1, 'Female', 'Florida'); + +-- Student 9 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('James Wilson', 5, 4, 'Male', 'Texas'); + +-- Student 10 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Emma Miller', 3, 1, 'Female', 'California'); + +-- Student 11 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Benjamin Brown', 4, 1, 'Male', 'New York'); + +-- Student 12 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Mia Smith', 2, 1, 'Female', 'Florida'); + +-- Student 13 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Daniel Johnson', 5, 4, 'Male', 'California'); + +-- Student 14 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Ava Davis', 4, 2, 'Female', 'Texas'); + +-- Student 15 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Matthew Martinez', 3, 1, 'Male', 'New York'); + +-- Student 16 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Sophia Taylor', 2, 1, 'Female', 'Florida'); + +-- Student 17 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Alexander White', 5, 4, 'Male', 'California'); + +-- Student 18 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Olivia Johnson', 4, 2, 'Female', 'Texas'); + +-- Student 19 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Christopher Lee', 3, 1, 'Male', 'New York'); + +-- Student 20 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Emma Wilson', 2, 1, 'Female', 'Florida'); + +-- Student 21 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Elijah Smith', 5, 3, 'Male', 'Texas'); + +-- Student 22 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Isabella Davis', 4, 2, 'Female', 'California'); + +-- Student 23 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Liam Johnson', 3, 1, 'Male', 'New York'); + +-- Student 24 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Olivia Garcia', 2, 1, 'Female', 'Florida'); + +-- Student 25 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Noah Rodriguez', 5, 3, 'Male', 'Texas'); + +-- Student 26 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Sophia Hernandez', 4, 2, 'Female', 'California'); + +-- Student 27 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Mason Smith', 3, 1, 'Male', 'New York'); + +-- Student 28 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Ava Taylor', 2, 1, 'Female', 'Florida'); + +-- Student 29 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('William Brown', 5, 5, 'Male', 'Texas'); + +-- Student 30 +INSERT INTO student (student_name, age, grade, gender, state) VALUES ('Olivia Martinez', 4, 4, 'Female', 'California'); diff --git a/persistence-modules/spring-jdbc-2/src/test/java/com/baeldung/jdbcclient/JdbcClientUnitTest.java b/persistence-modules/spring-jdbc-2/src/test/java/com/baeldung/jdbcclient/JdbcClientUnitTest.java new file mode 100644 index 0000000000..be30efcb9c --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/test/java/com/baeldung/jdbcclient/JdbcClientUnitTest.java @@ -0,0 +1,104 @@ +package com.baeldung.jdbcclient; + +import com.baeldung.jdbcclient.dao.StudentDao; +import com.baeldung.jdbcclient.model.Student; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +@Sql(value = "/jdbcclient/student.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = "/jdbcclient/drop_student.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) +@SpringBootTest(classes = JdbcClientDemoApplication.class) +@TestPropertySource(locations = {"classpath:jdbcclient/application.properties"}) + +public class JdbcClientUnitTest { + private static final Logger logger = LoggerFactory.getLogger(JdbcClientUnitTest.class); + + @Autowired + private StudentDao studentDao; + + @Test + void givenJdbcClient_whenInsertWithNamedParamAndSqlType_thenSuccess() { + logger.info("testing invoked successfully"); + Student student = getSampleStudent("Johny Dep", 8, 4, "Male", "New York"); + assertEquals(1, studentDao.insertWithSetParamWithNamedParamAndSqlType(student)); + } + + @Test + void givenJdbcClient_whenQueryWithPositionalParams_thenSuccess() { + logger.info("testing invoked successfully"); + List students = studentDao.getStudentsOfGradeStateAndGenderWithPositionalParams( + 1, "New York", "Male"); + logger.info("number of students fetched " + students.size()); + assertEquals(6, students.size()); + } + + @Test + void givenJdbcClient_whenQueryWithParamsInVarargs_thenSuccess() { + logger.info("testing invoked successfully"); + Student student = studentDao.getStudentsOfGradeStateAndGenderWithParamsInVarargs( + 1, "New York", "Male"); + assertNotNull(student); + } + + @Test + void givenJdbcClient_whenQueryWithParamsInList_thenSuccess() { + logger.info("testing invoked successfully"); + List params = List.of(1, "New York", "Male"); + Optional optional = studentDao.getStudentsOfGradeStateAndGenderWithParamsInList(params); + if(optional.isPresent()) { + assertNotNull(optional.get()); + } else { + assertThrows(NoSuchElementException.class, () -> optional.get()); + } + } + + @Test + void givenJdbcClient_whenQueryWithParamsIndex_thenSuccess() { + logger.info("testing invoked successfully"); + List students = studentDao.getStudentsOfGradeStateAndGenderWithParamIndex( + 1, "New York", "Male"); + assertEquals(6, students.size()); + } + @Test + void givenJdbcClient_whenQueryWithNamedParam_thenSuccess() { + logger.info("testing invoked successfully"); + Integer count = studentDao.getCountOfStudentsOfGradeStateAndGenderWithNamedParam( + 1, "New York", "Male"); + logger.info("number of students fetched " + count); + assertEquals(6, count); + } + @Test + void givenJdbcClient_whenQueryWithParamMap_thenSuccess() { + logger.info("testing invoked successfully"); + Map paramMap = Map.of( + "grade", 1, + "gender", "Male", + "state", "New York" + ); + List students = studentDao.getStudentsOfGradeStateAndGenderWithParamMap(paramMap); + logger.info("number of students fetched " + students.size()); + assertEquals(6, students.size()); + } + + private Student getSampleStudent(String name, int age, int grade, String gender, String state) { + Student student = new Student(); + student.setStudentName(name); + student.setStudentGender(gender); + student.setAge(age); + student.setGrade(grade); + student.setState(state); + return student; + } +} diff --git a/persistence-modules/spring-jdbc-2/src/test/resources/logback-test.xml b/persistence-modules/spring-jdbc-2/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..8d4771e308 --- /dev/null +++ b/persistence-modules/spring-jdbc-2/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + + [%d{ISO8601}]-[%thread] %-5level %logger - %msg%n + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 07b9d6d842..ab1fffa8b9 100644 --- a/pom.xml +++ b/pom.xml @@ -349,7 +349,6 @@ - @@ -362,7 +361,7 @@ muleesb web-modules/java-lite persistence-modules/deltaspike - persistence-modules/hibernate-ogm + persistence-modules/hibernate-ogm persistence-modules/spring-data-cassandra-reactive java-nashorn jeromq @@ -523,7 +522,6 @@ - @@ -534,7 +532,7 @@ muleesb web-modules/java-lite persistence-modules/deltaspike - persistence-modules/hibernate-ogm + persistence-modules/hibernate-ogm persistence-modules/spring-data-cassandra-reactive java-nashorn jeromq @@ -932,6 +930,7 @@ spring-di-4 spring-kafka-2 + libraries-llms @@ -1214,6 +1213,7 @@ spring-di-4 spring-kafka-2 + libraries-llms diff --git a/reactive-systems/docker-compose.yml b/reactive-systems/docker-compose.yml new file mode 100644 index 0000000000..51e15f6a19 --- /dev/null +++ b/reactive-systems/docker-compose.yml @@ -0,0 +1,57 @@ +version: '3' +services: + frontend: + build: ./frontend + ports: + - "80:80" + zookeeper: + image: confluentinc/cp-zookeeper:latest + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + ports: + - 22181:2181 + kafka: + image: confluentinc/cp-kafka:latest + container_name: kafka-broker + depends_on: + - zookeeper + ports: + - 29092:29092 + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,PLAINTEXT_HOST://localhost:29092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + mongodb: + container_name: mongo-db + image: mongo:6.0 + volumes: + - ~/mongo:/data/db + ports: + - "27017:27017" + healthcheck: + test: exit 0 + order-service: + build: ./order-service + ports: + - "8080:8080" + depends_on: + mongodb: + condition: service_healthy + inventory-service: + build: ./inventory-service + ports: + - "8081:8081" + depends_on: + mongodb: + condition: service_healthy + shipping-service: + build: ./shipping-service + ports: + - "8082:8082" + depends_on: + mongodb: + condition: service_healthy \ No newline at end of file diff --git a/reactive-systems/inventory-service/Dockerfile b/reactive-systems/inventory-service/Dockerfile index d37887cf34..d0900decfa 100644 --- a/reactive-systems/inventory-service/Dockerfile +++ b/reactive-systems/inventory-service/Dockerfile @@ -1,3 +1,3 @@ FROM openjdk:8-jdk-alpine -COPY target/inventory-service-async-0.0.1-SNAPSHOT.jar app.jar +COPY target/inventory-service-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","-Dspring.profiles.active=docker","/app.jar"] \ No newline at end of file diff --git a/reactive-systems/order-service/Dockerfile b/reactive-systems/order-service/Dockerfile index 516e088a05..e48c19c2b1 100644 --- a/reactive-systems/order-service/Dockerfile +++ b/reactive-systems/order-service/Dockerfile @@ -1,3 +1,3 @@ FROM openjdk:8-jdk-alpine -COPY target/order-service-async-0.0.1-SNAPSHOT.jar app.jar +COPY target/order-service-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","-Dspring.profiles.active=docker","/app.jar"] \ No newline at end of file diff --git a/reactive-systems/shipping-service/Dockerfile b/reactive-systems/shipping-service/Dockerfile index 4906d1d9a8..ff57bb953d 100644 --- a/reactive-systems/shipping-service/Dockerfile +++ b/reactive-systems/shipping-service/Dockerfile @@ -1,3 +1,3 @@ FROM openjdk:8-jdk-alpine -COPY target/shipping-service-async-0.0.1-SNAPSHOT.jar app.jar +COPY target/shipping-service-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","-Dspring.profiles.active=docker","/app.jar"] \ No newline at end of file diff --git a/reactor-core/pom.xml b/reactor-core/pom.xml index e27a1a2845..dd7533fe73 100644 --- a/reactor-core/pom.xml +++ b/reactor-core/pom.xml @@ -26,6 +26,11 @@ ${reactor.version} test + + io.projectreactor.addons + reactor-extra + ${reactor.version} + org.projectlombok lombok @@ -35,7 +40,7 @@ - 3.4.17 + 3.5.1 \ No newline at end of file diff --git a/reactor-core/src/test/java/com/baeldung/reactor/math/MathFluxOperationsUnitTest.java b/reactor-core/src/test/java/com/baeldung/reactor/math/MathFluxOperationsUnitTest.java new file mode 100644 index 0000000000..2ff8005acd --- /dev/null +++ b/reactor-core/src/test/java/com/baeldung/reactor/math/MathFluxOperationsUnitTest.java @@ -0,0 +1,48 @@ +package com.baeldung.math; + +import org.junit.Test; + +import reactor.math.MathFlux; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +public class MathFluxOperationsUnitTest { + + @Test + public void givenFluxOfNumbers_whenCalculatingSum_thenExpectCorrectResult() { + Flux numbers = Flux.just(1, 2, 3, 4, 5); + Mono sumMono = MathFlux.sumInt(numbers); + StepVerifier.create(sumMono) + .expectNext(15) + .verifyComplete(); + } + + @Test + public void givenFluxOfNumbers_whenCalculatingAverage_thenExpectCorrectResult() { + Flux numbers = Flux.just(1, 2, 3, 4, 5); + Mono averageMono = MathFlux.averageDouble(numbers); + StepVerifier.create(averageMono) + .expectNext(3.0) + .verifyComplete(); + } + + @Test + public void givenFluxOfNumbers_whenFindingMinElement_thenExpectCorrectResult() { + Flux numbers = Flux.just(3, 1, 5, 2, 4); + Mono minMono = MathFlux.min(numbers); + StepVerifier.create(minMono) + .expectNext(1) + .verifyComplete(); + } + + @Test + public void givenFluxOfNumbers_whenFindingMaxElement_thenExpectCorrectResult() { + Flux numbers = Flux.just(3, 1, 5, 2, 4); + Mono maxMono = MathFlux.max(numbers); + StepVerifier.create(maxMono) + .expectNext(5) + .verifyComplete(); + } + +} diff --git a/spring-activiti/src/test/java/com/baeldung/activitiwithspring/ActivitiWithSpringApplicationIntegrationTest.java b/spring-activiti/src/test/java/com/baeldung/activitiwithspring/ActivitiWithSpringApplicationIntegrationTest.java deleted file mode 100644 index d289693a73..0000000000 --- a/spring-activiti/src/test/java/com/baeldung/activitiwithspring/ActivitiWithSpringApplicationIntegrationTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.baeldung.activitiwithspring; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = ActivitiWithSpringApplication.class) -@AutoConfigureTestDatabase -public class ActivitiWithSpringApplicationIntegrationTest { - - @Test - public void contextLoads() { - } - -} diff --git a/spring-boot-modules/spring-boot-3-2/README.md b/spring-boot-modules/spring-boot-3-2/README.md new file mode 100644 index 0000000000..c176de2310 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-2/README.md @@ -0,0 +1,2 @@ +## Relevant Articles +- [Spring Boot 3.1’s ConnectionDetails Abstraction](https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction) diff --git a/spring-boot-modules/spring-boot-3-2/pom.xml b/spring-boot-modules/spring-boot-3-2/pom.xml index 9e73d8ea32..276659c609 100644 --- a/spring-boot-modules/spring-boot-3-2/pom.xml +++ b/spring-boot-modules/spring-boot-3-2/pom.xml @@ -15,6 +15,14 @@ ../../parent-boot-3 + + + repository.spring.release + Spring GA Repository + https://repo.spring.io/milestone + + + org.springframework.boot @@ -94,6 +102,17 @@ org.springframework.boot spring-boot-starter-test + 3.2.0-M2 + + + org.junit.jupiter + junit-jupiter + 5.10.0 + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 org.postgresql @@ -175,6 +194,18 @@ + + + + org.springframework.boot + spring-boot-dependencies + 3.2.0-M2 + import + pom + + + + @@ -244,9 +275,6 @@ org.apache.maven.plugins maven-compiler-plugin - - --enable-preview - diff --git a/spring-boot-modules/spring-boot-3-2/src/test/java/com/baeldung/conditionalonthreading/ConditionalOnThreadingUnitTest.java b/spring-boot-modules/spring-boot-3-2/src/test/java/com/baeldung/conditionalonthreading/ConditionalOnThreadingUnitTest.java new file mode 100644 index 0000000000..ed65769954 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-2/src/test/java/com/baeldung/conditionalonthreading/ConditionalOnThreadingUnitTest.java @@ -0,0 +1,70 @@ +package com.baeldung.conditionalonthreading; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading; +import org.springframework.boot.autoconfigure.thread.Threading; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +public class ConditionalOnThreadingUnitTest { + + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner() + .withUserConfiguration(CurrentConfig.class); + + @Configuration + static class CurrentConfig { + + @Bean + @ConditionalOnThreading(Threading.PLATFORM) + ThreadingType platformBean() { + return ThreadingType.PLATFORM; + } + + @Bean + @ConditionalOnThreading(Threading.VIRTUAL) + ThreadingType virtualBean() { + return ThreadingType.VIRTUAL; + } + } + + enum ThreadingType { + PLATFORM, VIRTUAL + } + + @Test + @EnabledForJreRange(max = JRE.JAVA_20) + public void whenJava20AndVirtualThreadsEnabled_thenThreadingIsPlatform() { + applicationContextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run(context -> { + Assertions.assertThat(context.getBean(ThreadingType.class)).isEqualTo(ThreadingType.PLATFORM); + }); + } + + @Test + @EnabledForJreRange(max = JRE.JAVA_20) + public void whenJava20AndVirtualThreadsDisabled_thenThreadingIsPlatform() { + applicationContextRunner.withPropertyValues("spring.threads.virtual.enabled=false").run(context -> { + Assertions.assertThat(context.getBean(ThreadingType.class)).isEqualTo(ThreadingType.PLATFORM); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + public void whenJava21AndVirtualThreadsEnabled_thenThreadingIsVirtual() { + applicationContextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run(context -> { + Assertions.assertThat(context.getBean(ThreadingType.class)).isEqualTo(ThreadingType.VIRTUAL); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + public void whenJava21AndVirtualThreadsDisabled_thenThreadingIsPlatform() { + applicationContextRunner.withPropertyValues("spring.threads.virtual.enabled=false").run(context -> { + Assertions.assertThat(context.getBean(ThreadingType.class)).isEqualTo(ThreadingType.PLATFORM); + }); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/README.md b/spring-boot-modules/spring-boot-3/README.md index 8791aaae1f..ded5db30e4 100644 --- a/spring-boot-modules/spring-boot-3/README.md +++ b/spring-boot-modules/spring-boot-3/README.md @@ -8,3 +8,4 @@ - [HTTP Interface in Spring 6](https://www.baeldung.com/spring-6-http-interface) - [Working with Virtual Threads in Spring 6](https://www.baeldung.com/spring-6-virtual-threads) - [Docker Compose Support in Spring Boot 3](https://www.baeldung.com/ops/docker-compose-support-spring-boot) +- [A Guide to RestClient in Spring Boot](https://www.baeldung.com/spring-boot-restclient) diff --git a/spring-boot-modules/spring-boot-data-2/README.md b/spring-boot-modules/spring-boot-data-2/README.md index 9ff2b2caf8..8e6619dce4 100644 --- a/spring-boot-modules/spring-boot-data-2/README.md +++ b/spring-boot-modules/spring-boot-data-2/README.md @@ -1,7 +1,6 @@ ### Relevant Articles: - [HttpMessageNotWritableException: No Converter for [class …] With Preset Content-Type](https://www.baeldung.com/spring-no-converter-with-preset) -- [Spring Boot: Customize the Jackson ObjectMapper](https://www.baeldung.com/spring-boot-customize-jackson-objectmapper) - [“HttpMessageNotWritableException: No converter found for return value of type”](https://www.baeldung.com/spring-no-converter-found) - [Creating a Read-Only Repository with Spring Data](https://www.baeldung.com/spring-data-read-only-repository) - [Using JaVers for Data Model Auditing in Spring Data](https://www.baeldung.com/spring-data-javers-audit) diff --git a/spring-boot-modules/spring-boot-data/README.md b/spring-boot-modules/spring-boot-data/README.md index c56c87014d..aa745b77a2 100644 --- a/spring-boot-modules/spring-boot-data/README.md +++ b/spring-boot-modules/spring-boot-data/README.md @@ -11,4 +11,5 @@ This module contains articles about Spring Boot with Spring Data - [Spring Custom Property Editor](https://www.baeldung.com/spring-mvc-custom-property-editor) - [Using @JsonComponent in Spring Boot](https://www.baeldung.com/spring-boot-jsoncomponent) - [Guide To Running Logic on Startup in Spring](https://www.baeldung.com/running-setup-logic-on-startup-in-spring) +- [Spring Boot: Customize the Jackson ObjectMapper](https://www.baeldung.com/spring-boot-customize-jackson-objectmapper) diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/app/Application.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/app/Application.java similarity index 100% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/app/Application.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/app/Application.java diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java similarity index 100% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java index d1875d03d9..ecb07e44b1 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java @@ -1,10 +1,10 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; - import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; + public class CoffeeConstants { public static final String DATETIME_FORMAT = "dd-MM-yyyy HH:mm"; diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java index edb2b478fc..0aa2d3eb96 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java @@ -1,11 +1,12 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.annotation.JsonInclude; +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; +import com.fasterxml.jackson.annotation.JsonInclude; @Configuration public class CoffeeCustomizerConfig { diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java index eff2b5c252..be8e3bb34d 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java @@ -1,12 +1,13 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.annotation.JsonInclude; +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; +import com.fasterxml.jackson.annotation.JsonInclude; @Configuration public class CoffeeHttpConverterConfiguration { diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java index 8057fff3db..19501f872c 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java @@ -1,12 +1,13 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.annotation.JsonInclude; +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; +import com.fasterxml.jackson.annotation.JsonInclude; @Configuration public class CoffeeJacksonBuilderConfig { diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java index f1ce6458ae..cadf17b6b8 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java @@ -1,13 +1,14 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @Configuration public class CoffeeObjectMapperConfig { diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java similarity index 88% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java index fc157f8156..55a928dabc 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java @@ -1,19 +1,19 @@ package com.baeldung.boot.jackson.config; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @Configuration @PropertySource("classpath:coffee.properties") public class CoffeeRegisterModuleConfig { @Bean - public Module javaTimeModule() { + public JavaTimeModule javaTimeModule() { JavaTimeModule module = new JavaTimeModule(); module.addSerializer(LOCAL_DATETIME_SERIALIZER); return module; diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java index 23749b18a2..36489a645a 100644 --- a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java +++ b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java @@ -1,11 +1,12 @@ package com.baeldung.boot.jackson.controller; -import com.baeldung.boot.jackson.model.Coffee; +import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; + import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; +import com.baeldung.boot.jackson.model.Coffee; @RestController public class CoffeeController { diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/model/Coffee.java b/spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/model/Coffee.java similarity index 100% rename from spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/model/Coffee.java rename to spring-boot-modules/spring-boot-data/src/main/java/com/baeldung/boot/jackson/model/Coffee.java diff --git a/spring-boot-modules/spring-boot-data-2/src/main/resources/coffee.properties b/spring-boot-modules/spring-boot-data/src/main/resources/coffee.properties similarity index 100% rename from spring-boot-modules/spring-boot-data-2/src/main/resources/coffee.properties rename to spring-boot-modules/spring-boot-data/src/main/resources/coffee.properties diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java index f1bc35a8ce..961ab8979c 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java @@ -1,15 +1,16 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeConstants; +import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.format.DateTimeFormatter; + import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import java.time.format.DateTimeFormatter; - -import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; -import static org.assertj.core.api.Assertions.assertThat; +import com.baeldung.boot.jackson.config.CoffeeConstants; @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public abstract class AbstractCoffeeIntegrationTest { diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java index d690de1b9c..ed5baf239c 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java @@ -1,8 +1,9 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeCustomizerConfig; import org.springframework.context.annotation.Import; +import com.baeldung.boot.jackson.config.CoffeeCustomizerConfig; + @Import(CoffeeCustomizerConfig.class) public class CoffeeCustomizerIntegrationTest extends AbstractCoffeeIntegrationTest { } diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java index 62b1d42152..0c77948d8e 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java @@ -1,8 +1,9 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeHttpConverterConfiguration; import org.springframework.context.annotation.Import; +import com.baeldung.boot.jackson.config.CoffeeHttpConverterConfiguration; + @Import(CoffeeHttpConverterConfiguration.class) public class CoffeeHttpConverterIntegrationTest extends AbstractCoffeeIntegrationTest { } diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java index 52a55394c0..da9251de99 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java @@ -1,8 +1,9 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeJacksonBuilderConfig; import org.springframework.context.annotation.Import; +import com.baeldung.boot.jackson.config.CoffeeJacksonBuilderConfig; + @Import(CoffeeJacksonBuilderConfig.class) public class CoffeeJacksonBuilderIntegrationTest extends AbstractCoffeeIntegrationTest { } diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java index 34743ceba5..1226eacbf2 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java @@ -1,8 +1,9 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeObjectMapperConfig; import org.springframework.context.annotation.Import; +import com.baeldung.boot.jackson.config.CoffeeObjectMapperConfig; + @Import(CoffeeObjectMapperConfig.class) public class CoffeeObjectMapperIntegrationTest extends AbstractCoffeeIntegrationTest { } diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java similarity index 99% rename from spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java rename to spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java index 69bbd5be2a..38dc4a76cd 100644 --- a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java +++ b/spring-boot-modules/spring-boot-data/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java @@ -1,8 +1,9 @@ package com.baeldung.boot.jackson.app; -import com.baeldung.boot.jackson.config.CoffeeRegisterModuleConfig; import org.springframework.context.annotation.Import; +import com.baeldung.boot.jackson.config.CoffeeRegisterModuleConfig; + @Import(CoffeeRegisterModuleConfig.class) public class CoffeeRegisterModuleIntegrationTest extends AbstractCoffeeIntegrationTest { } diff --git a/spring-boot-modules/spring-boot-mvc/pom.xml b/spring-boot-modules/spring-boot-mvc/pom.xml index 369bcf799b..a251c5049b 100644 --- a/spring-boot-modules/spring-boot-mvc/pom.xml +++ b/spring-boot-modules/spring-boot-mvc/pom.xml @@ -91,6 +91,10 @@ aspectjweaver ${aspectjweaver.version} + + org.projectlombok + lombok + diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/RestValidationApplication.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/RestValidationApplication.java new file mode 100644 index 0000000000..b335aecac2 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/RestValidationApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.restvalidation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = { "com.baeldung.restvalidation" }) +public class RestValidationApplication { + + public static void main(String[] args) { + SpringApplication.run(RestValidationApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/MessageConfig.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/MessageConfig.java new file mode 100644 index 0000000000..e37e2c9d78 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/MessageConfig.java @@ -0,0 +1,38 @@ +package com.baeldung.restvalidation.config; + +import javax.validation.MessageInterpolator; + +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator; + +@Configuration +public class MessageConfig { + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:CustomValidationMessages"); + messageSource.setDefaultEncoding("UTF-8"); + return messageSource; + } + + @Bean + public MessageInterpolator getMessageInterpolator(MessageSource messageSource) { + MessageSourceResourceBundleLocator resourceBundleLocator = new MessageSourceResourceBundleLocator(messageSource); + ResourceBundleMessageInterpolator messageInterpolator = new ResourceBundleMessageInterpolator(resourceBundleLocator); + return new RecursiveLocaleContextMessageInterpolator(messageInterpolator); + } + + @Bean + public LocalValidatorFactoryBean getValidator(MessageInterpolator messageInterpolator) { + LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); + bean.setMessageInterpolator(messageInterpolator); + return bean; + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/RecursiveLocaleContextMessageInterpolator.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/RecursiveLocaleContextMessageInterpolator.java new file mode 100644 index 0000000000..003a3d79b0 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/config/RecursiveLocaleContextMessageInterpolator.java @@ -0,0 +1,36 @@ +package com.baeldung.restvalidation.config; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.validation.MessageInterpolator; + +import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; + +public class RecursiveLocaleContextMessageInterpolator extends AbstractMessageInterpolator { + + private static final Pattern PATTERN_PLACEHOLDER = Pattern.compile("\\{([^}]+)\\}"); + + private final MessageInterpolator interpolator; + + public RecursiveLocaleContextMessageInterpolator(ResourceBundleMessageInterpolator interpolator) { + this.interpolator = interpolator; + } + + @Override + public String interpolate(MessageInterpolator.Context context, Locale locale, String message) { + int level = 0; + while (containsPlaceholder(message) && (level++ < 2)) { + message = this.interpolator.interpolate(message, context, locale); + } + return message; + } + + private boolean containsPlaceholder(String code) { + Matcher matcher = PATTERN_PLACEHOLDER.matcher(code); + return matcher.find(); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/InputFieldError.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/InputFieldError.java new file mode 100644 index 0000000000..06bd764d97 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/InputFieldError.java @@ -0,0 +1,12 @@ +package com.baeldung.restvalidation.response; + +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class InputFieldError { + private String field; + private String message; + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/UpdateUserResponse.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/UpdateUserResponse.java new file mode 100644 index 0000000000..7eb400cd2c --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/response/UpdateUserResponse.java @@ -0,0 +1,17 @@ +package com.baeldung.restvalidation.response; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; + +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UpdateUserResponse { + + private List fieldErrors; + +} diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/User.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/User.java new file mode 100644 index 0000000000..f9a7d1a9b5 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/User.java @@ -0,0 +1,15 @@ +package com.baeldung.restvalidation.service1; + +import javax.validation.constraints.NotEmpty; + +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + + @NotEmpty + private String email; + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/UserService1.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/UserService1.java new file mode 100644 index 0000000000..790b5031e6 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service1/UserService1.java @@ -0,0 +1,36 @@ +package com.baeldung.restvalidation.service1; + +import org.springframework.http.*; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@RestController +public class UserService1 { + + @PutMapping(value = "/user1", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateUser(@RequestBody @Valid User user, + BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + + List fieldErrorList = bindingResult.getFieldErrors().stream() + .map(error -> new InputFieldError(error.getField(), error.getDefaultMessage())) + .collect(Collectors.toList()); + + UpdateUserResponse updateResponse = new UpdateUserResponse(fieldErrorList); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(updateResponse); + } + else { + // Update logic... + return ResponseEntity.status(HttpStatus.OK).build(); + } + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/User.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/User.java new file mode 100644 index 0000000000..a2e567766c --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/User.java @@ -0,0 +1,15 @@ +package com.baeldung.restvalidation.service2; + +import javax.validation.constraints.NotEmpty; + +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + + @NotEmpty(message = "{validation.email.notEmpty}") + private String email; + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/UserService2.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/UserService2.java new file mode 100644 index 0000000000..4593a2b1bd --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service2/UserService2.java @@ -0,0 +1,40 @@ +package com.baeldung.restvalidation.service2; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@RestController +public class UserService2 { + + @PutMapping(value = "/user2", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateUser(@RequestBody @Valid User user, + BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + + List fieldErrorList = bindingResult.getFieldErrors().stream() + .map(error -> new InputFieldError(error.getField(), error.getDefaultMessage())) + .collect(Collectors.toList()); + + UpdateUserResponse updateResponse = new UpdateUserResponse(fieldErrorList); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(updateResponse); + } + else { + // Update logic... + return ResponseEntity.status(HttpStatus.OK).build(); + } + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmpty.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmpty.java new file mode 100644 index 0000000000..93c7052d5b --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmpty.java @@ -0,0 +1,32 @@ +package com.baeldung.restvalidation.service3; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Documented +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) +@Retention(RUNTIME) +@Constraint(validatedBy = { FieldNotEmptyValidator.class }) +public @interface FieldNotEmpty { + + String message() default "{validation.notEmpty}"; + + String field() default "Field"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmptyValidator.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmptyValidator.java new file mode 100644 index 0000000000..356efc590b --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/FieldNotEmptyValidator.java @@ -0,0 +1,16 @@ +package com.baeldung.restvalidation.service3; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class FieldNotEmptyValidator implements ConstraintValidator { + + private String message; + private String field; + + @Override + public boolean isValid(Object value, ConstraintValidatorContext context) { + return (value != null && !value.toString().trim().isEmpty()); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/User.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/User.java new file mode 100644 index 0000000000..97b38feba4 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/User.java @@ -0,0 +1,13 @@ +package com.baeldung.restvalidation.service3; + +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + + @FieldNotEmpty(message = "{validation.notEmpty}", field = "{field.personalEmail}") + private String email; + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/UserService3.java b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/UserService3.java new file mode 100644 index 0000000000..e506b63d8e --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/java/com/baeldung/restvalidation/service3/UserService3.java @@ -0,0 +1,40 @@ +package com.baeldung.restvalidation.service3; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@RestController +public class UserService3 { + + @PutMapping(value = "/user3", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateUser(@RequestBody @Valid User user, + BindingResult bindingResult) { + if (bindingResult.hasFieldErrors()) { + + List fieldErrorList = bindingResult.getFieldErrors().stream() + .map(error -> new InputFieldError(error.getField(), error.getDefaultMessage())) + .collect(Collectors.toList()); + + UpdateUserResponse updateResponse = new UpdateUserResponse(fieldErrorList); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(updateResponse); + } + else { + // Update logic... + return ResponseEntity.status(HttpStatus.OK).build(); + } + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages.properties new file mode 100644 index 0000000000..58cabc30c9 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages.properties @@ -0,0 +1,3 @@ +field.personalEmail=Personal Email +validation.notEmpty={field} cannot be empty +validation.email.notEmpty=Email cannot be empty \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages_zh.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages_zh.properties new file mode 100644 index 0000000000..b4fbe909bb --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/CustomValidationMessages_zh.properties @@ -0,0 +1,3 @@ +field.personalEmail=個人電郵 +validation.notEmpty={field}不能是空白 +validation.email.notEmpty=電郵不能留空 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages.properties new file mode 100644 index 0000000000..90d3c88a8f --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages.properties @@ -0,0 +1 @@ +javax.validation.constraints.NotEmpty.message=The field cannot be empty \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages_zh.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages_zh.properties new file mode 100644 index 0000000000..04f415911c --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/ValidationMessages_zh.properties @@ -0,0 +1 @@ +javax.validation.constraints.NotEmpty.message=本欄不能留空 \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service1/UserService1IntegrationTest.java b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service1/UserService1IntegrationTest.java new file mode 100644 index 0000000000..969a6c17a6 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service1/UserService1IntegrationTest.java @@ -0,0 +1,75 @@ +package com.baeldung.restvalidation.service1; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.baeldung.restvalidation.RestValidationApplication; +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RestValidationApplication.class) +class UserService1IntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void whenUpdateValidEmail_thenReturnsOK() { + + // When + ResponseEntity responseEntity = updateUser(new User("test@email.com"), null); + + // Then + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + } + + @Test + void whenUpdateEmptyEmail_thenReturnsErrorMessageInEnglish() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), null); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("The field cannot be empty", error.getMessage()); + } + + @Test + void whenUpdateEmptyEmailWithLanguageHeaderEqualsToZh_thenReturnsErrorMessageInChinese() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), "zh-tw"); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("本欄不能留空", error.getMessage()); + } + + private ResponseEntity updateUser(User user, String language) { + + HttpHeaders headers = new HttpHeaders(); + if (Objects.nonNull(language)) { + headers.set(HttpHeaders.ACCEPT_LANGUAGE, language); + } + + return restTemplate.exchange( + "/user1", + HttpMethod.PUT, + new HttpEntity<>(user, headers), + UpdateUserResponse.class + ); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service2/UserService2IntegrationTest.java b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service2/UserService2IntegrationTest.java new file mode 100644 index 0000000000..3e92229d7b --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service2/UserService2IntegrationTest.java @@ -0,0 +1,75 @@ +package com.baeldung.restvalidation.service2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.baeldung.restvalidation.RestValidationApplication; +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RestValidationApplication.class) +class UserService2IntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void whenUpdateValidEmail_thenReturnsOK() { + + // When + ResponseEntity responseEntity = updateUser(new User("test@email.com"), null); + + // Then + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + } + + @Test + void whenUpdateEmptyEmail_thenReturnsErrorMessageInEnglish() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), null); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("Email cannot be empty", error.getMessage()); + } + + @Test + void whenUpdateEmptyEmailWithLanguageHeaderEqualsToZh_thenReturnsErrorMessageInChinese() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), "zh-tw"); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("電郵不能留空", error.getMessage()); + } + + private ResponseEntity updateUser(User user, String language) { + + HttpHeaders headers = new HttpHeaders(); + if (Objects.nonNull(language)) { + headers.set(HttpHeaders.ACCEPT_LANGUAGE, language); + } + + return restTemplate.exchange( + "/user2", + HttpMethod.PUT, + new HttpEntity<>(user, headers), + UpdateUserResponse.class + ); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service3/UserService3IntegrationTest.java b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service3/UserService3IntegrationTest.java new file mode 100644 index 0000000000..8bc4d460ea --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc/src/test/java/com/baeldung/restvalidation/service3/UserService3IntegrationTest.java @@ -0,0 +1,75 @@ +package com.baeldung.restvalidation.service3; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Objects; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.baeldung.restvalidation.RestValidationApplication; +import com.baeldung.restvalidation.response.InputFieldError; +import com.baeldung.restvalidation.response.UpdateUserResponse; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RestValidationApplication.class) +class UserService3IntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void whenUpdateValidEmail_thenReturnsOK() { + + // When + ResponseEntity responseEntity = updateUser(new User("test@email.com"), null); + + // Then + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + } + + @Test + void whenUpdateEmptyEmail_thenReturnsErrorMessageInEnglish() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), null); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("Personal Email cannot be empty", error.getMessage()); + } + + @Test + void whenUpdateEmptyEmailWithLanguageHeaderEqualsToZh_thenReturnsErrorMessageInChinese() { + + // When + ResponseEntity responseEntity = updateUser(new User(""), "zh-tw"); + + // Then + InputFieldError error = responseEntity.getBody().getFieldErrors().get(0); + assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode()); + assertEquals("個人電郵不能是空白", error.getMessage()); + } + + private ResponseEntity updateUser(User user, String language) { + + HttpHeaders headers = new HttpHeaders(); + if (Objects.nonNull(language)) { + headers.set(HttpHeaders.ACCEPT_LANGUAGE, language); + } + + return restTemplate.exchange( + "/user3", + HttpMethod.PUT, + new HttpEntity<>(user, headers), + UpdateUserResponse.class + ); + } + +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/pom.xml b/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/pom.xml index a8c3337de5..234f8b1b60 100644 --- a/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/pom.xml +++ b/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/pom.xml @@ -16,10 +16,12 @@ + org.springframework.boot spring-boot-starter-web + org.springframework.boot spring-boot-starter-data-rest @@ -30,16 +32,25 @@ spring-cloud-contract-wiremock test + org.springframework.cloud spring-cloud-contract-stub-runner test + com.baeldung.spring.cloud spring-cloud-contract-producer ${project.parent.version} + stubs test + + + * + * + + diff --git a/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/src/test/java/com/baeldung/spring/cloud/springcloudcontractconsumer/controller/BasicMathControllerIntegrationTest.java b/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/src/test/java/com/baeldung/spring/cloud/springcloudcontractconsumer/controller/BasicMathControllerIntegrationTest.java index e21223e6ea..c7d8b695db 100644 --- a/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/src/test/java/com/baeldung/spring/cloud/springcloudcontractconsumer/controller/BasicMathControllerIntegrationTest.java +++ b/spring-cloud-modules/spring-cloud-contract/spring-cloud-contract-consumer/src/test/java/com/baeldung/spring/cloud/springcloudcontractconsumer/controller/BasicMathControllerIntegrationTest.java @@ -1,13 +1,13 @@ package com.baeldung.spring.cloud.springcloudcontractconsumer.controller; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; -import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties; +import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; @@ -20,10 +20,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @AutoConfigureJsonTesters -@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, - ids = "com.baeldung.spring.cloud:spring-cloud-contract-producer:+:stubs:8090") public class BasicMathControllerIntegrationTest { + @Rule + public StubRunnerRule rule = new StubRunnerRule().downloadStub( + "com.baeldung.spring.cloud", + "spring-cloud-contract-producer") + .withPort(8090).failOnNoStubs(true); + @Autowired private MockMvc mockMvc; diff --git a/spring-di-2/pom.xml b/spring-di-2/pom.xml index 69333c74f1..0bd6c41a8c 100644 --- a/spring-di-2/pom.xml +++ b/spring-di-2/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-spring-5 + parent-spring-6 0.0.1-SNAPSHOT - ../parent-spring-5 + ../parent-spring-6 @@ -46,11 +46,6 @@ org.springframework spring-aspects - - javax.inject - javax.inject - ${javax.inject.version} - org.springframework.boot spring-boot-starter-test @@ -84,9 +79,8 @@ - 2.6.1 + 3.1.2 1.14.0 - 1 2.17.1 diff --git a/spring-di-2/src/main/java/com/baeldung/di/aspectj/PersonEntity.java b/spring-di-2/src/main/java/com/baeldung/di/aspectj/PersonEntity.java index f087a97c7e..758a942227 100644 --- a/spring-di-2/src/main/java/com/baeldung/di/aspectj/PersonEntity.java +++ b/spring-di-2/src/main/java/com/baeldung/di/aspectj/PersonEntity.java @@ -3,9 +3,9 @@ package com.baeldung.di.aspectj; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Transient; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; @Entity @Configurable(preConstruction = true) diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldByNameInjectIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldByNameInjectIntegrationTest.java index d1a75d73ea..f7ca089355 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldByNameInjectIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldByNameInjectIntegrationTest.java @@ -3,8 +3,8 @@ package com.baeldung.wiring.configuration.inject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import javax.inject.Inject; -import javax.inject.Named; +import jakarta.inject.Inject; +import jakarta.inject.Named; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldInjectIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldInjectIntegrationTest.java index 995f560701..c06205aee8 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldInjectIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldInjectIntegrationTest.java @@ -3,7 +3,7 @@ package com.baeldung.wiring.configuration.inject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import javax.inject.Inject; +import jakarta.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldQualifierInjectIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldQualifierInjectIntegrationTest.java index 67fa2bf3d4..2a3e41f63f 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldQualifierInjectIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/inject/FieldQualifierInjectIntegrationTest.java @@ -3,7 +3,7 @@ package com.baeldung.wiring.configuration.inject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import javax.inject.Inject; +import jakarta.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/FieldResourceInjectionIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/FieldResourceInjectionIntegrationTest.java index 938d557939..16430a0573 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/FieldResourceInjectionIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/FieldResourceInjectionIntegrationTest.java @@ -5,7 +5,7 @@ import static org.junit.Assert.assertNotNull; import java.io.File; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByQualifierResourceIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByQualifierResourceIntegrationTest.java index f49bf70aba..1c1e3388b5 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByQualifierResourceIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByQualifierResourceIntegrationTest.java @@ -9,7 +9,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceQualifier; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByTypeResourceIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByTypeResourceIntegrationTest.java index aecd02a1d5..660610753f 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByTypeResourceIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodByTypeResourceIntegrationTest.java @@ -8,7 +8,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceNameType; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodResourceInjectionIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodResourceInjectionIntegrationTest.java index 4ef9368c28..0fcc2b1c50 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodResourceInjectionIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/MethodResourceInjectionIntegrationTest.java @@ -8,7 +8,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceNameType; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/NamedResourceIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/NamedResourceIntegrationTest.java index 4339194f63..5aed1ac7cb 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/NamedResourceIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/NamedResourceIntegrationTest.java @@ -8,7 +8,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceNameType; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/QualifierResourceInjectionIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/QualifierResourceInjectionIntegrationTest.java index cc8c669757..03f82adef3 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/QualifierResourceInjectionIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/QualifierResourceInjectionIntegrationTest.java @@ -9,7 +9,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceQualifier; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/SetterResourceInjectionIntegrationTest.java b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/SetterResourceInjectionIntegrationTest.java index 90c8677bff..605f86172c 100644 --- a/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/SetterResourceInjectionIntegrationTest.java +++ b/spring-di-2/src/test/java/com/baeldung/wiring/configuration/resource/SetterResourceInjectionIntegrationTest.java @@ -8,7 +8,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.wiring.configuration.ApplicationContextTestResourceNameType; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.io.File; import static org.junit.Assert.assertEquals; diff --git a/spring-di-3/pom.xml b/spring-di-3/pom.xml index 2d635d1f85..ba1a18ae8c 100644 --- a/spring-di-3/pom.xml +++ b/spring-di-3/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-spring-5 + parent-spring-6 0.0.1-SNAPSHOT - ../parent-spring-5 + ../parent-spring-6 @@ -45,8 +45,10 @@ - 2.6.1 + 3.1.2 2.17.1 + 2.0.9 + 1.4.11 \ No newline at end of file diff --git a/spring-di-3/src/test/java/com/baeldung/dynamic/autowire/DynamicAutowireIntegrationTest.java b/spring-di-3/src/test/java/com/baeldung/dynamic/autowire/DynamicAutowireIntegrationTest.java index 56582ecb66..d93f94b0e3 100644 --- a/spring-di-3/src/test/java/com/baeldung/dynamic/autowire/DynamicAutowireIntegrationTest.java +++ b/spring-di-3/src/test/java/com/baeldung/dynamic/autowire/DynamicAutowireIntegrationTest.java @@ -7,7 +7,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = DynamicAutowireConfig.class) diff --git a/spring-di-4/pom.xml b/spring-di-4/pom.xml index c6572495cb..1eec8efcf0 100644 --- a/spring-di-4/pom.xml +++ b/spring-di-4/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-2 + parent-boot-3 0.0.1-SNAPSHOT - ../parent-boot-2 + ../parent-boot-3 diff --git a/spring-di-4/src/main/java/com/baeldung/sampleabstract/BallService.java b/spring-di-4/src/main/java/com/baeldung/sampleabstract/BallService.java index 0d951aac8b..541d8dfb6b 100644 --- a/spring-di-4/src/main/java/com/baeldung/sampleabstract/BallService.java +++ b/spring-di-4/src/main/java/com/baeldung/sampleabstract/BallService.java @@ -2,7 +2,7 @@ package com.baeldung.sampleabstract; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; public abstract class BallService { diff --git a/spring-di/pom.xml b/spring-di/pom.xml index af0601deb6..bae7263ef9 100644 --- a/spring-di/pom.xml +++ b/spring-di/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-spring-5 + parent-spring-6 0.0.1-SNAPSHOT - ../parent-spring-5 + ../parent-spring-6 @@ -48,11 +48,6 @@ spring-context ${spring.version} - - javax.inject - javax.inject - ${javax.inject.version} - com.google.guava guava @@ -71,7 +66,7 @@ org.springframework.boot spring-boot-test - ${mockito.spring.boot.version} + ${spring-boot.version} test @@ -89,11 +84,6 @@ - - javax.annotation - javax.annotation-api - ${annotation-api.version} - @@ -143,11 +133,7 @@ org.baeldung.org.baeldung.sample.App - 1.3.2 - 1.4.4.RELEASE - 1 - 1.5.2.RELEASE - 1.10.19 + 3.1.2 1.9.5 diff --git a/spring-security-modules/spring-security-core-2/README.md b/spring-security-modules/spring-security-core-2/README.md index 80027ea42f..5f54dc1a50 100644 --- a/spring-security-modules/spring-security-core-2/README.md +++ b/spring-security-modules/spring-security-core-2/README.md @@ -8,7 +8,8 @@ This module contains articles about core Spring Security - [Prevent Cross-Site Scripting (XSS) in a Spring Application](https://www.baeldung.com/spring-prevent-xss) - [Guide to the AuthenticationManagerResolver in Spring Security](https://www.baeldung.com/spring-security-authenticationmanagerresolver) - [A Custom Spring SecurityConfigurer](https://www.baeldung.com/spring-security-custom-configurer) - +- [HttpSecurity vs. WebSecurity in Spring Security](https://www.baeldung.com/spring-security-httpsecurity-vs-websecurity) + ### Build the Project `mvn clean install` diff --git a/testing-modules/testing-libraries-2/README.md b/testing-modules/testing-libraries-2/README.md index 9e17da96a7..a37612e207 100644 --- a/testing-modules/testing-libraries-2/README.md +++ b/testing-modules/testing-libraries-2/README.md @@ -7,3 +7,4 @@ - [Gray Box Testing Using the OAT Technique](https://www.baeldung.com/java-gray-box-orthogonal-array-testing) - [Unit Testing of System.in With JUnit](https://www.baeldung.com/java-junit-testing-system-in) - [Fail Maven Build if JUnit Coverage Falls Below Certain Threshold](https://www.baeldung.com/maven-junit-fail-build-coverage-threshold) +- [How to Mock Environment Variables in Unit Tests](https://www.baeldung.com/java-unit-testing-environment-variables) diff --git a/grails/.gitignore b/web-modules/grails/.gitignore similarity index 100% rename from grails/.gitignore rename to web-modules/grails/.gitignore diff --git a/grails/README.md b/web-modules/grails/README.md similarity index 100% rename from grails/README.md rename to web-modules/grails/README.md diff --git a/grails/build.gradle b/web-modules/grails/build.gradle similarity index 100% rename from grails/build.gradle rename to web-modules/grails/build.gradle diff --git a/grails/gradle.properties b/web-modules/grails/gradle.properties similarity index 100% rename from grails/gradle.properties rename to web-modules/grails/gradle.properties diff --git a/grails/gradle/wrapper/gradle-wrapper.properties b/web-modules/grails/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from grails/gradle/wrapper/gradle-wrapper.properties rename to web-modules/grails/gradle/wrapper/gradle-wrapper.properties diff --git a/grails/gradlew b/web-modules/grails/gradlew similarity index 100% rename from grails/gradlew rename to web-modules/grails/gradlew diff --git a/grails/gradlew.bat b/web-modules/grails/gradlew.bat similarity index 100% rename from grails/gradlew.bat rename to web-modules/grails/gradlew.bat diff --git a/grails/grails-app/assets/images/apple-touch-icon-retina.png b/web-modules/grails/grails-app/assets/images/apple-touch-icon-retina.png similarity index 100% rename from grails/grails-app/assets/images/apple-touch-icon-retina.png rename to web-modules/grails/grails-app/assets/images/apple-touch-icon-retina.png diff --git a/grails/grails-app/assets/images/apple-touch-icon.png b/web-modules/grails/grails-app/assets/images/apple-touch-icon.png similarity index 100% rename from grails/grails-app/assets/images/apple-touch-icon.png rename to web-modules/grails/grails-app/assets/images/apple-touch-icon.png diff --git a/grails/grails-app/assets/images/favicon.ico b/web-modules/grails/grails-app/assets/images/favicon.ico similarity index 100% rename from grails/grails-app/assets/images/favicon.ico rename to web-modules/grails/grails-app/assets/images/favicon.ico diff --git a/grails/grails-app/assets/images/grails-cupsonly-logo-white.svg b/web-modules/grails/grails-app/assets/images/grails-cupsonly-logo-white.svg similarity index 100% rename from grails/grails-app/assets/images/grails-cupsonly-logo-white.svg rename to web-modules/grails/grails-app/assets/images/grails-cupsonly-logo-white.svg diff --git a/grails/grails-app/assets/images/grails.svg b/web-modules/grails/grails-app/assets/images/grails.svg similarity index 100% rename from grails/grails-app/assets/images/grails.svg rename to web-modules/grails/grails-app/assets/images/grails.svg diff --git a/grails/grails-app/assets/images/skin/database_add.png b/web-modules/grails/grails-app/assets/images/skin/database_add.png similarity index 100% rename from grails/grails-app/assets/images/skin/database_add.png rename to web-modules/grails/grails-app/assets/images/skin/database_add.png diff --git a/grails/grails-app/assets/images/skin/database_delete.png b/web-modules/grails/grails-app/assets/images/skin/database_delete.png similarity index 100% rename from grails/grails-app/assets/images/skin/database_delete.png rename to web-modules/grails/grails-app/assets/images/skin/database_delete.png diff --git a/grails/grails-app/assets/images/skin/database_edit.png b/web-modules/grails/grails-app/assets/images/skin/database_edit.png similarity index 100% rename from grails/grails-app/assets/images/skin/database_edit.png rename to web-modules/grails/grails-app/assets/images/skin/database_edit.png diff --git a/grails/grails-app/assets/images/skin/database_save.png b/web-modules/grails/grails-app/assets/images/skin/database_save.png similarity index 100% rename from grails/grails-app/assets/images/skin/database_save.png rename to web-modules/grails/grails-app/assets/images/skin/database_save.png diff --git a/grails/grails-app/assets/images/skin/database_table.png b/web-modules/grails/grails-app/assets/images/skin/database_table.png similarity index 100% rename from grails/grails-app/assets/images/skin/database_table.png rename to web-modules/grails/grails-app/assets/images/skin/database_table.png diff --git a/grails/grails-app/assets/images/skin/exclamation.png b/web-modules/grails/grails-app/assets/images/skin/exclamation.png similarity index 100% rename from grails/grails-app/assets/images/skin/exclamation.png rename to web-modules/grails/grails-app/assets/images/skin/exclamation.png diff --git a/grails/grails-app/assets/images/skin/house.png b/web-modules/grails/grails-app/assets/images/skin/house.png similarity index 100% rename from grails/grails-app/assets/images/skin/house.png rename to web-modules/grails/grails-app/assets/images/skin/house.png diff --git a/grails/grails-app/assets/images/skin/information.png b/web-modules/grails/grails-app/assets/images/skin/information.png similarity index 100% rename from grails/grails-app/assets/images/skin/information.png rename to web-modules/grails/grails-app/assets/images/skin/information.png diff --git a/grails/grails-app/assets/images/skin/shadow.jpg b/web-modules/grails/grails-app/assets/images/skin/shadow.jpg similarity index 100% rename from grails/grails-app/assets/images/skin/shadow.jpg rename to web-modules/grails/grails-app/assets/images/skin/shadow.jpg diff --git a/grails/grails-app/assets/images/skin/sorted_asc.gif b/web-modules/grails/grails-app/assets/images/skin/sorted_asc.gif similarity index 100% rename from grails/grails-app/assets/images/skin/sorted_asc.gif rename to web-modules/grails/grails-app/assets/images/skin/sorted_asc.gif diff --git a/grails/grails-app/assets/images/skin/sorted_desc.gif b/web-modules/grails/grails-app/assets/images/skin/sorted_desc.gif similarity index 100% rename from grails/grails-app/assets/images/skin/sorted_desc.gif rename to web-modules/grails/grails-app/assets/images/skin/sorted_desc.gif diff --git a/grails/grails-app/assets/images/spinner.gif b/web-modules/grails/grails-app/assets/images/spinner.gif similarity index 100% rename from grails/grails-app/assets/images/spinner.gif rename to web-modules/grails/grails-app/assets/images/spinner.gif diff --git a/grails/grails-app/assets/javascripts/application.js b/web-modules/grails/grails-app/assets/javascripts/application.js similarity index 100% rename from grails/grails-app/assets/javascripts/application.js rename to web-modules/grails/grails-app/assets/javascripts/application.js diff --git a/grails/grails-app/assets/javascripts/bootstrap.js b/web-modules/grails/grails-app/assets/javascripts/bootstrap.js similarity index 100% rename from grails/grails-app/assets/javascripts/bootstrap.js rename to web-modules/grails/grails-app/assets/javascripts/bootstrap.js diff --git a/grails/grails-app/assets/javascripts/jquery-2.2.0.min.js b/web-modules/grails/grails-app/assets/javascripts/jquery-2.2.0.min.js similarity index 100% rename from grails/grails-app/assets/javascripts/jquery-2.2.0.min.js rename to web-modules/grails/grails-app/assets/javascripts/jquery-2.2.0.min.js diff --git a/grails/grails-app/assets/stylesheets/application.css b/web-modules/grails/grails-app/assets/stylesheets/application.css similarity index 100% rename from grails/grails-app/assets/stylesheets/application.css rename to web-modules/grails/grails-app/assets/stylesheets/application.css diff --git a/grails/grails-app/assets/stylesheets/bootstrap.css b/web-modules/grails/grails-app/assets/stylesheets/bootstrap.css similarity index 100% rename from grails/grails-app/assets/stylesheets/bootstrap.css rename to web-modules/grails/grails-app/assets/stylesheets/bootstrap.css diff --git a/grails/grails-app/assets/stylesheets/errors.css b/web-modules/grails/grails-app/assets/stylesheets/errors.css similarity index 100% rename from grails/grails-app/assets/stylesheets/errors.css rename to web-modules/grails/grails-app/assets/stylesheets/errors.css diff --git a/grails/grails-app/assets/stylesheets/grails.css b/web-modules/grails/grails-app/assets/stylesheets/grails.css similarity index 100% rename from grails/grails-app/assets/stylesheets/grails.css rename to web-modules/grails/grails-app/assets/stylesheets/grails.css diff --git a/grails/grails-app/assets/stylesheets/main.css b/web-modules/grails/grails-app/assets/stylesheets/main.css similarity index 100% rename from grails/grails-app/assets/stylesheets/main.css rename to web-modules/grails/grails-app/assets/stylesheets/main.css diff --git a/grails/grails-app/assets/stylesheets/mobile.css b/web-modules/grails/grails-app/assets/stylesheets/mobile.css similarity index 100% rename from grails/grails-app/assets/stylesheets/mobile.css rename to web-modules/grails/grails-app/assets/stylesheets/mobile.css diff --git a/grails/grails-app/conf/application.yml b/web-modules/grails/grails-app/conf/application.yml similarity index 100% rename from grails/grails-app/conf/application.yml rename to web-modules/grails/grails-app/conf/application.yml diff --git a/grails/grails-app/conf/logback.groovy b/web-modules/grails/grails-app/conf/logback.groovy similarity index 100% rename from grails/grails-app/conf/logback.groovy rename to web-modules/grails/grails-app/conf/logback.groovy diff --git a/grails/grails-app/conf/spring/resources.groovy b/web-modules/grails/grails-app/conf/spring/resources.groovy similarity index 100% rename from grails/grails-app/conf/spring/resources.groovy rename to web-modules/grails/grails-app/conf/spring/resources.groovy diff --git a/grails/grails-app/controllers/com/baeldung/grails/StudentController.groovy b/web-modules/grails/grails-app/controllers/com/baeldung/grails/StudentController.groovy similarity index 100% rename from grails/grails-app/controllers/com/baeldung/grails/StudentController.groovy rename to web-modules/grails/grails-app/controllers/com/baeldung/grails/StudentController.groovy diff --git a/grails/grails-app/controllers/grails/UrlMappings.groovy b/web-modules/grails/grails-app/controllers/grails/UrlMappings.groovy similarity index 100% rename from grails/grails-app/controllers/grails/UrlMappings.groovy rename to web-modules/grails/grails-app/controllers/grails/UrlMappings.groovy diff --git a/grails/grails-app/domain/com/baeldung/grails/Student.groovy b/web-modules/grails/grails-app/domain/com/baeldung/grails/Student.groovy similarity index 100% rename from grails/grails-app/domain/com/baeldung/grails/Student.groovy rename to web-modules/grails/grails-app/domain/com/baeldung/grails/Student.groovy diff --git a/grails/grails-app/i18n/messages.properties b/web-modules/grails/grails-app/i18n/messages.properties similarity index 100% rename from grails/grails-app/i18n/messages.properties rename to web-modules/grails/grails-app/i18n/messages.properties diff --git a/grails/grails-app/i18n/messages_cs.properties b/web-modules/grails/grails-app/i18n/messages_cs.properties similarity index 100% rename from grails/grails-app/i18n/messages_cs.properties rename to web-modules/grails/grails-app/i18n/messages_cs.properties diff --git a/grails/grails-app/i18n/messages_da.properties b/web-modules/grails/grails-app/i18n/messages_da.properties similarity index 100% rename from grails/grails-app/i18n/messages_da.properties rename to web-modules/grails/grails-app/i18n/messages_da.properties diff --git a/grails/grails-app/i18n/messages_de.properties b/web-modules/grails/grails-app/i18n/messages_de.properties similarity index 100% rename from grails/grails-app/i18n/messages_de.properties rename to web-modules/grails/grails-app/i18n/messages_de.properties diff --git a/grails/grails-app/i18n/messages_es.properties b/web-modules/grails/grails-app/i18n/messages_es.properties similarity index 100% rename from grails/grails-app/i18n/messages_es.properties rename to web-modules/grails/grails-app/i18n/messages_es.properties diff --git a/grails/grails-app/i18n/messages_fr.properties b/web-modules/grails/grails-app/i18n/messages_fr.properties similarity index 100% rename from grails/grails-app/i18n/messages_fr.properties rename to web-modules/grails/grails-app/i18n/messages_fr.properties diff --git a/grails/grails-app/i18n/messages_it.properties b/web-modules/grails/grails-app/i18n/messages_it.properties similarity index 100% rename from grails/grails-app/i18n/messages_it.properties rename to web-modules/grails/grails-app/i18n/messages_it.properties diff --git a/grails/grails-app/i18n/messages_ja.properties b/web-modules/grails/grails-app/i18n/messages_ja.properties similarity index 100% rename from grails/grails-app/i18n/messages_ja.properties rename to web-modules/grails/grails-app/i18n/messages_ja.properties diff --git a/grails/grails-app/i18n/messages_nb.properties b/web-modules/grails/grails-app/i18n/messages_nb.properties similarity index 100% rename from grails/grails-app/i18n/messages_nb.properties rename to web-modules/grails/grails-app/i18n/messages_nb.properties diff --git a/grails/grails-app/i18n/messages_nl.properties b/web-modules/grails/grails-app/i18n/messages_nl.properties similarity index 100% rename from grails/grails-app/i18n/messages_nl.properties rename to web-modules/grails/grails-app/i18n/messages_nl.properties diff --git a/grails/grails-app/i18n/messages_pl.properties b/web-modules/grails/grails-app/i18n/messages_pl.properties similarity index 100% rename from grails/grails-app/i18n/messages_pl.properties rename to web-modules/grails/grails-app/i18n/messages_pl.properties diff --git a/grails/grails-app/i18n/messages_pt_BR.properties b/web-modules/grails/grails-app/i18n/messages_pt_BR.properties similarity index 100% rename from grails/grails-app/i18n/messages_pt_BR.properties rename to web-modules/grails/grails-app/i18n/messages_pt_BR.properties diff --git a/grails/grails-app/i18n/messages_pt_PT.properties b/web-modules/grails/grails-app/i18n/messages_pt_PT.properties similarity index 100% rename from grails/grails-app/i18n/messages_pt_PT.properties rename to web-modules/grails/grails-app/i18n/messages_pt_PT.properties diff --git a/grails/grails-app/i18n/messages_ru.properties b/web-modules/grails/grails-app/i18n/messages_ru.properties similarity index 100% rename from grails/grails-app/i18n/messages_ru.properties rename to web-modules/grails/grails-app/i18n/messages_ru.properties diff --git a/grails/grails-app/i18n/messages_sk.properties b/web-modules/grails/grails-app/i18n/messages_sk.properties similarity index 100% rename from grails/grails-app/i18n/messages_sk.properties rename to web-modules/grails/grails-app/i18n/messages_sk.properties diff --git a/grails/grails-app/i18n/messages_sv.properties b/web-modules/grails/grails-app/i18n/messages_sv.properties similarity index 100% rename from grails/grails-app/i18n/messages_sv.properties rename to web-modules/grails/grails-app/i18n/messages_sv.properties diff --git a/grails/grails-app/i18n/messages_th.properties b/web-modules/grails/grails-app/i18n/messages_th.properties similarity index 100% rename from grails/grails-app/i18n/messages_th.properties rename to web-modules/grails/grails-app/i18n/messages_th.properties diff --git a/grails/grails-app/i18n/messages_zh_CN.properties b/web-modules/grails/grails-app/i18n/messages_zh_CN.properties similarity index 100% rename from grails/grails-app/i18n/messages_zh_CN.properties rename to web-modules/grails/grails-app/i18n/messages_zh_CN.properties diff --git a/grails/grails-app/init/grails/Application.groovy b/web-modules/grails/grails-app/init/grails/Application.groovy similarity index 100% rename from grails/grails-app/init/grails/Application.groovy rename to web-modules/grails/grails-app/init/grails/Application.groovy diff --git a/grails/grails-app/init/grails/BootStrap.groovy b/web-modules/grails/grails-app/init/grails/BootStrap.groovy similarity index 100% rename from grails/grails-app/init/grails/BootStrap.groovy rename to web-modules/grails/grails-app/init/grails/BootStrap.groovy diff --git a/grails/grails-app/services/com/baeldung/grails/StudentService.groovy b/web-modules/grails/grails-app/services/com/baeldung/grails/StudentService.groovy similarity index 100% rename from grails/grails-app/services/com/baeldung/grails/StudentService.groovy rename to web-modules/grails/grails-app/services/com/baeldung/grails/StudentService.groovy diff --git a/grails/grails-app/views/error.gsp b/web-modules/grails/grails-app/views/error.gsp similarity index 100% rename from grails/grails-app/views/error.gsp rename to web-modules/grails/grails-app/views/error.gsp diff --git a/grails/grails-app/views/index.gsp b/web-modules/grails/grails-app/views/index.gsp similarity index 100% rename from grails/grails-app/views/index.gsp rename to web-modules/grails/grails-app/views/index.gsp diff --git a/grails/grails-app/views/layouts/main.gsp b/web-modules/grails/grails-app/views/layouts/main.gsp similarity index 100% rename from grails/grails-app/views/layouts/main.gsp rename to web-modules/grails/grails-app/views/layouts/main.gsp diff --git a/grails/grails-app/views/notFound.gsp b/web-modules/grails/grails-app/views/notFound.gsp similarity index 100% rename from grails/grails-app/views/notFound.gsp rename to web-modules/grails/grails-app/views/notFound.gsp diff --git a/grails/grails-app/views/student/create.gsp b/web-modules/grails/grails-app/views/student/create.gsp similarity index 100% rename from grails/grails-app/views/student/create.gsp rename to web-modules/grails/grails-app/views/student/create.gsp diff --git a/grails/grails-app/views/student/index.gsp b/web-modules/grails/grails-app/views/student/index.gsp similarity index 100% rename from grails/grails-app/views/student/index.gsp rename to web-modules/grails/grails-app/views/student/index.gsp diff --git a/grails/grails-app/views/student/show.gsp b/web-modules/grails/grails-app/views/student/show.gsp similarity index 100% rename from grails/grails-app/views/student/show.gsp rename to web-modules/grails/grails-app/views/student/show.gsp diff --git a/grails/grailsw b/web-modules/grails/grailsw similarity index 100% rename from grails/grailsw rename to web-modules/grails/grailsw diff --git a/grails/grailsw.bat b/web-modules/grails/grailsw.bat similarity index 100% rename from grails/grailsw.bat rename to web-modules/grails/grailsw.bat diff --git a/grails/src/integration-test/groovy/com/baeldung/grails/StudentIntegrationSpec.groovy b/web-modules/grails/src/integration-test/groovy/com/baeldung/grails/StudentIntegrationSpec.groovy similarity index 100% rename from grails/src/integration-test/groovy/com/baeldung/grails/StudentIntegrationSpec.groovy rename to web-modules/grails/src/integration-test/groovy/com/baeldung/grails/StudentIntegrationSpec.groovy diff --git a/grails/src/integration-test/resources/GebConfig.groovy b/web-modules/grails/src/integration-test/resources/GebConfig.groovy similarity index 100% rename from grails/src/integration-test/resources/GebConfig.groovy rename to web-modules/grails/src/integration-test/resources/GebConfig.groovy diff --git a/grails/src/test/groovy/com/baeldung/grails/StudentControllerSpec.groovy b/web-modules/grails/src/test/groovy/com/baeldung/grails/StudentControllerSpec.groovy similarity index 100% rename from grails/src/test/groovy/com/baeldung/grails/StudentControllerSpec.groovy rename to web-modules/grails/src/test/groovy/com/baeldung/grails/StudentControllerSpec.groovy diff --git a/web-modules/pom.xml b/web-modules/pom.xml index 684283b546..57810f90de 100644 --- a/web-modules/pom.xml +++ b/web-modules/pom.xml @@ -19,6 +19,7 @@ bootique dropwizard google-web-toolkit + jakarta-ee javax-servlets