diff --git a/core-java-modules/core-java-collections-maps-2/src/main/java/com/baeldung/map/Product.java b/core-java-modules/core-java-collections-maps-2/src/main/java/com/baeldung/map/Product.java index 5559895730..09b1a8847d 100644 --- a/core-java-modules/core-java-collections-maps-2/src/main/java/com/baeldung/map/Product.java +++ b/core-java-modules/core-java-collections-maps-2/src/main/java/com/baeldung/map/Product.java @@ -26,7 +26,7 @@ public class Product { return tags; } - public Product addTagsOfOtherProdcut(Product product) { + public Product addTagsOfOtherProduct(Product product) { this.tags.addAll(product.getTags()); return this; } @@ -100,11 +100,11 @@ public class Product { HashMap productsByName = new HashMap<>(); Product eBike2 = new Product("E-Bike", "A bike with a battery"); eBike2.getTags().add("sport"); - productsByName.merge("E-Bike", eBike2, Product::addTagsOfOtherProdcut); + productsByName.merge("E-Bike", eBike2, Product::addTagsOfOtherProduct); //Prior to Java 8: if(productsByName.containsKey("E-Bike")) { - productsByName.get("E-Bike").addTagsOfOtherProdcut(eBike2); + productsByName.get("E-Bike").addTagsOfOtherProduct(eBike2); } else { productsByName.put("E-Bike", eBike2); } @@ -117,7 +117,7 @@ public class Product { productsByName.compute("E-Bike", (k,v) -> { if(v != null) { - return v.addTagsOfOtherProdcut(eBike2); + return v.addTagsOfOtherProduct(eBike2); } else { return eBike2; } @@ -125,7 +125,7 @@ public class Product { //Prior to Java 8: if(productsByName.containsKey("E-Bike")) { - productsByName.get("E-Bike").addTagsOfOtherProdcut(eBike2); + productsByName.get("E-Bike").addTagsOfOtherProduct(eBike2); } else { productsByName.put("E-Bike", eBike2); } diff --git a/core-java-modules/core-java-concurrency-advanced-4/README.md b/core-java-modules/core-java-concurrency-advanced-4/README.md index 98f2894515..5b93cec0dd 100644 --- a/core-java-modules/core-java-concurrency-advanced-4/README.md +++ b/core-java-modules/core-java-concurrency-advanced-4/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock) +- [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices) diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalBadPractice.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalBadPractice.java new file mode 100644 index 0000000000..ca6b7db765 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalBadPractice.java @@ -0,0 +1,35 @@ +package com.baeldung.synchronizationbadpractices; + +public class AnimalBadPractice { + + private String name; + private String owner; + + public String getName() { + return name; + } + + public String getOwner() { + return owner; + } + + public synchronized void setName(String name) { + this.name = name; + } + + public void setOwner(String owner) { + synchronized(this) { + this.owner = owner; + } + } + + public AnimalBadPractice() { + + } + + public AnimalBadPractice(String name, String owner) { + this.name = name; + this.owner = owner; + } + +} diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalSolution.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalSolution.java new file mode 100644 index 0000000000..b49cfa05d4 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/AnimalSolution.java @@ -0,0 +1,42 @@ +package com.baeldung.synchronizationbadpractices; + +public class AnimalSolution { + + private final Object objLock1 = new Object(); + private final Object objLock2 = new Object(); + + private String name; + private String owner; + + public String getName() { + return name; + } + + public String getOwner() { + return owner; + } + + + public void setName(String name) { + synchronized(objLock1) { + this.name = name; + } + } + + public void setOwner(String owner) { + synchronized(objLock2) { + this.owner = owner; + } + } + + public AnimalSolution() { + + } + + public AnimalSolution(String name, String owner) { + this.name = name; + this.owner = owner; + } + + +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationBadPracticeExample.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationBadPracticeExample.java new file mode 100644 index 0000000000..84d2e1cbb6 --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationBadPracticeExample.java @@ -0,0 +1,51 @@ +package com.baeldung.synchronizationbadpractices; + +public class SynchronizationBadPracticeExample { + + public void stringBadPractice1() { + String stringLock = "LOCK_STRING"; + synchronized (stringLock) { + // ... + } + } + + private final String stringLock = "LOCK_STRING"; + public void stringBadPractice2() { + synchronized (stringLock) { + // ... + } + } + + private final String internedStringLock = new String("LOCK_STRING").intern(); + public void stringBadPractice3() { + synchronized (internedStringLock) { + // ... + } + } + + private final Boolean booleanLock = Boolean.FALSE; + public void booleanBadPractice() { + synchronized (booleanLock) { + // ... + } + } + + private int count = 0; + private final Integer intLock = count; + public void boxedPrimitiveBadPractice() { + synchronized (intLock) { + count++; + // ... + } + } + + public void classBadPractice() throws InterruptedException { + AnimalBadPractice animalObj = new AnimalBadPractice("Tommy", "John"); + synchronized(animalObj) { + while (true) { + Thread.sleep(Integer.MAX_VALUE); + } + } + } + +} diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationSolutionExample.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationSolutionExample.java new file mode 100644 index 0000000000..a3ab8a2cee --- /dev/null +++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/synchronizationbadpractices/SynchronizationSolutionExample.java @@ -0,0 +1,30 @@ +package com.baeldung.synchronizationbadpractices; + +public class SynchronizationSolutionExample { + + private final String stringLock = new String("LOCK_STRING"); + public void stringSolution() { + synchronized (stringLock) { + // ... + } + } + + private int count = 0; + private final Integer intLock = new Integer(count); + public void boxedPrimitiveSolution() { + synchronized(intLock) { + count++; + // ... + } + } + + private static int staticCount = 0; + private static final Object staticObjLock = new Object(); + public void staticVariableSolution() { + synchronized(staticObjLock) { + staticCount++; + // ... + } + } + +} diff --git a/core-java-modules/core-java-jvm-2/README.md b/core-java-modules/core-java-jvm-2/README.md index 36cafd3288..ccca3a11ac 100644 --- a/core-java-modules/core-java-jvm-2/README.md +++ b/core-java-modules/core-java-jvm-2/README.md @@ -11,4 +11,5 @@ This module contains articles about working with the Java Virtual Machine (JVM). - [Where Is the Array Length Stored in JVM?](https://www.baeldung.com/java-jvm-array-length) - [Memory Address of Objects in Java](https://www.baeldung.com/java-object-memory-address) - [List All Classes Loaded in a Specific Class Loader](https://www.baeldung.com/java-list-classes-class-loader) +- [An Introduction to the Constant Pool in the JVM](https://www.baeldung.com/jvm-constant-pool) - More articles: [[<-- prev]](/core-java-modules/core-java-jvm) diff --git a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccount.java b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccount.java new file mode 100644 index 0000000000..db1f41e6df --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccount.java @@ -0,0 +1,16 @@ +package com.baeldung.compareto; + +public class BankAccount implements Comparable { + + private final int balance; + + public BankAccount(int balance) { + this.balance = balance; + } + + @Override + public int compareTo(BankAccount anotherAccount) { + return this.balance - anotherAccount.balance; + } + +} diff --git a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccountFix.java b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccountFix.java new file mode 100644 index 0000000000..95e55fdf22 --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/BankAccountFix.java @@ -0,0 +1,16 @@ +package com.baeldung.compareto; + +public class BankAccountFix implements Comparable { + + private final int balance; + + public BankAccountFix(int balance) { + this.balance = balance; + } + + @Override + public int compareTo(BankAccountFix anotherAccount) { + return Integer.compare(this.balance, anotherAccount.balance); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/FootballPlayer.java b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/FootballPlayer.java new file mode 100644 index 0000000000..173ee32434 --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/FootballPlayer.java @@ -0,0 +1,32 @@ +package com.baeldung.compareto; + +public class FootballPlayer implements Comparable { + + private final String name; + private final int goalsScored; + + public FootballPlayer(String name, int goalsScored) { + this.name = name; + this.goalsScored = goalsScored; + } + + public String getName() { + return name; + } + + @Override + public int compareTo(FootballPlayer anotherPlayer) { + return Integer.compare(this.goalsScored, anotherPlayer.goalsScored); + } + + @Override + public boolean equals(Object object) { + if (this == object) + return true; + if (object == null || getClass() != object.getClass()) + return false; + FootballPlayer player = (FootballPlayer) object; + return name.equals(player.name); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/HandballPlayer.java b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/HandballPlayer.java new file mode 100644 index 0000000000..022155ccae --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/compareto/HandballPlayer.java @@ -0,0 +1,12 @@ +package com.baeldung.compareto; + +public class HandballPlayer { + + private final String name; + private final int height; + + public HandballPlayer(String name, int height) { + this.name = name; + this.height = height; + } +} diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/ArraysSortingUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/ArraysSortingUnitTest.java new file mode 100644 index 0000000000..2082386dba --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/ArraysSortingUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.compareto; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ArraysSortingUnitTest { + + @Test + public void givenArrayOfNumbers_whenSortingArray_thenNumbersAreSortedAscending() { + int[] numbers = new int[] {5, 3, 9, 11, 1, 7}; + Arrays.sort(numbers); + assertThat(numbers).containsExactly(1, 3, 5, 7, 9, 11); + } + + @Test + public void givenArrayOfStrings_whenSortingArray_thenStringsAreSortedAlphabetically() { + String[] players = new String[] {"ronaldo", "modric", "ramos", "messi"}; + Arrays.sort(players); + assertThat(players).containsExactly("messi", "modric", "ramos", "ronaldo"); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountFixUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountFixUnitTest.java new file mode 100644 index 0000000000..9ca16d1372 --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountFixUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.compareto; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BankAccountFixUnitTest { + + @Test + public void givenComparisonBasedImpl_whenUsingSmallIntegers_thenComparisonWorks() { + BankAccountFix accountOne = new BankAccountFix(5000); + BankAccountFix accountTwo = new BankAccountFix(1000); + int comparison = accountOne.compareTo(accountTwo); + assertThat(comparison).isPositive(); + } + + @Test + public void givenComparisonBasedImpl_whenUsingLargeIntegers_thenComparisonWorks() { + BankAccountFix accountOne = new BankAccountFix(1900000000); + BankAccountFix accountTwo = new BankAccountFix(-2000000000); + int comparison = accountOne.compareTo(accountTwo); + assertThat(comparison).isPositive(); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountUnitTest.java new file mode 100644 index 0000000000..6ef9372ff9 --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/BankAccountUnitTest.java @@ -0,0 +1,25 @@ +package com.baeldung.compareto; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +public class BankAccountUnitTest { + + @Test + public void givenSubtractionBasedImpl_whenUsingSmallIntegers_thenComparisonWorks() { + BankAccount accountOne = new BankAccount(5000); + BankAccount accountTwo = new BankAccount(1000); + int comparison = accountOne.compareTo(accountTwo); + assertThat(comparison).isPositive(); + } + + @Test + public void givenSubtractionBasedImpl_whenUsingLargeIntegers_thenComparisonBreaks() { + BankAccount accountOne = new BankAccount(1900000000); + BankAccount accountTwo = new BankAccount(-2000000000); + int comparison = accountOne.compareTo(accountTwo); + assertThat(comparison).isNegative(); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/FootballPlayerUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/FootballPlayerUnitTest.java new file mode 100644 index 0000000000..6abd1e113b --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/FootballPlayerUnitTest.java @@ -0,0 +1,57 @@ +package com.baeldung.compareto; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FootballPlayerUnitTest { + + @Test + public void givenInconsistentCompareToAndEqualsImpl_whenUsingSortedSet_thenSomeElementsAreNotAdded() { + FootballPlayer messi = new FootballPlayer("Messi", 800); + FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 800); + + TreeSet set = new TreeSet<>(); + set.add(messi); + set.add(ronaldo); + + assertThat(set).hasSize(1); + assertThat(set).doesNotContain(ronaldo); + } + + @Test + public void givenCompareToImpl_whenUsingCustomComparator_thenComparatorLogicIsApplied() { + FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 900); + FootballPlayer messi = new FootballPlayer("Messi", 800); + FootballPlayer modric = new FootballPlayer("Modric", 100); + + List players = Arrays.asList(ronaldo, messi, modric); + Comparator nameComparator = Comparator.comparing(FootballPlayer::getName); + Collections.sort(players, nameComparator); + + assertThat(players).containsExactly(messi, modric, ronaldo); + } + + @Test + public void givenCompareToImpl_whenSavingElementsInTreeMap_thenKeysAreSortedUsingCompareTo() { + FootballPlayer ronaldo = new FootballPlayer("Ronaldo", 900); + FootballPlayer messi = new FootballPlayer("Messi", 800); + FootballPlayer modric = new FootballPlayer("Modric", 100); + + Map players = new TreeMap<>(); + players.put(ronaldo, "forward"); + players.put(messi, "forward"); + players.put(modric, "midfielder"); + + assertThat(players.keySet()).containsExactly(modric, messi, ronaldo); + } + +} diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/HandballPlayerUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/HandballPlayerUnitTest.java new file mode 100644 index 0000000000..143286f15f --- /dev/null +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/compareto/HandballPlayerUnitTest.java @@ -0,0 +1,20 @@ +package com.baeldung.compareto; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class HandballPlayerUnitTest { + + @Test + public void givenComparableIsNotImplemented_whenSortingArray_thenExceptionIsThrown() { + HandballPlayer duvnjak = new HandballPlayer("Duvnjak", 197); + HandballPlayer hansen = new HandballPlayer("Hansen", 196); + + HandballPlayer[] players = new HandballPlayer[] {duvnjak, hansen}; + + assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> Arrays.sort(players)); + } + +} diff --git a/core-java-modules/core-java-lang-oop-generics/README.md b/core-java-modules/core-java-lang-oop-generics/README.md index 74b9df7c65..9c9080ece3 100644 --- a/core-java-modules/core-java-lang-oop-generics/README.md +++ b/core-java-modules/core-java-lang-oop-generics/README.md @@ -7,3 +7,4 @@ This module contains articles about generics in Java - [Type Erasure in Java Explained](https://www.baeldung.com/java-type-erasure) - [Raw Types in Java](https://www.baeldung.com/raw-types-java) - [Super Type Tokens in Java Generics](https://www.baeldung.com/java-super-type-tokens) +- [Java Warning “unchecked conversion”](https://www.baeldung.com/java-unchecked-conversion) diff --git a/core-java-modules/core-java-lang-oop-generics/pom.xml b/core-java-modules/core-java-lang-oop-generics/pom.xml index 65a0aeac59..1a538edac9 100644 --- a/core-java-modules/core-java-lang-oop-generics/pom.xml +++ b/core-java-modules/core-java-lang-oop-generics/pom.xml @@ -13,4 +13,32 @@ core-java-lang-oop-generics jar + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + + 1.8 + 1.8 + + \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedconversion/UncheckedConversion.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedconversion/UncheckedConversion.java new file mode 100644 index 0000000000..9ad4a92077 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/uncheckedconversion/UncheckedConversion.java @@ -0,0 +1,45 @@ +package com.baeldung.uncheckedconversion; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public class UncheckedConversion { + public static List getRawList() { + List result = new ArrayList(); + result.add("I am the 1st String."); + result.add("I am the 2nd String."); + result.add("I am the 3rd String."); + return result; + } + + public static List getRawListWithMixedTypes() { + List result = new ArrayList(); + result.add("I am the 1st String."); + result.add("I am the 2nd String."); + result.add("I am the 3rd String."); + result.add(new Date()); + return result; + } + + public static List castList(Class clazz, Collection rawCollection) { + List result = new ArrayList<>(rawCollection.size()); + for (Object o : rawCollection) { + try { + result.add(clazz.cast(o)); + } catch (ClassCastException e) { + // log the exception or other error handling + } + } + return result; + } + + public static List castList2(Class clazz, Collection rawCollection) throws ClassCastException { + List result = new ArrayList<>(rawCollection.size()); + for (Object o : rawCollection) { + result.add(clazz.cast(o)); + } + return result; + } +} diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedconversion/UncheckedConversionUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedconversion/UncheckedConversionUnitTest.java new file mode 100644 index 0000000000..37b9a878d3 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/uncheckedconversion/UncheckedConversionUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.uncheckedconversion; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class UncheckedConversionUnitTest { + + @Test + public void givenRawList_whenAssignToTypedList_shouldHaveCompilerWarning() { + List fromRawList = UncheckedConversion.getRawList(); + Assert.assertEquals(3, fromRawList.size()); + Assert.assertEquals("I am the 1st String.", fromRawList.get(0)); + } + + @Test(expected = ClassCastException.class) + public void givenRawList_whenListHasMixedType_shouldThrowClassCastException() { + List fromRawList = UncheckedConversion.getRawListWithMixedTypes(); + Assert.assertEquals(4, fromRawList.size()); + Assert.assertFalse(fromRawList.get(3).endsWith("String.")); + } + + @Test + public void givenRawList_whenAssignToTypedListAfterCallingCastList_shouldOnlyHaveElementsWithExpectedType() { + List rawList = UncheckedConversion.getRawListWithMixedTypes(); + List strList = UncheckedConversion.castList(String.class, rawList); + Assert.assertEquals(4, rawList.size()); + Assert.assertEquals("One element with the wrong type has been filtered out.", 3, strList.size()); + Assert.assertTrue(strList.stream().allMatch(el -> el.endsWith("String."))); + } + + @Test(expected = ClassCastException.class) + public void givenRawListWithWrongType_whenAssignToTypedListAfterCallingCastList2_shouldThrowException() { + List rawList = UncheckedConversion.getRawListWithMixedTypes(); + UncheckedConversion.castList2(String.class, rawList); + } + +} diff --git a/core-java-modules/core-java-reflection-2/src/main/java/com/baeldung/reflection/check/abstractclass/InterfaceExample.java b/core-java-modules/core-java-reflection-2/src/main/java/com/baeldung/reflection/check/abstractclass/InterfaceExample.java new file mode 100644 index 0000000000..d226611084 --- /dev/null +++ b/core-java-modules/core-java-reflection-2/src/main/java/com/baeldung/reflection/check/abstractclass/InterfaceExample.java @@ -0,0 +1,4 @@ +package com.baeldung.reflection.check.abstractclass; + +public interface InterfaceExample { +} diff --git a/core-java-modules/core-java-reflection-2/src/test/java/com/baeldung/reflection/check/abstractclass/AbstractExampleUnitTest.java b/core-java-modules/core-java-reflection-2/src/test/java/com/baeldung/reflection/check/abstractclass/AbstractExampleUnitTest.java index cb5d927c23..d9a955ca6d 100644 --- a/core-java-modules/core-java-reflection-2/src/test/java/com/baeldung/reflection/check/abstractclass/AbstractExampleUnitTest.java +++ b/core-java-modules/core-java-reflection-2/src/test/java/com/baeldung/reflection/check/abstractclass/AbstractExampleUnitTest.java @@ -1,16 +1,36 @@ package com.baeldung.reflection.check.abstractclass; -import java.lang.reflect.Modifier; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.lang.reflect.Modifier; +import java.util.Date; + class AbstractExampleUnitTest { @Test - void givenAbstractClass_whenCheckModifierIsAbstract_thenTrue() throws Exception { + void givenAbstractClass_whenCheckModifierIsAbstract_thenTrue() { Class clazz = AbstractExample.class; Assertions.assertTrue(Modifier.isAbstract(clazz.getModifiers())); } + @Test + void givenInterface_whenCheckModifierIsAbstract_thenTrue() { + Class clazz = InterfaceExample.class; + Assertions.assertTrue(Modifier.isAbstract(clazz.getModifiers())); + } + + @Test + void givenAbstractClass_whenCheckIsAbstractClass_thenTrue() { + Class clazz = AbstractExample.class; + int mod = clazz.getModifiers(); + Assertions.assertTrue(Modifier.isAbstract(mod) && !Modifier.isInterface(mod)); + } + + @Test + void givenConcreteClass_whenCheckIsAbstractClass_thenFalse() { + Class clazz = Date.class; + int mod = clazz.getModifiers(); + Assertions.assertFalse(Modifier.isAbstract(mod) && !Modifier.isInterface(mod)); + } } diff --git a/core-java-modules/core-java-security-2/src/main/java/com/baeldung/egd/JavaSecurityEgdTester.java b/core-java-modules/core-java-security-2/src/main/java/com/baeldung/egd/JavaSecurityEgdTester.java new file mode 100644 index 0000000000..347d3b4cba --- /dev/null +++ b/core-java-modules/core-java-security-2/src/main/java/com/baeldung/egd/JavaSecurityEgdTester.java @@ -0,0 +1,23 @@ +package com.baeldung.egd; + +import java.security.SecureRandom; + +/** + * JavaSecurityEgdTester - run this with JVM parameter java.security.egd, e.g.: + * java -Djava.security.egd=file:/dev/urandom -cp . com.baeldung.egd.JavaSecurityEgdTester + */ +public class JavaSecurityEgdTester { + public static final double NANOSECS = 1000000000.0; + public static final String JAVA_SECURITY_EGD = "java.security.egd"; + + public static void main(String[] args) { + SecureRandom secureRandom = new SecureRandom(); + long start = System.nanoTime(); + byte[] randomBytes = new byte[256]; + secureRandom.nextBytes(randomBytes); + double duration = (System.nanoTime() - start) / NANOSECS; + + String message = String.format("java.security.egd=%s took %.3f seconds and used the %s algorithm", System.getProperty(JAVA_SECURITY_EGD), duration, secureRandom.getAlgorithm()); + System.out.println(message); + } +} diff --git a/java-collections-maps-3/README.md b/java-collections-maps-3/README.md index 39ac8575fa..bd1029c9cf 100644 --- a/java-collections-maps-3/README.md +++ b/java-collections-maps-3/README.md @@ -2,3 +2,4 @@ - [Java Map With Case-Insensitive Keys](https://www.baeldung.com/java-map-with-case-insensitive-keys) - [Using a Byte Array as Map Key in Java](https://www.baeldung.com/java-map-key-byte-array) +- [Using the Map.Entry Java Class](https://www.baeldung.com/java-map-entry) diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/entry/Book.java b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/Book.java new file mode 100644 index 0000000000..7e47e22908 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/Book.java @@ -0,0 +1,35 @@ +package com.baeldung.map.entry; + +public class Book { + private String title; + private String author; + + public Book(String title, String author) { + this.title = title; + this.author = author; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + @Override + public String toString() { + return "Book{" + + "title='" + title + '\'' + + ", author='" + author + '\'' + + '}'; + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryEfficiencyExample.java b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryEfficiencyExample.java new file mode 100644 index 0000000000..d64bcb38df --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryEfficiencyExample.java @@ -0,0 +1,34 @@ +package com.baeldung.map.entry; + +import java.util.HashMap; +import java.util.Map; + +public class MapEntryEfficiencyExample { + + public static void main(String[] args) { + MapEntryEfficiencyExample mapEntryEfficiencyExample = new MapEntryEfficiencyExample(); + Map map = new HashMap<>(); + + map.put("Robert C. Martin", "Clean Code"); + map.put("Joshua Bloch", "Effective Java"); + + System.out.println("Iterating Using Map.KeySet - 2 operations"); + mapEntryEfficiencyExample.usingKeySet(map); + + System.out.println("Iterating Using Map.Entry - 1 operation"); + mapEntryEfficiencyExample.usingEntrySet(map); + + } + + public void usingKeySet(Map bookMap) { + for (String key : bookMap.keySet()) { + System.out.println("key: " + key + " value: " + bookMap.get(key)); + } + } + + public void usingEntrySet(Map bookMap) { + for (Map.Entry book: bookMap.entrySet()) { + System.out.println("key: " + book.getKey() + " value: " + book.getValue()); + } + } +} diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryTupleExample.java b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryTupleExample.java new file mode 100644 index 0000000000..edcbd263fe --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/entry/MapEntryTupleExample.java @@ -0,0 +1,25 @@ +package com.baeldung.map.entry; + +import java.util.*; + +public class MapEntryTupleExample { + + public static void main(String[] args) { + Map.Entry tuple1; + Map.Entry tuple2; + Map.Entry tuple3; + + tuple1 = new AbstractMap.SimpleEntry<>("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch")); + tuple2 = new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin")); + tuple3 = new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin")); + + List> orderedTuples = new ArrayList<>(); + orderedTuples.add(tuple1); + orderedTuples.add(tuple2); + orderedTuples.add(tuple3); + + for (Map.Entry tuple : orderedTuples) { + System.out.println("key: " + tuple.getKey() + " value: " + tuple.getValue()); + } + } +} diff --git a/java-collections-maps-3/src/test/java/com/baeldung/map/entry/MapEntryUnitTest.java b/java-collections-maps-3/src/test/java/com/baeldung/map/entry/MapEntryUnitTest.java new file mode 100644 index 0000000000..7340558023 --- /dev/null +++ b/java-collections-maps-3/src/test/java/com/baeldung/map/entry/MapEntryUnitTest.java @@ -0,0 +1,33 @@ +package com.baeldung.map.entry; + +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class MapEntryUnitTest { + + @Test + public void givenSimpleEntryList_whenAddDuplicateKey_thenDoesNotOverwriteExistingKey() { + List> orderedTuples = new ArrayList<>(); + orderedTuples.add(new AbstractMap.SimpleEntry<>("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch"))); + orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin"))); + orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", new Book("Clean Code", "Robert C Martin"))); + + assertEquals(3, orderedTuples.size()); + assertEquals("9780134685991", orderedTuples.get(0).getKey()); + assertEquals("9780132350884", orderedTuples.get(1).getKey()); + assertEquals("9780132350884", orderedTuples.get(2).getKey()); + } + + @Test + public void givenRegularMap_whenAddDuplicateKey_thenOverwritesExistingKey() { + Map entries = new HashMap<>(); + entries.put("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch")); + entries.put("9780132350884", new Book("Clean Code", "Robert C Martin")); + entries.put("9780132350884", new Book("Clean Code", "Robert C Martin")); + + assertEquals(2, entries.size()); + } +} diff --git a/libraries-http/pom.xml b/libraries-http/pom.xml index 74e00a7291..257cb988d6 100644 --- a/libraries-http/pom.xml +++ b/libraries-http/pom.xml @@ -120,7 +120,7 @@ 4.5.3 3.6.2 - 3.14.2 + 4.9.1 1.23.0 2.2.0 2.3.0 diff --git a/performance-tests/README.md b/performance-tests/README.md index 27c0363010..09bf6dba1f 100644 --- a/performance-tests/README.md +++ b/performance-tests/README.md @@ -6,6 +6,7 @@ This module contains articles about performance testing. - [Performance of Java Mapping Frameworks](https://www.baeldung.com/java-performance-mapping-frameworks) - [Performance Effects of Exceptions in Java](https://www.baeldung.com/java-exceptions-performance) +- [Is Java a Compiled or Interpreted Language?](https://www.baeldung.com/java-compiled-interpreted) ### Running diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/zoneddatetime/config/MongoConfig.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/zoneddatetime/config/MongoConfig.java index 3cfefa099c..4eb3872e34 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/zoneddatetime/config/MongoConfig.java +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/zoneddatetime/config/MongoConfig.java @@ -1,11 +1,8 @@ package com.baeldung.zoneddatetime.config; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; @@ -13,12 +10,7 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie import com.baeldung.zoneddatetime.converter.ZonedDateTimeReadConverter; import com.baeldung.zoneddatetime.converter.ZonedDateTimeWriteConverter; -import com.mongodb.ConnectionString; -import com.mongodb.MongoClientSettings; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -@Configuration @EnableMongoRepositories(basePackages = { "com.baeldung" }) public class MongoConfig extends AbstractMongoClientConfiguration { @@ -29,20 +21,6 @@ public class MongoConfig extends AbstractMongoClientConfiguration { return "test"; } - @Override - public MongoClient mongoClient() { - final ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test"); - final MongoClientSettings mongoClientSettings = MongoClientSettings.builder() - .applyConnectionString(connectionString) - .build(); - return MongoClients.create(mongoClientSettings); - } - - @Override - public Collection getMappingBasePackages() { - return Collections.singleton("com.baeldung"); - } - @Override public MongoCustomConversions customConversions() { converters.add(new ZonedDateTimeReadConverter()); diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/mongoConfig.xml b/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/mongoConfig.xml index c5b9068de3..7a10ef6a69 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/mongoConfig.xml +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/mongoConfig.xml @@ -24,8 +24,8 @@ - - + + \ No newline at end of file diff --git a/spring-5-reactive-client/README.md b/spring-5-reactive-client/README.md index eebdc23aed..b247a1669b 100644 --- a/spring-5-reactive-client/README.md +++ b/spring-5-reactive-client/README.md @@ -11,3 +11,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Logging Spring WebClient Calls](https://www.baeldung.com/spring-log-webclient-calls) - [Mocking a WebClient in Spring](https://www.baeldung.com/spring-mocking-webclient) - [Spring WebClient Filters](https://www.baeldung.com/spring-webclient-filters) +- [Get List of JSON Objects with WebClient](https://www.baeldung.com/spring-webclient-json-list) diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java new file mode 100644 index 0000000000..17676b3f33 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java @@ -0,0 +1,18 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; + +import java.util.List; + +public interface ReaderConsumerService { + + List processReaderDataFromObjectArray(); + + List processReaderDataFromReaderArray(); + + List processReaderDataFromReaderList(); + + List processNestedReaderDataFromReaderArray(); + + List processNestedReaderDataFromReaderList(); +} diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java new file mode 100644 index 0000000000..8f1a4c019a --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java @@ -0,0 +1,90 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; +import com.baeldung.webclient.json.model.Reader; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ReaderConsumerServiceImpl implements ReaderConsumerService { + + private final WebClient webClient; + private static final ObjectMapper mapper = new ObjectMapper(); + + public ReaderConsumerServiceImpl(WebClient webClient) { + this.webClient = webClient; + } + @Override + public List processReaderDataFromObjectArray() { + Mono response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Object[].class).log(); + Object[] objects = response.block(); + return Arrays.stream(objects) + .map(object -> mapper.convertValue(object, Reader.class)) + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processReaderDataFromReaderArray() { + Mono response = + webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Reader[].class).log(); + + Reader[] readers = response.block(); + return Arrays.stream(readers) + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processReaderDataFromReaderList() { + Mono> response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}); + List readers = response.block(); + + return readers.stream() + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processNestedReaderDataFromReaderArray() { + Mono response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Reader[].class).log(); + Reader[] readers = response.block(); + + return Arrays.stream(readers) + .flatMap(reader -> reader.getBooksRead().stream()) + .map(Book::getAuthor) + .collect(Collectors.toList()); + } + + @Override + public List processNestedReaderDataFromReaderList() { + Mono> response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}); + + List readers = response.block(); + return readers.stream() + .flatMap(reader -> reader.getBooksRead().stream()) + .map(Book::getAuthor) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java new file mode 100644 index 0000000000..cb3fb258d4 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java @@ -0,0 +1,22 @@ +package com.baeldung.webclient.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Book { + private final String author; + private final String title; + + @JsonCreator + public Book( + @JsonProperty("author") String author, + @JsonProperty("title") String title) { + this.author = author; + this.title = title; + } + public String getAuthor() { + return this.author; + } +} diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java new file mode 100644 index 0000000000..7d02853ea1 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java @@ -0,0 +1,33 @@ +package com.baeldung.webclient.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Reader { + private final int id; + private final String name; + private final Book favouriteBook; + private final List booksRead; + + @JsonCreator + public Reader( + @JsonProperty("id") int id, + @JsonProperty("name") String name, + @JsonProperty("favouriteBook") Book favouriteBook, + @JsonProperty("booksRead") List booksRead) { + this.id = id; + this.name = name; + this.favouriteBook = favouriteBook; + this.booksRead =booksRead; + } + + public Book getFavouriteBook() { + return favouriteBook; + } + + public List getBooksRead() { return booksRead; } +} diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java b/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java new file mode 100644 index 0000000000..51f0a5c1bb --- /dev/null +++ b/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java @@ -0,0 +1,88 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasProperty; + +public class ReaderConsumerServiceImplUnitTest { + + private static String READER_JSON = "[{\"id\":1,\"name\":\"reader1\",\"favouriteBook\":{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}," + + "\"booksRead\":[{\"author\":\"Charles Dickens\",\"title\":\"Oliver Twist\"},{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}]}," + + "{\"id\":2,\"name\":\"reader2\",\"favouriteBook\":{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}," + + "\"booksRead\":[{\"author\":\"J.R.R. Tolkien\",\"title\":\"Lord of the Rings\"}, " + + "{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}]}]"; + + private static String BASE_URL = "http://localhost:8080/readers"; + + WebClient webClientMock = WebClient.builder().baseUrl(BASE_URL) + .exchangeFunction(clientRequest -> Mono.just(ClientResponse.create(HttpStatus.OK) + .header("content-type", "application/json") + .body(READER_JSON) + .build())) + .build(); + + private final ReaderConsumerService tested = new ReaderConsumerServiceImpl(webClientMock); + + @Test + void when_processReaderDataFromObjectArray_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromObjectArray(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + } + + @Test + void when_processReaderDataFromReaderArray_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromReaderArray(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + } + + @Test + void when_processReaderDataFromReaderList_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromReaderList(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + + } + + @Test + void when_processNestedReaderDataFromReaderArray_then_OK() { + List expected = Arrays.asList( + "Milan Kundera", + "Charles Dickens", + "J.R.R. Tolkien", + "Douglas Adams"); + + List actual = tested.processNestedReaderDataFromReaderArray(); + assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3))); + } + + @Test + void when_processNestedReaderDataFromReaderList_then_OK() { + List expected = Arrays.asList( + "Milan Kundera", + "Charles Dickens", + "J.R.R. Tolkien", + "Douglas Adams"); + + List actual = tested.processNestedReaderDataFromReaderList(); + assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3))); + } +} \ No newline at end of file diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml index 60c8b90e16..40791faaaf 100644 --- a/spring-5-reactive/pom.xml +++ b/spring-5-reactive/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 spring-5-reactive 0.0.1-SNAPSHOT @@ -73,6 +74,12 @@ spring-security-test test + + io.projectreactor + reactor-test + test + + diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java index c4f8c9f41f..2b415d5f1e 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java @@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicLong; import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; import static org.springframework.web.reactive.function.BodyExtractors.toFormData; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.ServerResponse.ok; public class FormHandler { @@ -29,7 +29,7 @@ public class FormHandler { Mono handleUpload(ServerRequest request) { return request.body(toDataBuffers()) .collectList() - .flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString()))); + .flatMap(dataBuffers -> ok().body(fromValue(extractData(dataBuffers).toString()))); } private AtomicLong extractData(List dataBuffers) { diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java index 9cbc1b7669..9bfd0afe7e 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java +++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java @@ -1,6 +1,6 @@ package com.baeldung.functional; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; import static org.springframework.web.reactive.function.server.RequestPredicates.path; @@ -44,7 +44,7 @@ public class FunctionalSpringBootApplication { .doOnNext(actors::add) .then(ok().build())); - return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))) .andRoute(POST("/login"), formHandler::handleLogin) .andRoute(POST("/upload"), formHandler::handleUpload) .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java index b89f74ad92..9930ffb474 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java +++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java @@ -1,6 +1,6 @@ package com.baeldung.functional; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; @@ -42,7 +42,7 @@ public class FunctionalWebApplication { .doOnNext(actors::add) .then(ok().build())); - return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin) + return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin) .andRoute(POST("/upload"), formHandler::handleUpload) .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) .andNest(accept(MediaType.APPLICATION_JSON), restfulRouter) diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java b/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java index 8fe24821de..6c36b7fa03 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java +++ b/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java @@ -2,7 +2,7 @@ package com.baeldung.functional; import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; import static org.springframework.web.reactive.function.BodyExtractors.toFormData; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; import static org.springframework.web.reactive.function.server.RequestPredicates.path; @@ -46,7 +46,7 @@ public class RootServlet extends ServletHttpHandlerAdapter { private static RouterFunction routingFunction() { - return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData()) + return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData()) .map(MultiValueMap::toSingleValueMap) .map(formData -> { System.out.println("form data: " + formData.toString()); @@ -65,7 +65,7 @@ public class RootServlet extends ServletHttpHandlerAdapter { dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer() .array().length)); System.out.println("data length:" + atomicLong.get()); - return ok().body(fromObject(atomicLong.toString())) + return ok().body(fromValue(atomicLong.toString())) .block(); })) .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java index 051e4b8df5..4f3f1795da 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java @@ -2,7 +2,8 @@ package com.baeldung.reactive.errorhandling; import java.util.Map; -import org.springframework.boot.autoconfigure.web.ResourceProperties; + +import org.springframework.boot.autoconfigure.web.WebProperties; import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler; import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.reactive.error.ErrorAttributes; @@ -18,6 +19,7 @@ import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; + import reactor.core.publisher.Mono; @Component @@ -26,7 +28,7 @@ public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHan public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) { - super(g, new ResourceProperties(), applicationContext); + super(g, new WebProperties.Resources(), applicationContext); super.setMessageWriters(serverCodecConfigurer.getWriters()); super.setMessageReaders(serverCodecConfigurer.getReaders()); } @@ -41,8 +43,8 @@ public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHan final Map errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults()); return ServerResponse.status(HttpStatus.BAD_REQUEST) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .body(BodyInserters.fromObject(errorPropertiesMap)); + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(errorPropertiesMap)); } } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java index 87b78a4654..c71c8ecac0 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java @@ -14,7 +14,7 @@ public class Handler1 { return sayHello(request).onErrorReturn("Hello, Stranger") .flatMap(s -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) - .syncBody(s)); + .bodyValue(s)); } private Mono sayHello(ServerRequest request) { diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java index 12172a0f54..92e881543e 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java @@ -15,11 +15,11 @@ public Mono handleRequest2(ServerRequest request) { sayHello(request) .flatMap(s -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) - .syncBody(s)) + .bodyValue(s)) .onErrorResume(e -> sayHelloFallback() .flatMap(s -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) - .syncBody(s))); + .bodyValue(s))); } private Mono sayHello(ServerRequest request) { diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java index e95b039cce..8c988a6633 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java @@ -15,11 +15,11 @@ public class Handler3 { sayHello(request) .flatMap(s -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) - .syncBody(s)) + .bodyValue(s)) .onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " + e.getMessage())).flatMap(s -> ServerResponse.ok() .contentType(MediaType.TEXT_PLAIN) - .syncBody(s))); + .bodyValue(s))); } private Mono sayHello(ServerRequest request) { diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/ExploreSpring5URLPatternUsingRouterFunctions.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/ExploreSpring5URLPatternUsingRouterFunctions.java index 115a057915..34abada2f1 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/ExploreSpring5URLPatternUsingRouterFunctions.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/ExploreSpring5URLPatternUsingRouterFunctions.java @@ -1,6 +1,6 @@ package com.baeldung.reactive.urlmatch; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RouterFunctions.route; import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; @@ -24,10 +24,10 @@ public class ExploreSpring5URLPatternUsingRouterFunctions { private RouterFunction routingFunction() { - return route(GET("/p?ths"), serverRequest -> ok().body(fromObject("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id")))) - .andRoute(GET("/*card"), serverRequest -> ok().body(fromObject("/*card path was accessed"))) - .andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2")))) - .andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromObject("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung")))) + return route(GET("/p?ths"), serverRequest -> ok().body(fromValue("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id")))) + .andRoute(GET("/*card"), serverRequest -> ok().body(fromValue("/*card path was accessed"))) + .andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromValue(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2")))) + .andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromValue("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung")))) .and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/"))); } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FormHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FormHandler.java index 0781230379..7b1fb06459 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FormHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FormHandler.java @@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicLong; import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; import static org.springframework.web.reactive.function.BodyExtractors.toFormData; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.ServerResponse.ok; public class FormHandler { @@ -29,7 +29,7 @@ public class FormHandler { Mono handleUpload(ServerRequest request) { return request.body(toDataBuffers()) .collectList() - .flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString()))); + .flatMap(dataBuffers -> ok().body(fromValue(extractData(dataBuffers).toString()))); } private AtomicLong extractData(List dataBuffers) { diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FunctionalWebApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FunctionalWebApplication.java index 2ea5420a2b..6cec902a0d 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FunctionalWebApplication.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/urlmatch/FunctionalWebApplication.java @@ -1,6 +1,6 @@ package com.baeldung.reactive.urlmatch; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; import static org.springframework.web.reactive.function.server.RequestPredicates.path; @@ -40,7 +40,7 @@ public class FunctionalWebApplication { .doOnNext(actors::add) .then(ok().build())); - return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin) + return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin) .andRoute(POST("/upload"), formHandler::handleUpload) .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) .andNest(path("/actor"), restfulRouter) diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java new file mode 100644 index 0000000000..c6e3678832 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/Foo.java @@ -0,0 +1,24 @@ +package com.baeldung.web.reactive.client; + +public class Foo { + + private String name; + + public Foo() { + super(); + } + + public Foo(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java index f104ad30f1..aa9b81de4f 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java +++ b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientApplication.java @@ -2,9 +2,10 @@ package com.baeldung.web.reactive.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; -@SpringBootApplication -public class WebClientApplication{ +@SpringBootApplication(exclude = { ReactiveSecurityAutoConfiguration.class }) +public class WebClientApplication { public static void main(String[] args) { SpringApplication.run(WebClientApplication.class, args); diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java index 2d42e848b2..1a91001807 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java +++ b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java @@ -1,36 +1,18 @@ package com.baeldung.web.reactive.client; -import java.net.URI; -import java.nio.charset.Charset; -import java.time.ZonedDateTime; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ReactiveHttpOutputMessage; -import org.springframework.http.client.reactive.ClientHttpRequest; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; -import io.netty.channel.ChannelOption; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.netty.handler.timeout.WriteTimeoutHandler; import reactor.core.publisher.Mono; -import reactor.netty.http.client.HttpClient; @RestController public class WebClientController { @@ -43,74 +25,18 @@ public class WebClientController { return response; } - public void demonstrateWebClient() { - // request - WebClient.UriSpec request1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST); - WebClient.UriSpec request2 = createWebClientWithServerURLAndDefaultValues().post(); - - // request body specifications - WebClient.RequestBodySpec uri1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST) - .uri("/resource"); - WebClient.RequestBodySpec uri2 = createWebClientWithServerURLAndDefaultValues().post() - .uri(URI.create("/resource")); - - // request header specification - WebClient.RequestHeadersSpec requestSpec1 = uri1.body(BodyInserters.fromPublisher(Mono.just("data"), String.class)); - WebClient.RequestHeadersSpec requestSpec2 = uri2.body(BodyInserters.fromValue("data")); - - // inserters - BodyInserter, ReactiveHttpOutputMessage> inserter1 = BodyInserters.fromPublisher(Subscriber::onComplete, String.class); - - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - map.add("key1", "value1"); - map.add("key2", "value2"); - - BodyInserter, ClientHttpRequest> inserter2 = BodyInserters.fromMultipartData(map); - BodyInserter inserter3 = BodyInserters.fromValue(new Object()); - BodyInserter inserter4 = BodyInserters.fromValue("body"); - - // responses - WebClient.ResponseSpec response1 = uri1.body(inserter3) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) - .acceptCharset(Charset.forName("UTF-8")) - .ifNoneMatch("*") - .ifModifiedSince(ZonedDateTime.now()) - .retrieve(); - String response2 = uri1.exchangeToMono(response -> response.bodyToMono(String.class)) - .block(); - String response3 = uri2.retrieve() - .bodyToMono(String.class) - .block(); - WebClient.ResponseSpec response4 = requestSpec2.retrieve(); + @PostMapping("/resource") + public Mono postStringResource(@RequestBody Mono bodyString) { + return bodyString.map(body -> "processed-" + body); } - private WebClient createWebClient() { - return WebClient.create(); + @PostMapping("/resource-foo") + public Mono postFooResource(@RequestBody Mono bodyFoo) { + return bodyFoo.map(foo -> "processedFoo-" + foo.getName()); } - private WebClient createWebClientWithServerURL() { - return WebClient.create("http://localhost:8081"); + @PostMapping(value = "/resource-multipart", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String handleFormUpload(@RequestPart("key1") String value1, @RequestPart("key2") String value2) { + return "processed-" + value1 + "-" + value2; } - - private WebClient createWebClientConfiguringTimeout() { - HttpClient httpClient = HttpClient.create() - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) - .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)) - .addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS))); - - return WebClient.builder() - .clientConnector(new ReactorClientHttpConnector(httpClient)) - .build(); - } - - private WebClient createWebClientWithServerURLAndDefaultValues() { - return WebClient.builder() - .baseUrl("http://localhost:8081") - .defaultCookie("cookieKey", "cookieValue") - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080")) - .build(); - } - } diff --git a/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java index 5c0b4f69d0..3164adbe4a 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java @@ -1,17 +1,15 @@ package com.baeldung.functional; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.BodyInserters.fromResource; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.springframework.boot.web.server.WebServer; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -115,7 +113,7 @@ public class FunctionalWebApplicationIntegrationTest { client.post() .uri("/actor") - .body(fromObject(new Actor("Clint", "Eastwood"))) + .body(fromValue(new Actor("Clint", "Eastwood"))) .exchange() .expectStatus() .isOk(); diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java index 3bbbed0d77..38443a4eac 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingIntegrationTest.java @@ -133,7 +133,7 @@ public class ErrorHandlingIntegrationTest { .expectStatus() .isBadRequest() .expectHeader() - .contentType(MediaType.APPLICATION_JSON_UTF8) + .contentType(MediaType.APPLICATION_JSON) .expectBody() .jsonPath("$.message") .isNotEmpty() @@ -164,7 +164,7 @@ public class ErrorHandlingIntegrationTest { .expectStatus() .isBadRequest() .expectHeader() - .contentType(MediaType.APPLICATION_JSON_UTF8) + .contentType(MediaType.APPLICATION_JSON) .expectBody() .jsonPath("$.message") .isNotEmpty() diff --git a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java new file mode 100644 index 0000000000..39adf0b5c0 --- /dev/null +++ b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebClientIntegrationTest.java @@ -0,0 +1,331 @@ +package com.baeldung.web.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.codec.CodecException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ReactiveHttpOutputMessage; +import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; +import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec; +import org.springframework.web.reactive.function.client.WebClient.ResponseSpec; +import org.springframework.web.reactive.function.client.WebClientRequestException; + +import com.baeldung.web.reactive.client.Foo; +import com.baeldung.web.reactive.client.WebClientApplication; + +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.test.StepVerifier; + +@SpringBootTest(classes = WebClientApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) +public class WebClientIntegrationTest { + + @LocalServerPort + private int port; + + private static final String BODY_VALUE = "bodyValue"; + private static final ParameterizedTypeReference> MAP_RESPONSE_REF = new ParameterizedTypeReference>() { + }; + + @Test + public void givenDifferentWebClientCreationMethods_whenUsed_thenObtainExpectedResponse() { + // WebClient creation + WebClient client1 = WebClient.create(); + WebClient client2 = WebClient.create("http://localhost:" + port); + WebClient client3 = WebClient.builder() + .baseUrl("http://localhost:" + port) + .defaultCookie("cookieKey", "cookieValue") + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080")) + .build(); + + // response assertions + StepVerifier.create(retrieveResponse(client1.post() + .uri("http://localhost:" + port + "/resource"))) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(client2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(client3)) + .expectNext("processed-bodyValue") + .verifyComplete(); + // assert response without specifying URI + StepVerifier.create(retrieveResponse(client1)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + } + + @Test + public void givenDifferentMethodSpecifications_whenUsed_thenObtainExpectedResponse() { + // request specification + RequestBodyUriSpec uriSpecPost1 = createDefaultClient().method(HttpMethod.POST); + RequestBodyUriSpec uriSpecPost2 = createDefaultClient().post(); + RequestHeadersUriSpec requestGet = createDefaultClient().get(); + + // response assertions + StepVerifier.create(retrieveResponse(uriSpecPost1)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(uriSpecPost2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveGetResponse(requestGet)) + .expectNextMatches(nextMap -> nextMap.get("field") + .equals("value")) + .verifyComplete(); + } + + @Test + public void givenDifferentUriSpecifications_whenUsed_thenObtainExpectedResponse() { + // uri specification + RequestBodySpec bodySpecUsingString = createDefaultPostRequest().uri("/resource"); + RequestBodySpec bodySpecUsingUriBuilder = createDefaultPostRequest().uri(uriBuilder -> uriBuilder.pathSegment("resource") + .build()); + RequestBodySpec bodySpecusingURI = createDefaultPostRequest().uri(URI.create("http://localhost:" + port + "/resource")); + RequestBodySpec bodySpecOverridenBaseUri = createDefaultPostRequest().uri(URI.create("/resource")); + RequestBodySpec bodySpecOverridenBaseUri2 = WebClient.builder() + .baseUrl("http://localhost:" + port) + .build() + .post() + .uri(URI.create("/resource")); + + // response assertions + StepVerifier.create(retrieveResponse(bodySpecUsingString)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(bodySpecUsingUriBuilder)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(bodySpecusingURI)) + .expectNext("processed-bodyValue") + .verifyComplete(); + // assert sending request overriding base URI + StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + StepVerifier.create(retrieveResponse(bodySpecOverridenBaseUri2)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ex.getMessage() + .contains("Connection refused")) + .verify(); + } + + @Test + public void givenDifferentBodySpecifications_whenUsed_thenObtainExpectedResponse() { + // request body specifications + RequestHeadersSpec headersSpecPost1 = createDefaultPostResourceRequest().body(BodyInserters.fromPublisher(Mono.just(BODY_VALUE), String.class)); + RequestHeadersSpec headersSpecPost2 = createDefaultPostResourceRequest().body(BodyInserters.fromValue(BODY_VALUE)); + RequestHeadersSpec headersSpecPost3 = createDefaultPostResourceRequest().bodyValue(BODY_VALUE); + RequestHeadersSpec headersSpecFooPost = createDefaultPostRequest().uri("/resource-foo") + .body(Mono.just(new Foo("fooName")), Foo.class); + BodyInserter inserterPlainObject = BodyInserters.fromValue(new Object()); + RequestHeadersSpec headersSpecPlainObject = createDefaultPostResourceRequest().body(inserterPlainObject); + + // request body specifications - using other inserter method (multipart request) + LinkedMultiValueMap map = new LinkedMultiValueMap<>(); + map.add("key1", "multipartValue1"); + map.add("key2", "multipartValue2"); + BodyInserter, ClientHttpRequest> inserterMultipart = BodyInserters.fromMultipartData(map); + RequestHeadersSpec headersSpecInserterMultipart = createDefaultPostRequest().uri("/resource-multipart") + .body(inserterMultipart); + + // response assertions + StepVerifier.create(retrieveResponse(headersSpecPost1)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecPost2)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecPost3)) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecFooPost)) + .expectNext("processedFoo-fooName") + .verifyComplete(); + StepVerifier.create(retrieveResponse(headersSpecInserterMultipart)) + .expectNext("processed-multipartValue1-multipartValue2") + .verifyComplete(); + // assert error plain `new Object()` as request body + StepVerifier.create(retrieveResponse(headersSpecPlainObject)) + .expectError(CodecException.class) + .verify(); + // assert response for request with no body + Mono> responsePostWithNoBody = createDefaultPostResourceRequest().exchangeToMono(responseHandler -> { + assertThat(responseHandler.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + return responseHandler.bodyToMono(MAP_RESPONSE_REF); + }); + StepVerifier.create(responsePostWithNoBody) + .expectNextMatches(nextMap -> nextMap.get("error") + .equals("Bad Request")) + .verifyComplete(); + } + + @Test + public void givenPostSpecifications_whenHeadersAdded_thenObtainExpectedResponse() { + // request header specification + RequestHeadersSpec headersSpecInserterStringWithHeaders = createDefaultPostResourceRequestResponse().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) + .acceptCharset(StandardCharsets.UTF_8) + .ifNoneMatch("*") + .ifModifiedSince(ZonedDateTime.now()); + + // response assertions + StepVerifier.create(retrieveResponse(headersSpecInserterStringWithHeaders)) + .expectNext("processed-bodyValue") + .verifyComplete(); + } + + @Test + public void givenDifferentResponseSpecifications_whenUsed_thenObtainExpectedResponse() { + ResponseSpec responseSpecPostString = createDefaultPostResourceRequestResponse().retrieve(); + Mono responsePostString = responseSpecPostString.bodyToMono(String.class); + Mono responsePostString2 = createDefaultPostResourceRequestResponse().exchangeToMono(response -> { + if (response.statusCode() + .equals(HttpStatus.OK)) { + return response.bodyToMono(String.class); + } else if (response.statusCode() + .is4xxClientError()) { + return Mono.just("Error response"); + } else { + return response.createException() + .flatMap(Mono::error); + } + }); + Mono responsePostNoBody = createDefaultPostResourceRequest().exchangeToMono(response -> { + if (response.statusCode() + .equals(HttpStatus.OK)) { + return response.bodyToMono(String.class); + } else if (response.statusCode() + .is4xxClientError()) { + return Mono.just("Error response"); + } else { + return response.createException() + .flatMap(Mono::error); + } + }); + Mono> responseGet = createDefaultClient().get() + .uri("/resource") + .retrieve() + .bodyToMono(MAP_RESPONSE_REF); + + // response assertions + StepVerifier.create(responsePostString) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(responsePostString2) + .expectNext("processed-bodyValue") + .verifyComplete(); + StepVerifier.create(responsePostNoBody) + .expectNext("Error response") + .verifyComplete(); + StepVerifier.create(responseGet) + .expectNextMatches(nextMap -> nextMap.get("field") + .equals("value")) + .verifyComplete(); + } + + @Test + public void givenWebClientWithTimeoutConfigurations_whenRequestUsingWronglyConfiguredPublisher_thenObtainTimeout() { + HttpClient httpClient = HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) + .responseTimeout(Duration.ofMillis(1000)) + .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(1000, TimeUnit.MILLISECONDS)) + .addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS))); + + WebClient timeoutClient = WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); + + BodyInserter, ReactiveHttpOutputMessage> inserterCompleteSuscriber = BodyInserters.fromPublisher(Subscriber::onComplete, String.class); + RequestHeadersSpec headersSpecInserterCompleteSuscriber = timeoutClient.post() + .uri("/resource") + .body(inserterCompleteSuscriber); + + StepVerifier.create(headersSpecInserterCompleteSuscriber.retrieve() + .bodyToMono(String.class)) + .expectTimeout(Duration.ofMillis(2000)) + .verify(); + } + + // helper methods to create default instances + private WebClient createDefaultClient() { + return WebClient.create("http://localhost:" + port); + } + + private RequestBodyUriSpec createDefaultPostRequest() { + return createDefaultClient().post(); + } + + private RequestBodySpec createDefaultPostResourceRequest() { + return createDefaultPostRequest().uri("/resource"); + } + + private RequestHeadersSpec createDefaultPostResourceRequestResponse() { + return createDefaultPostResourceRequest().bodyValue(BODY_VALUE); + } + + // helper methods to retrieve a response based on different steps of the process (specs) + private Mono retrieveResponse(WebClient client) { + return client.post() + .uri("/resource") + .bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono retrieveResponse(RequestBodyUriSpec spec) { + return spec.uri("/resource") + .bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono> retrieveGetResponse(RequestHeadersUriSpec spec) { + return spec.uri("/resource") + .retrieve() + .bodyToMono(MAP_RESPONSE_REF); + } + + private Mono retrieveResponse(RequestBodySpec spec) { + return spec.bodyValue(BODY_VALUE) + .retrieve() + .bodyToMono(String.class); + } + + private Mono retrieveResponse(RequestHeadersSpec spec) { + return spec.retrieve() + .bodyToMono(String.class); + } +} diff --git a/spring-aop/README.md b/spring-aop/README.md index 91cccbd114..c92e132d1e 100644 --- a/spring-aop/README.md +++ b/spring-aop/README.md @@ -11,3 +11,4 @@ This module contains articles about Spring aspect oriented programming (AOP) - [Introduction to Pointcut Expressions in Spring](https://www.baeldung.com/spring-aop-pointcut-tutorial) - [Introduction to Advice Types in Spring](https://www.baeldung.com/spring-aop-advice-tutorial) - [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception) +- [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info) diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index ee088c357a..263d2af089 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -62,6 +62,7 @@ spring-boot-properties-3 spring-boot-property-exp spring-boot-runtime + spring-boot-runtime-2 spring-boot-security spring-boot-springdoc spring-boot-swagger diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml index 18da6d3a9a..a808b8cb1b 100644 --- a/spring-boot-modules/spring-boot-actuator/pom.xml +++ b/spring-boot-modules/spring-boot-actuator/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.0.RELEASE + 2.3.2.RELEASE diff --git a/spring-boot-modules/spring-boot-actuator/src/main/resources/application.properties b/spring-boot-modules/spring-boot-actuator/src/main/resources/application.properties index 00100d6d97..de7be417a8 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-actuator/src/main/resources/application.properties @@ -1,4 +1,6 @@ -management.health.probes.enabled=true +management.endpoint.health.probes.enabled=true +management.health.livenessState.enabled=true +management.health.readinessState.enabled=true management.endpoint.health.show-details=always management.endpoint.health.status.http-mapping.down=500 management.endpoint.health.status.http-mapping.out_of_service=503 @@ -8,4 +10,4 @@ management.endpoint.health.status.http-mapping.warning=500 info.app.name=Spring Sample Application info.app.description=This is my first spring boot application G1 info.app.version=1.0.0 -info.java-vendor = ${java.specification.vendor} \ No newline at end of file +info.java-vendor = ${java.specification.vendor} diff --git a/spring-boot-modules/spring-boot-annotations/README.md b/spring-boot-modules/spring-boot-annotations/README.md index 6ead94de86..1b2bca435c 100644 --- a/spring-boot-modules/spring-boot-annotations/README.md +++ b/spring-boot-modules/spring-boot-annotations/README.md @@ -10,3 +10,4 @@ This module contains articles about Spring Boot annotations - [Spring Core Annotations](https://www.baeldung.com/spring-core-annotations) - [Spring Bean Annotations](https://www.baeldung.com/spring-bean-annotations) - [Difference Between @ComponentScan and @EnableAutoConfiguration in Spring Boot](https://www.baeldung.com/spring-componentscan-vs-enableautoconfiguration) +- [Where Should the Spring @Service Annotation Be Kept?](https://www.baeldung.com/spring-service-annotation-placement) diff --git a/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/AuthApplication.java b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/AuthApplication.java new file mode 100644 index 0000000000..fa5770b08e --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/AuthApplication.java @@ -0,0 +1,22 @@ +package com.baeldung.annotations.service; + +import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService; +import com.baeldung.annotations.service.interfaces.AuthenticationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AuthApplication { + + @Autowired + private AuthenticationService inMemoryAuthService; + + @Autowired + private AbstractAuthenticationService ldapAuthService; + + public static void main(String[] args) { + SpringApplication.run(AuthApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/abstracts/AbstractAuthenticationService.java b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/abstracts/AbstractAuthenticationService.java new file mode 100644 index 0000000000..47fac229f7 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/abstracts/AbstractAuthenticationService.java @@ -0,0 +1,12 @@ +package com.baeldung.annotations.service.abstracts; + +import org.springframework.stereotype.Service; + +@Service +public abstract class AbstractAuthenticationService { + + public boolean authenticate(String username, String password) { + return false; + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/InMemoryAuthenticationService.java b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/InMemoryAuthenticationService.java new file mode 100644 index 0000000000..8f80cb8593 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/InMemoryAuthenticationService.java @@ -0,0 +1,14 @@ +package com.baeldung.annotations.service.concretes; + +import com.baeldung.annotations.service.interfaces.AuthenticationService; +import org.springframework.stereotype.Service; + +@Service +public class InMemoryAuthenticationService implements AuthenticationService { + + @Override + public boolean authenticate(String username, String password) { + return false; + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/LdapAuthenticationService.java b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/LdapAuthenticationService.java new file mode 100644 index 0000000000..af93ea13be --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/concretes/LdapAuthenticationService.java @@ -0,0 +1,14 @@ +package com.baeldung.annotations.service.concretes; + +import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService; +import org.springframework.stereotype.Service; + +@Service +public class LdapAuthenticationService extends AbstractAuthenticationService { + + @Override + public boolean authenticate(String username, String password) { + return true; + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/interfaces/AuthenticationService.java b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/interfaces/AuthenticationService.java new file mode 100644 index 0000000000..0537343266 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/main/java/com/baeldung/annotations/service/interfaces/AuthenticationService.java @@ -0,0 +1,10 @@ +package com.baeldung.annotations.service.interfaces; + +import org.springframework.stereotype.Service; + +@Service +public interface AuthenticationService { + + boolean authenticate(String username, String password); + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-annotations/src/main/test/com.baeldung.annotations/EmployeeApplicationTest.java b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/EmployeeApplicationUnitTest.java similarity index 96% rename from spring-boot-modules/spring-boot-annotations/src/main/test/com.baeldung.annotations/EmployeeApplicationTest.java rename to spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/EmployeeApplicationUnitTest.java index 66700cf781..8dfc743f3d 100644 --- a/spring-boot-modules/spring-boot-annotations/src/main/test/com.baeldung.annotations/EmployeeApplicationTest.java +++ b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/EmployeeApplicationUnitTest.java @@ -6,7 +6,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertAll; -public class EmployeeApplicationTest { +public class EmployeeApplicationUnitTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(EmployeeApplication.class); diff --git a/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/AuthApplicationUnitTest.java b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/AuthApplicationUnitTest.java new file mode 100644 index 0000000000..ecbf1b38e5 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/AuthApplicationUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.annotations.service; + +import com.baeldung.annotations.service.abstracts.AbstractAuthenticationService; +import com.baeldung.annotations.service.config.AbstractsAnnotatedTestConfiguration; +import com.baeldung.annotations.service.config.ConcreteClassesAnnotatedTestConfiguration; +import com.baeldung.annotations.service.config.InterfacesAnnotatedTestConfiguration; +import com.baeldung.annotations.service.interfaces.AuthenticationService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +public class AuthApplicationUnitTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); + + @Test + void whenOnlyInterfacesAnnotated_noSuchBeanDefinitionExceptionThrown() { + contextRunner + .withUserConfiguration(InterfacesAnnotatedTestConfiguration.class) + .run(context -> { + Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> { + context.getBean(AuthenticationService.class); + }); + }); + } + + @Test + void whenOnlyAbstractClassesAnnotated_noSuchBeanDefinitionExceptionThrown() { + contextRunner + .withUserConfiguration(AbstractsAnnotatedTestConfiguration.class) + .run(context -> { + Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> { + context.getBean(AbstractAuthenticationService.class); + }); + }); + } + + @Test + void whenConcreteClassesAnnotated_noExceptionThrown() { + contextRunner + .withUserConfiguration(ConcreteClassesAnnotatedTestConfiguration.class) + .run(context -> { + AuthenticationService inMemoryAuthService = context.getBean(AuthenticationService.class); + AbstractAuthenticationService ldapAuthService = context.getBean(AbstractAuthenticationService.class); + + Assertions.assertNotNull(inMemoryAuthService); + Assertions.assertNotNull(ldapAuthService); + }); + } +} diff --git a/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/AbstractsAnnotatedTestConfiguration.java b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/AbstractsAnnotatedTestConfiguration.java new file mode 100644 index 0000000000..4c52401a06 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/AbstractsAnnotatedTestConfiguration.java @@ -0,0 +1,10 @@ +package com.baeldung.annotations.service.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.ComponentScan; + +@TestConfiguration +@ComponentScan("com.baeldung.annotations.service.abstracts") +public class AbstractsAnnotatedTestConfiguration { + +} diff --git a/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/ConcreteClassesAnnotatedTestConfiguration.java b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/ConcreteClassesAnnotatedTestConfiguration.java new file mode 100644 index 0000000000..baf7fb970c --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/ConcreteClassesAnnotatedTestConfiguration.java @@ -0,0 +1,10 @@ +package com.baeldung.annotations.service.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.ComponentScan; + +@TestConfiguration +@ComponentScan("com.baeldung.annotations.service.concretes") +public class ConcreteClassesAnnotatedTestConfiguration { + +} diff --git a/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/InterfacesAnnotatedTestConfiguration.java b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/InterfacesAnnotatedTestConfiguration.java new file mode 100644 index 0000000000..94659902a1 --- /dev/null +++ b/spring-boot-modules/spring-boot-annotations/src/test/java/com.baeldung.annotations/service/config/InterfacesAnnotatedTestConfiguration.java @@ -0,0 +1,10 @@ +package com.baeldung.annotations.service.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.ComponentScan; + +@TestConfiguration +@ComponentScan("com.baeldung.annotations.service.interfaces") +public class InterfacesAnnotatedTestConfiguration { + +} diff --git a/spring-boot-modules/spring-boot-runtime-2/README.md b/spring-boot-modules/spring-boot-runtime-2/README.md new file mode 100644 index 0000000000..f997f2473d --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/README.md @@ -0,0 +1,6 @@ +## Spring Boot Runtime 2 + +This module contains articles about administering a Spring Boot runtime + +### Relevant Articles: + - diff --git a/spring-boot-modules/spring-boot-runtime-2/pom.xml b/spring-boot-modules/spring-boot-runtime-2/pom.xml new file mode 100644 index 0000000000..8f6351165a --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + ../ + + + spring-boot-runtime-2 + jar + + spring-boot-runtime-2 + Demo project for Spring Boot + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + ${project.artifactId} + + + src/main/resources/heap + ${project.build.directory} + true + + ${project.name}.conf + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + com.baeldung.heap.HeapSizeDemoApplication + + + + + true + + -Xms256m + -Xmx1g + + + + + + diff --git a/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/HeapSizeDemoApplication.java b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/HeapSizeDemoApplication.java new file mode 100644 index 0000000000..60d4bf7bab --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/HeapSizeDemoApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.heap; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HeapSizeDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(HeapSizeDemoApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStats.java b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStats.java new file mode 100644 index 0000000000..0d2f471a12 --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStats.java @@ -0,0 +1,31 @@ +package com.baeldung.heap; + +public class MemoryStats { + private long heapSize; + private long heapMaxSize; + private long heapFreeSize; + + public long getHeapSize() { + return heapSize; + } + + public void setHeapSize(long heapSize) { + this.heapSize = heapSize; + } + + public long getHeapMaxSize() { + return heapMaxSize; + } + + public void setHeapMaxSize(long heapMaxSize) { + this.heapMaxSize = heapMaxSize; + } + + public long getHeapFreeSize() { + return heapFreeSize; + } + + public void setHeapFreeSize(long heapFreeSize) { + this.heapFreeSize = heapFreeSize; + } +} diff --git a/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStatusController.java b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStatusController.java new file mode 100644 index 0000000000..293747fbd1 --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/src/main/java/com/baeldung/heap/MemoryStatusController.java @@ -0,0 +1,20 @@ +package com.baeldung.heap; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MemoryStatusController { + + @GetMapping("memory-status") + public MemoryStats getMemoryStatistics() { + MemoryStats stats = new MemoryStats(); + stats.setHeapSize(Runtime.getRuntime() + .totalMemory()); + stats.setHeapMaxSize(Runtime.getRuntime() + .maxMemory()); + stats.setHeapFreeSize(Runtime.getRuntime() + .freeMemory()); + return stats; + } +} diff --git a/spring-boot-modules/spring-boot-runtime-2/src/main/resources/heap/spring-boot-runtime-2.conf b/spring-boot-modules/spring-boot-runtime-2/src/main/resources/heap/spring-boot-runtime-2.conf new file mode 100644 index 0000000000..3bfde4e3d9 --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/src/main/resources/heap/spring-boot-runtime-2.conf @@ -0,0 +1 @@ +JAVA_OPTS="-Xms512m -Xmx1024m" \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime-2/src/test/java/com/baeldung/heap/MemoryStatusControllerIntegrationTest.java b/spring-boot-modules/spring-boot-runtime-2/src/test/java/com/baeldung/heap/MemoryStatusControllerIntegrationTest.java new file mode 100644 index 0000000000..2e744285d8 --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime-2/src/test/java/com/baeldung/heap/MemoryStatusControllerIntegrationTest.java @@ -0,0 +1,32 @@ +package com.baeldung.heap; + +import static org.hamcrest.Matchers.notANumber; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@RunWith(SpringRunner.class) +@WebMvcTest(MemoryStatusController.class) +public class MemoryStatusControllerIntegrationTest { + + @Autowired + private MockMvc mvc; + + @Test + public void whenGetMemoryStatistics_thenReturnJsonArray() throws Exception { + mvc.perform(get("/memory-status").contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("heapSize", not(notANumber()))) + .andExpect(jsonPath("heapMaxSize", not(notANumber()))) + .andExpect(jsonPath("heapFreeSize", not(notANumber()))); + } +} diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/RegularRestController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/RegularRestController.java new file mode 100644 index 0000000000..f3135229d6 --- /dev/null +++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/RegularRestController.java @@ -0,0 +1,31 @@ +package com.baeldung.springdoc.controller; + +import java.time.LocalDate; +import java.time.LocalTime; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Hidden; + +@Hidden +@RestController +public class RegularRestController { + + @Hidden + @GetMapping("/getAuthor") + public String getAuthor() { + return "Umang Budhwar"; + } + + @GetMapping("/getDate") + public LocalDate getDate() { + return LocalDate.now(); + } + + @GetMapping("/getTime") + public LocalTime getTime() { + return LocalTime.now(); + } + +} diff --git a/spring-web-modules/spring-mvc-forms-jsp/README.md b/spring-web-modules/spring-mvc-forms-jsp/README.md index 2c077f5171..afbf7afe40 100644 --- a/spring-web-modules/spring-mvc-forms-jsp/README.md +++ b/spring-web-modules/spring-mvc-forms-jsp/README.md @@ -7,3 +7,4 @@ This module contains articles about Spring MVC Forms using JSP - [Getting Started with Forms in Spring MVC](https://www.baeldung.com/spring-mvc-form-tutorial) - [Form Validation with AngularJS and Spring MVC](https://www.baeldung.com/validation-angularjs-spring-mvc) - [A Guide to the JSTL Library](https://www.baeldung.com/jstl) +- [Multiple Submit Buttons on a Form](https://www.baeldung.com/spring-form-multiple-submit-buttons) diff --git a/spring-web-modules/spring-mvc-forms-jsp/src/main/java/com/baeldung/springmvcforms/controller/EmployeeController.java b/spring-web-modules/spring-mvc-forms-jsp/src/main/java/com/baeldung/springmvcforms/controller/EmployeeController.java index 3ece77bb18..478b3532fe 100644 --- a/spring-web-modules/spring-mvc-forms-jsp/src/main/java/com/baeldung/springmvcforms/controller/EmployeeController.java +++ b/spring-web-modules/spring-mvc-forms-jsp/src/main/java/com/baeldung/springmvcforms/controller/EmployeeController.java @@ -26,7 +26,7 @@ public class EmployeeController { return employeeMap.get(Id); } - @RequestMapping(value = "/addEmployee", method = RequestMethod.POST) + @RequestMapping(value = "/addEmployee", method = RequestMethod.POST, params = "submit") public String submit(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) { if (result.hasErrors()) { return "error"; @@ -37,5 +37,11 @@ public class EmployeeController { employeeMap.put(employee.getId(), employee); return "employeeView"; } + + @RequestMapping(value = "/addEmployee", method = RequestMethod.POST, params = "cancel") + public String cancel(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) { + model.addAttribute("message", "You clicked cancel, please re-enter employee details:"); + return "employeeHome"; + } } diff --git a/spring-web-modules/spring-mvc-forms-jsp/src/main/webapp/WEB-INF/views/employeeHome.jsp b/spring-web-modules/spring-mvc-forms-jsp/src/main/webapp/WEB-INF/views/employeeHome.jsp index 5ed572000a..82f2cbae00 100644 --- a/spring-web-modules/spring-mvc-forms-jsp/src/main/webapp/WEB-INF/views/employeeHome.jsp +++ b/spring-web-modules/spring-mvc-forms-jsp/src/main/webapp/WEB-INF/views/employeeHome.jsp @@ -7,6 +7,7 @@

Welcome, Enter The Employee Details

+

${message}

@@ -23,7 +24,8 @@ - + +
diff --git a/spring-web-modules/spring-rest-http-2/README.md b/spring-web-modules/spring-rest-http-2/README.md index 97cdc2d068..b5358f888f 100644 --- a/spring-web-modules/spring-rest-http-2/README.md +++ b/spring-web-modules/spring-rest-http-2/README.md @@ -8,3 +8,4 @@ The "REST With Spring 2" Classes: http://bit.ly/restwithspring ### Relevant Articles: - [How to Turn Off Swagger-ui in Production](https://www.baeldung.com/swagger-ui-turn-off-in-production) +- [Setting a Request Timeout for a Spring REST API](https://www.baeldung.com/spring-rest-timeout) diff --git a/spring-web-modules/spring-resttemplate-2/README.md b/spring-web-modules/spring-resttemplate-2/README.md index a903757bb4..54fad5e01b 100644 --- a/spring-web-modules/spring-resttemplate-2/README.md +++ b/spring-web-modules/spring-resttemplate-2/README.md @@ -10,3 +10,5 @@ This module contains articles about Spring RestTemplate - [RestTemplate Post Request with JSON](https://www.baeldung.com/spring-resttemplate-post-json) - [How to Compress Requests Using the Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-compressing-requests) - [Get list of JSON objects with Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-json-list) +- [A Guide To Spring Redirects](https://www.baeldung.com/spring-redirect-and-forward) +- [Spring RestTemplate Exception: “Not enough variables available to expand”](https://www.baeldung.com/spring-not-enough-variables-available) diff --git a/spring-web-modules/spring-resttemplate-2/pom.xml b/spring-web-modules/spring-resttemplate-2/pom.xml index 04be058638..158380b403 100644 --- a/spring-web-modules/spring-resttemplate-2/pom.xml +++ b/spring-web-modules/spring-resttemplate-2/pom.xml @@ -11,7 +11,7 @@ com.baeldung parent-boot-2 0.0.1-SNAPSHOT - parent-boot-2/pom.xml + ../../parent-boot-2/pom.xml diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/RestTemplateExceptionApplication.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/RestTemplateExceptionApplication.java new file mode 100644 index 0000000000..84a337f5ee --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/RestTemplateExceptionApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.resttemplateexception; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RestTemplateExceptionApplication { + + public static void main(String[] args) { + SpringApplication.run(RestTemplateExceptionApplication.class, args); + } + +} diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/controller/ProductApi.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/controller/ProductApi.java new file mode 100644 index 0000000000..2c530cae2b --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/controller/ProductApi.java @@ -0,0 +1,46 @@ +package com.baeldung.resttemplateexception.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.resttemplateexception.model.Criterion; +import com.baeldung.resttemplateexception.model.Product; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@RestController +@RequestMapping("/api") +public class ProductApi { + + private List productList = new ArrayList<>(Arrays.asList(new Product(1, "Acer Aspire 5", 437), new Product(2, "ASUS VivoBook", 650), new Product(3, "Lenovo Legion", 990))); + + @GetMapping("/get") + public Product get(@RequestParam String criterion) throws JsonMappingException, JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + Criterion crt = objectMapper.readValue(criterion, Criterion.class); + if (crt.getProp().equals("name")) + return findByName(crt.getValue()); + + // Search by other properties (id,price) + + return null; + } + + private Product findByName(String name) { + for (Product product : this.productList) { + if (product.getName().equals(name)) { + return product; + } + } + return null; + } + + // Other methods +} diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Criterion.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Criterion.java new file mode 100644 index 0000000000..9a96ad4dc3 --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Criterion.java @@ -0,0 +1,28 @@ +package com.baeldung.resttemplateexception.model; + +public class Criterion { + + private String prop; + private String value; + + public Criterion() { + + } + + public String getProp() { + return prop; + } + + public void setProp(String prop) { + this.prop = prop; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Product.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Product.java new file mode 100644 index 0000000000..e4cc29279c --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/resttemplateexception/model/Product.java @@ -0,0 +1,43 @@ +package com.baeldung.resttemplateexception.model; + +public class Product { + + private int id; + private String name; + private double price; + + public Product() { + + } + + public Product(int id, String name, double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + +} diff --git a/spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java b/spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java similarity index 100% rename from spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java rename to spring-web-modules/spring-resttemplate-2/src/main/java/com/baeldung/sampleapp/web/controller/redirect/RedirectController.java diff --git a/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/api-servlet.xml b/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/api-servlet.xml new file mode 100644 index 0000000000..ed37a962e9 --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/api-servlet.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + /WEB-INF/spring-views.xml + + + + + + + + + + + + + diff --git a/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/spring-views.xml b/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/spring-views.xml new file mode 100644 index 0000000000..2944828d6d --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/main/webapp/WEB-INF/spring-views.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/spring-web-modules/spring-resttemplate-2/src/test/java/com/baeldung/resttemplateexception/RestTemplateExceptionLiveTest.java b/spring-web-modules/spring-resttemplate-2/src/test/java/com/baeldung/resttemplateexception/RestTemplateExceptionLiveTest.java new file mode 100644 index 0000000000..adfb8ffa4e --- /dev/null +++ b/spring-web-modules/spring-resttemplate-2/src/test/java/com/baeldung/resttemplateexception/RestTemplateExceptionLiveTest.java @@ -0,0 +1,47 @@ +package com.baeldung.resttemplateexception; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import com.baeldung.resttemplateexception.model.Product; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = { RestTemplate.class, RestTemplateExceptionApplication.class }) +public class RestTemplateExceptionLiveTest { + + @Autowired + RestTemplate restTemplate; + + @Test(expected = IllegalArgumentException.class) + public void givenGetUrl_whenJsonIsPassed_thenThrowException() { + String url = "http://localhost:8080/spring-rest/api/get?criterion={\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}"; + Product product = restTemplate.getForObject(url, Product.class); + } + + @Test + public void givenGetUrl_whenJsonIsPassed_thenGetProduct() { + String criterion = "{\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}"; + String url = "http://localhost:8080/spring-rest/api/get?criterion={criterion}"; + Product product = restTemplate.getForObject(url, Product.class, criterion); + + assertEquals(product.getPrice(), 650, 0); + } + + @Test + public void givenGetUrl_whenJsonIsPassed_thenReturnProduct() { + String criterion = "{\"prop\":\"name\",\"value\":\"Acer Aspire 5\"}"; + String url = "http://localhost:8080/spring-rest/api/get"; + + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url).queryParam("criterion", criterion); + Product product = restTemplate.getForObject(builder.build().toUri(), Product.class); + + assertEquals(product.getId(), 1, 0); + } +} diff --git a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/controller/redirect/RedirectControllerIntegrationTest.java b/spring-web-modules/spring-resttemplate-2/src/test/java/com/baeldung/web/controller/redirect/RedirectControllerIntegrationTest.java similarity index 100% rename from spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/controller/redirect/RedirectControllerIntegrationTest.java rename to spring-web-modules/spring-resttemplate-2/src/test/java/com/baeldung/web/controller/redirect/RedirectControllerIntegrationTest.java diff --git a/spring-web-modules/spring-resttemplate/pom.xml b/spring-web-modules/spring-resttemplate/pom.xml index c0f266fd62..1db6b5db57 100644 --- a/spring-web-modules/spring-resttemplate/pom.xml +++ b/spring-web-modules/spring-resttemplate/pom.xml @@ -188,7 +188,7 @@ cargo-maven2-plugin ${cargo-maven2-plugin.version} - + true tomcat8x embedded @@ -297,7 +297,7 @@ 20.0 - 1.6.0 + 1.6.1 3.0.4 diff --git a/spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/resttemplate/web/service/EmployeeService.java b/spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/mock/EmployeeService.java similarity index 95% rename from spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/resttemplate/web/service/EmployeeService.java rename to spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/mock/EmployeeService.java index 18dff3db1b..16180a3640 100644 --- a/spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/resttemplate/web/service/EmployeeService.java +++ b/spring-web-modules/spring-resttemplate/src/main/java/com/baeldung/mock/EmployeeService.java @@ -1,4 +1,4 @@ -package com.baeldung.resttemplate.web.service; +package com.baeldung.mock; import com.baeldung.resttemplate.web.model.Employee; import org.slf4j.Logger; diff --git a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/client/TestRestTemplateBasicLiveTest.java b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/client/TestRestTemplateBasicLiveTest.java index 9f4b3c9b35..406dd5979b 100644 --- a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/client/TestRestTemplateBasicLiveTest.java +++ b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/client/TestRestTemplateBasicLiveTest.java @@ -18,6 +18,7 @@ import org.springframework.web.client.RestTemplate; import okhttp3.Request; import okhttp3.RequestBody; +// This test needs RestTemplateConfigurationApplication to be up and running public class TestRestTemplateBasicLiveTest { private RestTemplate restTemplate; diff --git a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceMockRestServiceServerUnitTest.java similarity index 96% rename from spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java rename to spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceMockRestServiceServerUnitTest.java index ee01cb6a50..309e0635a4 100644 --- a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java +++ b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceMockRestServiceServerUnitTest.java @@ -1,4 +1,4 @@ -package com.baeldung.web.service; +package com.baeldung.mock; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; @@ -7,8 +7,9 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat import java.net.URI; import com.baeldung.SpringTestConfig; +import com.baeldung.mock.EmployeeService; import com.baeldung.resttemplate.web.model.Employee; -import com.baeldung.resttemplate.web.service.EmployeeService; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceUnitTest.java b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceUnitTest.java similarity index 92% rename from spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceUnitTest.java rename to spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceUnitTest.java index 6eb040414b..9a992f390a 100644 --- a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/web/service/EmployeeServiceUnitTest.java +++ b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/mock/EmployeeServiceUnitTest.java @@ -1,7 +1,8 @@ -package com.baeldung.web.service; +package com.baeldung.mock; +import com.baeldung.mock.EmployeeService; import com.baeldung.resttemplate.web.model.Employee; -import com.baeldung.resttemplate.web.service.EmployeeService; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/resttemplate/RestTemplateBasicLiveTest.java b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/resttemplate/RestTemplateBasicLiveTest.java index 0dab124316..8d52394dd1 100644 --- a/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/resttemplate/RestTemplateBasicLiveTest.java +++ b/spring-web-modules/spring-resttemplate/src/test/java/com/baeldung/resttemplate/RestTemplateBasicLiveTest.java @@ -38,6 +38,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.google.common.base.Charsets; +// This test needs RestTemplateConfigurationApplication to be up and running public class RestTemplateBasicLiveTest { private RestTemplate restTemplate; diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md index c7b62182b5..4bd2ff9759 100644 --- a/testing-modules/mockito-2/README.md +++ b/testing-modules/mockito-2/README.md @@ -9,3 +9,4 @@ - [Mockito – Using Spies](https://www.baeldung.com/mockito-spy) - [Using Mockito ArgumentCaptor](https://www.baeldung.com/mockito-argumentcaptor) - [Difference Between when() and doXxx() Methods in Mockito](https://www.baeldung.com/java-mockito-when-vs-do) +- [Overview of Mockito MockSettings](https://www.baeldung.com/mockito-mocksettings)