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-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-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-jndi/src/test/java/com/baeldung/jndi/JndiNamingUnitTest.java b/core-java-modules/core-java-jndi/src/test/java/com/baeldung/jndi/JndiNamingUnitTest.java new file mode 100644 index 0000000000..66d8d57d4e --- /dev/null +++ b/core-java-modules/core-java-jndi/src/test/java/com/baeldung/jndi/JndiNamingUnitTest.java @@ -0,0 +1,72 @@ +package com.baeldung.jndi; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.jndi.JndiTemplate; +import org.springframework.mock.jndi.SimpleNamingContextBuilder; + +import javax.naming.*; +import javax.sql.DataSource; + +import java.util.Enumeration; + +import static org.junit.jupiter.api.Assertions.*; + +class JndiNamingUnitTest { + + private static InitialContext context; + private static DriverManagerDataSource dataSource; + + @BeforeAll + static void setUp() throws Exception { + SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); + dataSource = new DriverManagerDataSource("jdbc:h2:mem:mydb"); + builder.activate(); + + JndiTemplate jndiTemplate = new JndiTemplate(); + context = (InitialContext) jndiTemplate.getContext(); + + dataSource.setDriverClassName("org.h2.Driver"); + context.bind("java:comp/env/jdbc/datasource", dataSource); + } + + @Test + void givenACompositeName_whenAddingAnElement_thenNameIsAdded() throws Exception { + Name objectName = new CompositeName("java:comp/env/jdbc"); + + Enumeration 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-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/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-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/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/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-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 c19b3f3694..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,24 +1,18 @@ package com.baeldung.spring.cloud.springcloudcontractconsumer.controller; -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; +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; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -26,42 +20,16 @@ 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; - private static WireMockServer wireMockServer; - - - @BeforeClass - public static void setupClass() { - WireMockConfiguration wireMockConfiguration = WireMockConfiguration.options().port(8090); // Use the same port as in your code - - wireMockServer = new WireMockServer(wireMockConfiguration); - wireMockServer.start(); - } - - @AfterClass - public static void teardownClass() { - wireMockServer.stop(); - } - - @Before - public void setup() { - wireMockServer.stubFor(get(urlEqualTo("/validate/prime-number?number=1")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("Odd"))); - - wireMockServer.stubFor(get(urlEqualTo("/validate/prime-number?number=2")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("Even"))); - } @Test public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven() throws Exception { 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