diff --git a/core-java-modules/core-java-10/README.md b/core-java-modules/core-java-10/README.md index 38b1db1c05..11c2051816 100644 --- a/core-java-modules/core-java-10/README.md +++ b/core-java-modules/core-java-10/README.md @@ -5,7 +5,7 @@ This module contains articles about Java 10 core features ### Relevant Articles: - [Java 10 LocalVariable Type-Inference](http://www.baeldung.com/java-10-local-variable-type-inference) -- [Guide to Java 10](http://www.baeldung.com/java-10-overview) +- [New Features in Java 10](https://www.baeldung.com/java-10-overview) - [Copy a List to Another List in Java](http://www.baeldung.com/java-copy-list-to-another) - [Deep Dive Into the New Java JIT Compiler – Graal](https://www.baeldung.com/graal-java-jit-compiler) - [Copying Sets in Java](https://www.baeldung.com/java-copy-sets) diff --git a/core-java-modules/core-java-12/README.md b/core-java-modules/core-java-12/README.md index c28df26c6f..b509be876c 100644 --- a/core-java-modules/core-java-12/README.md +++ b/core-java-modules/core-java-12/README.md @@ -1,5 +1,4 @@ ## Relevant Articles: - - [String API Updates in Java 12](https://www.baeldung.com/java12-string-api) -- [Java 12 New Features](https://www.baeldung.com/java-12-new-features) +- [New Features in Java 12](https://www.baeldung.com/java-12-new-features) diff --git a/core-java-modules/core-java-13/README.md b/core-java-modules/core-java-13/README.md index 697f89c362..9215139dd4 100644 --- a/core-java-modules/core-java-13/README.md +++ b/core-java-modules/core-java-13/README.md @@ -1,4 +1,4 @@ ### Relevant articles: - [Java Switch Statement](https://www.baeldung.com/java-switch) -- [New Java 13 Features](https://www.baeldung.com/java-13-new-features) +- [New Features in Java 13](https://www.baeldung.com/java-13-new-features) diff --git a/core-java-modules/core-java-14/README.md b/core-java-modules/core-java-14/README.md index 004b3587c4..07cdf9859c 100644 --- a/core-java-modules/core-java-14/README.md +++ b/core-java-modules/core-java-14/README.md @@ -10,4 +10,4 @@ This module contains articles about Java 14. - [Helpful NullPointerExceptions in Java 14](https://www.baeldung.com/java-14-nullpointerexception) - [Foreign Memory Access API in Java 14](https://www.baeldung.com/java-foreign-memory-access) - [Java 14 Record Keyword](https://www.baeldung.com/java-record-keyword) -- [Java 14 – New Features](https://www.baeldung.com/java-14-new-features) +- [New Features in Java 14](https://www.baeldung.com/java-14-new-features) diff --git a/core-java-modules/core-java-9-new-features/README.md b/core-java-modules/core-java-9-new-features/README.md index 5af069c6f0..4045a37d9d 100644 --- a/core-java-modules/core-java-9-new-features/README.md +++ b/core-java-modules/core-java-9-new-features/README.md @@ -4,7 +4,7 @@ This module contains articles about core Java features that have been introduced ### Relevant Articles: -- [Java 9 New Features](https://www.baeldung.com/new-java-9) +- [New Features in Java 9](https://www.baeldung.com/new-java-9) - [Java 9 Variable Handles Demystified](http://www.baeldung.com/java-variable-handles) - [Exploring the New HTTP Client in Java 9 and 11](http://www.baeldung.com/java-9-http-client) - [Multi-Release Jar Files](https://www.baeldung.com/java-multi-release-jar) diff --git a/core-java-modules/core-java-char/README.md b/core-java-modules/core-java-char/README.md index fd79da15ab..5f33aa6914 100644 --- a/core-java-modules/core-java-char/README.md +++ b/core-java-modules/core-java-char/README.md @@ -3,4 +3,4 @@ This module contains articles about Java Character Class ### Relevant Articles: -- [Character#isAlphabetic vs Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic) +- [Character#isAlphabetic vs. Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic) 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-collections-maps-3/pom.xml b/core-java-modules/core-java-collections-maps-3/pom.xml index 577ad58255..c9fda26fc3 100644 --- a/core-java-modules/core-java-collections-maps-3/pom.xml +++ b/core-java-modules/core-java-collections-maps-3/pom.xml @@ -20,6 +20,11 @@ jmh-core ${jmh-core.version} + + com.google.guava + guava + ${guava.version} + diff --git a/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverter.java b/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverter.java new file mode 100644 index 0000000000..3472f998f5 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/main/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverter.java @@ -0,0 +1,39 @@ +package com.baeldung.map.propertieshashmap; + +import com.google.common.collect.Maps; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +public class PropertiesToHashMapConverter { + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static HashMap typeCastConvert(Properties prop) { + Map step1 = prop; + Map step2 = (Map) step1; + return new HashMap<>(step2); + } + + public static HashMap loopConvert(Properties prop) { + HashMap retMap = new HashMap<>(); + for (Map.Entry entry : prop.entrySet()) { + retMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); + } + return retMap; + } + + public static HashMap streamConvert(Properties prop) { + return prop.entrySet().stream().collect( + Collectors.toMap( + e -> String.valueOf(e.getKey()), + e -> String.valueOf(e.getValue()), + (prev, next) -> next, HashMap::new + )); + } + + public static HashMap guavaConvert(Properties prop) { + return Maps.newHashMap(Maps.fromProperties(prop)); + } +} diff --git a/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/hashmap/loadfactor/HashMapLoadFactorUnitTest.java b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/hashmap/loadfactor/HashMapLoadFactorUnitTest.java new file mode 100644 index 0000000000..94967a4905 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/hashmap/loadfactor/HashMapLoadFactorUnitTest.java @@ -0,0 +1,49 @@ +package com.baeldung.map.hashmap.loadfactor; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +public class HashMapLoadFactorUnitTest { + + @Test + public void whenCreateMapWithDefaultParam_thenSucces() { + Map mapWithDefaultParams = new HashMap<>(); + mapWithDefaultParams.put("1", "one"); + mapWithDefaultParams.put("2", "two"); + mapWithDefaultParams.put("3", "three"); + mapWithDefaultParams.put("4", "four"); + mapWithDefaultParams.put("5", "five"); + + Assert.assertEquals(5, mapWithDefaultParams.size()); + } + + @Test + public void whenCreateMapWithInitialCapacity_thenSucces() { + Map mapWithInitialCapacity = new HashMap<>(5); + mapWithInitialCapacity.put("1", "one"); + mapWithInitialCapacity.put("2", "two"); + mapWithInitialCapacity.put("3", "three"); + + Assert.assertEquals(3, mapWithInitialCapacity.size()); + } + + @Test + public void whenCreateMapWithInitialCapacityAndLF_thenSucces() { + Map mapWithInitialCapacityAndLF = new HashMap<>(5, 0.5f); + mapWithInitialCapacityAndLF.put("1", "one"); + mapWithInitialCapacityAndLF.put("2", "two"); + mapWithInitialCapacityAndLF.put("3", "three"); + mapWithInitialCapacityAndLF.put("4", "four"); + mapWithInitialCapacityAndLF.put("5", "five"); + mapWithInitialCapacityAndLF.put("6", "six"); + mapWithInitialCapacityAndLF.put("7", "seven"); + mapWithInitialCapacityAndLF.put("8", "eight"); + mapWithInitialCapacityAndLF.put("9", "nine"); + mapWithInitialCapacityAndLF.put("10", "ten"); + + Assert.assertEquals(10, mapWithInitialCapacityAndLF.size()); + } +} diff --git a/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverterUnitTest.java b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverterUnitTest.java new file mode 100644 index 0000000000..1985fbe673 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/test/java/com/baeldung/map/propertieshashmap/PropertiesToHashMapConverterUnitTest.java @@ -0,0 +1,192 @@ +package com.baeldung.map.propertieshashmap; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +class PropertiesToHashMapConverterUnitTest { + + private Properties properties; + + private final static String propertyFileName = "toHashMap.properties"; + + @BeforeEach + public void setup() throws IOException { + properties = new Properties(); + try (InputStream is = getClass().getClassLoader().getResourceAsStream(propertyFileName)) { + if (is != null) { + properties.load(is); + } + } + } + + @Test + public void havingPropertiesLoaded_whenCheck_thenEquals() { + assertEquals(3, properties.size()); + assertEquals("str_value", properties.get("property1")); + assertEquals("123", properties.get("property2")); + assertEquals("", properties.get("property3")); + } + + @Test + public void whenPropertiesModified_thenTypeSafeIssues() { + compromiseProperties(properties); + + assertEquals(5, properties.size()); + + assertNull(properties.getProperty("property4")); + assertNotEquals(String.class, properties.get("property4").getClass()); + assertEquals(456, properties.get("property4")); + + + assertNull(properties.getProperty("5")); + assertNotEquals(String.class, properties.get(5).getClass()); + assertEquals(10.11, properties.get(5)); + } + + @Test + public void havingNonModifiedProperties_whenTypeCastConvert_thenNoTypeSafeIssues() { + HashMap hMap = PropertiesToHashMapConverter.typeCastConvert(properties); + + assertEquals(3, hMap.size()); + assertEquals(String.class, hMap.get("property1").getClass()); + assertEquals(properties.get("property1"), hMap.get("property1")); + assertEquals(String.class, hMap.get("property2").getClass()); + assertEquals(properties.get("property2"), hMap.get("property2")); + assertEquals(String.class, hMap.get("property3").getClass()); + assertEquals(properties.get("property3"), hMap.get("property3")); + } + + @Test + public void havingModifiedProperties_whenTypeCastConvert_thenClassCastException() { + compromiseProperties(properties); + HashMap hMap = PropertiesToHashMapConverter.typeCastConvert(properties); + assertEquals(5, hMap.size()); + + assertThrows(ClassCastException.class, () -> { + String s = hMap.get("property4"); + }); + assertEquals(Integer.class, ((Object) hMap.get("property4")).getClass()); + + assertNull(hMap.get("5")); + assertNotNull(hMap.get(5)); + assertThrows(ClassCastException.class, () -> { + String s = hMap.get(5); + }); + assertEquals(Double.class, ((Object) hMap.get(5)).getClass()); + } + + @Test + public void havingNonModifiedProperties_whenLoopConvert_thenNoTypeSafeIssues() { + HashMap hMap = PropertiesToHashMapConverter.loopConvert(properties); + + assertEquals(3, hMap.size()); + assertEquals(String.class, hMap.get("property1").getClass()); + assertEquals(properties.get("property1"), hMap.get("property1")); + assertEquals(String.class, hMap.get("property2").getClass()); + assertEquals(properties.get("property2"), hMap.get("property2")); + assertEquals(String.class, hMap.get("property3").getClass()); + assertEquals(properties.get("property3"), hMap.get("property3")); + } + + @Test + public void havingModifiedProperties_whenLoopConvert_thenNoClassCastException() { + compromiseProperties(properties); + HashMap hMap = PropertiesToHashMapConverter.loopConvert(properties); + assertEquals(5, hMap.size()); + + assertDoesNotThrow(() -> { + String s = hMap.get("property4"); + }); + assertEquals(String.class, hMap.get("property4").getClass()); + assertEquals("456", hMap.get("property4")); + + assertDoesNotThrow(() -> { + String s = hMap.get("5"); + }); + assertEquals("10.11", hMap.get("5")); + } + + @Test + public void havingNonModifiedProperties_whenStreamConvert_thenNoTypeSafeIssues() { + HashMap hMap = PropertiesToHashMapConverter.streamConvert(properties); + + assertEquals(3, hMap.size()); + assertEquals(String.class, hMap.get("property1").getClass()); + assertEquals(properties.get("property1"), hMap.get("property1")); + assertEquals(String.class, hMap.get("property2").getClass()); + assertEquals(properties.get("property2"), hMap.get("property2")); + assertEquals(String.class, hMap.get("property3").getClass()); + assertEquals(properties.get("property3"), hMap.get("property3")); + } + + @Test + public void havingModifiedProperties_whenStreamConvert_thenNoClassCastException() { + compromiseProperties(properties); + HashMap hMap = PropertiesToHashMapConverter.streamConvert(properties); + assertEquals(5, hMap.size()); + + assertDoesNotThrow(() -> { + String s = hMap.get("property4"); + }); + assertEquals(String.class, hMap.get("property4").getClass()); + assertEquals("456", hMap.get("property4")); + + assertDoesNotThrow(() -> { + String s = hMap.get("5"); + }); + assertEquals("10.11", hMap.get("5")); + } + + @Test + public void havingModifiedProperties_whenLoopConvertAndStreamConvert_thenHashMapsSame() { + compromiseProperties(properties); + HashMap hMap1 = PropertiesToHashMapConverter.loopConvert(properties); + HashMap hMap2 = PropertiesToHashMapConverter.streamConvert(properties); + + assertEquals(hMap2, hMap1); + } + + @Test + public void havingNonModifiedProperties_whenGuavaConvert_thenNoTypeSafeIssues() { + HashMap hMap = PropertiesToHashMapConverter.guavaConvert(properties); + + assertEquals(3, hMap.size()); + assertEquals(String.class, hMap.get("property1").getClass()); + assertEquals(properties.get("property1"), hMap.get("property1")); + assertEquals(String.class, hMap.get("property2").getClass()); + assertEquals(properties.get("property2"), hMap.get("property2")); + assertEquals(String.class, hMap.get("property3").getClass()); + assertEquals(properties.get("property3"), hMap.get("property3")); + } + + @Test + public void havingModifiedProperties_whenGuavaConvert_thenUnableToConvertAndThrowException() { + compromiseProperties(properties); + assertThrows(Exception.class, () -> PropertiesToHashMapConverter.guavaConvert(properties)); + } + + @Test + public void havingModifiedPropertiesWithNoIntegerValue_whenGuavaConvert_thenNullPointerException() { + properties.put("property4", 456); + assertThrows(NullPointerException.class, () -> PropertiesToHashMapConverter.guavaConvert(properties)); + } + + @Test + public void havingModifiedPropertiesWithNoIntegerKey_whenGuavaConvert_thenClassCastException() { + properties.put(5, 10.11); + assertThrows(ClassCastException.class, () -> PropertiesToHashMapConverter.guavaConvert(properties)); + } + + + private void compromiseProperties(Properties prop) { + prop.put("property4", 456); + prop.put(5, 10.11); + } +} diff --git a/core-java-modules/core-java-collections-maps-3/src/test/resources/toHashMap.properties b/core-java-modules/core-java-collections-maps-3/src/test/resources/toHashMap.properties new file mode 100644 index 0000000000..727e858a8c --- /dev/null +++ b/core-java-modules/core-java-collections-maps-3/src/test/resources/toHashMap.properties @@ -0,0 +1,3 @@ +property1=str_value +property2=123 +property3= 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-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/README.md b/core-java-modules/core-java-lang-3/README.md index 5279cc23b0..8ed945a56c 100644 --- a/core-java-modules/core-java-lang-3/README.md +++ b/core-java-modules/core-java-lang-3/README.md @@ -11,4 +11,5 @@ This module contains articles about core features in the Java language - [The transient Keyword in Java](https://www.baeldung.com/java-transient-keyword) - [How to Access an Iteration Counter in a For Each Loop](https://www.baeldung.com/java-foreach-counter) - [Comparing Doubles in Java](https://www.baeldung.com/java-comparing-doubles) +- [Guide to Implementing the compareTo Method](https://www.baeldung.com/java-compareto) - [[<-- Prev]](/core-java-modules/core-java-lang-2) 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-math-3/README.md b/core-java-modules/core-java-lang-math-3/README.md index dda3013407..1dd3a3c7e0 100644 --- a/core-java-modules/core-java-lang-math-3/README.md +++ b/core-java-modules/core-java-lang-math-3/README.md @@ -4,6 +4,5 @@ ### Relevant articles: -- [Calculate Factorial in Java](https://www.baeldung.com/java-calculate-factorial) - [Evaluating a Math Expression in Java](https://www.baeldung.com/java-evaluate-math-expression-string) - More articles: [[<-- Prev]](/core-java-modules/core-java-lang-math-2) 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-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/guest/core-kotlin/README.md b/guest/core-kotlin/README.md index c211773f27..fad62ebea6 100644 --- a/guest/core-kotlin/README.md +++ b/guest/core-kotlin/README.md @@ -1,3 +1,3 @@ ### Relevant Articles: -- [Kotlin vs Java](https://www.baeldung.com/kotlin/kotlin-vs-java) +- [Kotlin vs Java](https://www.baeldung.com/kotlin/vs-java) 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/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/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/persistence-modules/spring-data-mongodb/src/main/java/com/baeldung/config/MongoConfig.java b/persistence-modules/spring-data-mongodb/src/main/java/com/baeldung/config/MongoConfig.java index 90b1268133..a7db26eba5 100644 --- a/persistence-modules/spring-data-mongodb/src/main/java/com/baeldung/config/MongoConfig.java +++ b/persistence-modules/spring-data-mongodb/src/main/java/com/baeldung/config/MongoConfig.java @@ -68,4 +68,8 @@ public class MongoConfig extends AbstractMongoClientConfiguration { return new MongoTransactionManager(dbFactory); } + @Override + protected boolean autoIndexCreation() { + return true; + } } diff --git a/pom.xml b/pom.xml index 70efb02f4d..ccdc91ffe0 100644 --- a/pom.xml +++ b/pom.xml @@ -631,6 +631,7 @@ spring-core-2 spring-core-3 spring-core-4 + spring-core-5 spring-cucumber spring-data-rest @@ -1082,6 +1083,7 @@ spring-core-2 spring-core-3 spring-core-4 + spring-core-5 spring-cucumber spring-data-rest 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/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/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 index 39adf0b5c0..7e1fc86847 100644 --- 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 @@ -11,8 +11,6 @@ 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; @@ -41,6 +39,7 @@ import com.baeldung.web.reactive.client.Foo; import com.baeldung.web.reactive.client.WebClientApplication; import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutException; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.handler.timeout.WriteTimeoutHandler; import reactor.core.publisher.Mono; @@ -265,17 +264,18 @@ public class WebClientIntegrationTest { .addHandlerLast(new WriteTimeoutHandler(1000, TimeUnit.MILLISECONDS))); WebClient timeoutClient = WebClient.builder() + .baseUrl("http://localhost:" + port) .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); - BodyInserter, ReactiveHttpOutputMessage> inserterCompleteSuscriber = BodyInserters.fromPublisher(Subscriber::onComplete, String.class); - RequestHeadersSpec headersSpecInserterCompleteSuscriber = timeoutClient.post() + RequestHeadersSpec neverendingMonoBodyRequest = timeoutClient.post() .uri("/resource") - .body(inserterCompleteSuscriber); + .body(Mono.never(), String.class); - StepVerifier.create(headersSpecInserterCompleteSuscriber.retrieve() + StepVerifier.create(neverendingMonoBodyRequest.retrieve() .bodyToMono(String.class)) - .expectTimeout(Duration.ofMillis(2000)) + .expectErrorMatches(ex -> WebClientRequestException.class.isAssignableFrom(ex.getClass()) && ReadTimeoutException.class.isAssignableFrom(ex.getCause() + .getClass())) .verify(); } diff --git a/spring-5/README.md b/spring-5/README.md index cce18bedf8..2ddd9fa94f 100644 --- a/spring-5/README.md +++ b/spring-5/README.md @@ -11,3 +11,5 @@ This module contains articles about Spring 5 - [Spring Assert Statements](https://www.baeldung.com/spring-assert) - [Difference between \ vs \](https://www.baeldung.com/spring-contextannotation-contextcomponentscan) - [Finding the Spring Version](https://www.baeldung.com/spring-find-version) +- [Spring 5 Testing with @EnabledIf Annotation](https://www.baeldung.com/spring-5-enabledIf) +- [Configuring a Hikari Connection Pool with Spring Boot](https://www.baeldung.com/spring-boot-hikari) diff --git a/spring-5/src/test/java/com/baeldung/README.md b/spring-5/src/test/java/com/baeldung/README.md new file mode 100644 index 0000000000..0ff61914d5 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- [Concurrent Test Execution in Spring 5](https://www.baeldung.com/spring-5-concurrent-tests) 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-data-2/README.md b/spring-boot-modules/spring-boot-data-2/README.md index c21ce02a2e..d5020ce354 100644 --- a/spring-boot-modules/spring-boot-data-2/README.md +++ b/spring-boot-modules/spring-boot-data-2/README.md @@ -1,4 +1,3 @@ ### Relevant Articles: - [Spring Boot: Customize the Jackson ObjectMapper](https://www.baeldung.com/spring-boot-customize-jackson-objectmapper) -- [Configuring a Hikari Connection Pool with Spring Boot](https://www.baeldung.com/spring-boot-hikari) 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/README.md b/spring-boot-modules/spring-boot-springdoc/README.md index 2447f30f6b..608e4afa2e 100644 --- a/spring-boot-modules/spring-boot-springdoc/README.md +++ b/spring-boot-modules/spring-boot-springdoc/README.md @@ -2,3 +2,4 @@ - [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation) - [Spring REST Docs vs OpenAPI](https://www.baeldung.com/spring-rest-docs-vs-openapi) +- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints) diff --git a/spring-core-3/README.md b/spring-core-3/README.md index b6257cb9a4..7232ad71a1 100644 --- a/spring-core-3/README.md +++ b/spring-core-3/README.md @@ -11,4 +11,4 @@ This module contains articles about core Spring functionality - [Difference Between BeanFactory and ApplicationContext](https://www.baeldung.com/spring-beanfactory-vs-applicationcontext) - [A Spring Custom Annotation for a Better DAO](http://www.baeldung.com/spring-annotation-bean-pre-processor) - [Custom Scope in Spring](http://www.baeldung.com/spring-custom-scope) -- More articles: [[<-- prev]](/spring-core-2) +- More articles: [[<-- prev]](/spring-core-2) [[next -->]](/spring-core-4) diff --git a/spring-core-4/README.md b/spring-core-4/README.md index 03a6747c1d..3b2c3d4764 100644 --- a/spring-core-4/README.md +++ b/spring-core-4/README.md @@ -11,4 +11,4 @@ This module contains articles about core Spring functionality - [Using @Autowired in Abstract Classes](https://www.baeldung.com/spring-autowired-abstract-class) - [Constructor Injection in Spring with Lombok](https://www.baeldung.com/spring-injection-lombok) - [The Spring ApplicationContext](https://www.baeldung.com/spring-application-context) -- More articles: [[<-- prev]](/spring-core-3) +- More articles: [[<-- prev]](/spring-core-3) [[next -->]](/spring-core-5) diff --git a/spring-core-5/README.md b/spring-core-5/README.md new file mode 100644 index 0000000000..81669e46a7 --- /dev/null +++ b/spring-core-5/README.md @@ -0,0 +1,7 @@ +## Spring Core + +This module contains articles about core Spring functionality + +## Relevant Articles: + +- More articles: [[<-- prev]](/spring-core-4) \ No newline at end of file diff --git a/spring-core-5/pom.xml b/spring-core-5/pom.xml new file mode 100644 index 0000000000..743002c137 --- /dev/null +++ b/spring-core-5/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + spring-core-5 + spring-core-5 + + + com.baeldung + parent-spring-5 + 0.0.1-SNAPSHOT + ../parent-spring-5 + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot-starter.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot-starter.version} + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + + + + + 5.3.3 + 2.4.2 + 2.22.1 + + + diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/AmbiguousBean.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/AmbiguousBean.java new file mode 100644 index 0000000000..70e1f0a79a --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/AmbiguousBean.java @@ -0,0 +1,4 @@ +package com.baeldung.component.inscope; + +public interface AmbiguousBean { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanExample.java new file mode 100644 index 0000000000..d1b4cd34f1 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanExample.java @@ -0,0 +1,4 @@ +package com.baeldung.component.inscope; + +public class BeanExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplA.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplA.java new file mode 100644 index 0000000000..08e1661499 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplA.java @@ -0,0 +1,4 @@ +package com.baeldung.component.inscope; + +public class BeanImplA implements AmbiguousBean { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplB.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplB.java new file mode 100644 index 0000000000..8a45581532 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/BeanImplB.java @@ -0,0 +1,4 @@ +package com.baeldung.component.inscope; + +public class BeanImplB implements AmbiguousBean { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentApplication.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentApplication.java new file mode 100644 index 0000000000..604e7d6632 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentApplication.java @@ -0,0 +1,42 @@ +package com.baeldung.component.inscope; + +import com.baeldung.component.outsidescope.OutsideScopeBeanExample; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@SpringBootApplication +@ComponentScan({"com.baeldung.component.inscope", "com.baeldung.component.scannedscope"}) +@Configuration +public class ComponentApplication { + + @Value( "${ambiguous-bean}" ) + public String ambiguousBean; + + public static void main(String[] args) { + SpringApplication.run( ComponentApplication.class, args ); + } + + @Bean + public BeanExample beanExample() { + return new BeanExample(); + } + + @Bean + public OutsideScopeBeanExample outsideScopeBeanExample() { + return new OutsideScopeBeanExample(); + } + + @Bean + public AmbiguousBean ambiguousBean() { + if (ambiguousBean.equals("A")) { + return new BeanImplA(); + } + else { + return new BeanImplB(); + } + } +} \ No newline at end of file diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentExample.java new file mode 100644 index 0000000000..66d3e20d34 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/ComponentExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.inscope; + +import org.springframework.stereotype.Component; + +@Component +public class ComponentExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/ControllerExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/ControllerExample.java new file mode 100644 index 0000000000..773af2a113 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/ControllerExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.inscope; + +import org.springframework.stereotype.Controller; + +@Controller +public class ControllerExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponent.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponent.java new file mode 100644 index 0000000000..795a1b23fa --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponent.java @@ -0,0 +1,14 @@ +package com.baeldung.component.inscope; + +import org.springframework.stereotype.Component; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface CustomComponent { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponentExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponentExample.java new file mode 100644 index 0000000000..8009f8ec20 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/CustomComponentExample.java @@ -0,0 +1,5 @@ +package com.baeldung.component.inscope; + +@CustomComponent +public class CustomComponentExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/RepositoryExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/RepositoryExample.java new file mode 100644 index 0000000000..9297992904 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/RepositoryExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.inscope; + +import org.springframework.stereotype.Repository; + +@Repository +public class RepositoryExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/inscope/ServiceExample.java b/spring-core-5/src/main/java/com/baeldung/component/inscope/ServiceExample.java new file mode 100644 index 0000000000..e2db60504a --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/inscope/ServiceExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.inscope; + +import org.springframework.stereotype.Service; + +@Service +public class ServiceExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeBeanExample.java b/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeBeanExample.java new file mode 100644 index 0000000000..079c012862 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeBeanExample.java @@ -0,0 +1,4 @@ +package com.baeldung.component.outsidescope; + +public class OutsideScopeBeanExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeExample.java b/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeExample.java new file mode 100644 index 0000000000..5c7fff959d --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/outsidescope/OutsideScopeExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.outsidescope; + +import org.springframework.stereotype.Component; + +@Component +public class OutsideScopeExample { +} diff --git a/spring-core-5/src/main/java/com/baeldung/component/scannedscope/ScannedScopeExample.java b/spring-core-5/src/main/java/com/baeldung/component/scannedscope/ScannedScopeExample.java new file mode 100644 index 0000000000..59873634d6 --- /dev/null +++ b/spring-core-5/src/main/java/com/baeldung/component/scannedscope/ScannedScopeExample.java @@ -0,0 +1,7 @@ +package com.baeldung.component.scannedscope; + +import org.springframework.stereotype.Component; + +@Component +public class ScannedScopeExample { +} diff --git a/spring-core-5/src/main/resources/application.yml b/spring-core-5/src/main/resources/application.yml new file mode 100644 index 0000000000..5c09fdb8b0 --- /dev/null +++ b/spring-core-5/src/main/resources/application.yml @@ -0,0 +1 @@ +ambiguous-bean: 'A' \ No newline at end of file diff --git a/spring-core-5/src/test/java/com/baeldung/component/inscope/ComponentUnitTest.java b/spring-core-5/src/test/java/com/baeldung/component/inscope/ComponentUnitTest.java new file mode 100644 index 0000000000..09ce604d40 --- /dev/null +++ b/spring-core-5/src/test/java/com/baeldung/component/inscope/ComponentUnitTest.java @@ -0,0 +1,54 @@ +package com.baeldung.component.inscope; + +import com.baeldung.component.outsidescope.OutsideScopeBeanExample; +import com.baeldung.component.outsidescope.OutsideScopeExample; +import com.baeldung.component.scannedscope.ScannedScopeExample; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ExtendWith(SpringExtension.class) +public class ComponentUnitTest { + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void givenInScopeComponents_whenSearchingInApplicationContext_thenFindThem() { + assertNotNull(applicationContext.getBean(ControllerExample.class)); + assertNotNull(applicationContext.getBean(ServiceExample.class)); + assertNotNull(applicationContext.getBean(RepositoryExample.class)); + assertNotNull(applicationContext.getBean(ComponentExample.class)); + assertNotNull(applicationContext.getBean(CustomComponentExample.class)); + } + + @Test + public void givenOutsideScopeComponent_whenSearchingInApplicationContext_thenFail() { + assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(OutsideScopeExample.class)); + } + + @Test + public void givenScannedScopeComponent_whenSearchingInApplicationContext_thenFindIt() { + assertNotNull(applicationContext.getBean(ScannedScopeExample.class)); + } + + @Test + public void givenBeanComponents_whenSearchingInApplicationContext_thenFindThem() { + assertNotNull(applicationContext.getBean(BeanExample.class)); + assertNotNull(applicationContext.getBean(OutsideScopeBeanExample.class)); + } + + @Test + public void givenAmbiguousBeanSetToB_whenSearchingInApplicationContext_thenFindImplB() { + AmbiguousBean ambiguousBean = applicationContext.getBean(AmbiguousBean.class); + assertNotNull(ambiguousBean); + assertTrue(ambiguousBean instanceof BeanImplB); + } +} \ No newline at end of file diff --git a/spring-core-5/src/test/resources/application.yml b/spring-core-5/src/test/resources/application.yml new file mode 100644 index 0000000000..da23e59c24 --- /dev/null +++ b/spring-core-5/src/test/resources/application.yml @@ -0,0 +1 @@ +ambiguous-bean: 'B' \ No newline at end of file diff --git a/spring-security-modules/spring-5-security/pom.xml b/spring-security-modules/spring-5-security/pom.xml index 09de91491c..f50b5ff7a9 100644 --- a/spring-security-modules/spring-5-security/pom.xml +++ b/spring-security-modules/spring-5-security/pom.xml @@ -46,6 +46,21 @@ spring-security-test test + + org.owasp.esapi + esapi + 2.2.2.0 + + + org.jsoup + jsoup + 1.13.1 + + + commons-io + commons-io + 2.8.0 + diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Application.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Application.java new file mode 100644 index 0000000000..b463a7adc3 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Application.java @@ -0,0 +1,14 @@ +package com.baeldung.xss; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication +@EnableWebSecurity +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Person.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Person.java new file mode 100644 index 0000000000..1e7c02bae8 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/Person.java @@ -0,0 +1,36 @@ +package com.baeldung.xss; + +public class Person { + private String firstName; + private String lastName; + private int age; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "Person {" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + '}'; + } +} diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/PersonController.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/PersonController.java new file mode 100644 index 0000000000..8486e04e48 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/PersonController.java @@ -0,0 +1,31 @@ +package com.baeldung.xss; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.Map; + +@RestController +@RequestMapping("/personService") +public class PersonController { + + @PostMapping(value = "/person") + private ResponseEntity savePerson(@RequestHeader Map headers, + @RequestParam String param, @RequestBody Person body) { + ObjectNode response = JsonNodeFactory.instance.objectNode(); + headers.forEach((key, value) -> response.put(key, value)); + response.put("firstName", body.getFirstName()); + response.put("lastName", body.getLastName()); + response.put("age", body.getAge()); + response.put("param", param); + return new ResponseEntity(response.toString(), HttpStatus.OK); + } +} \ No newline at end of file diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/SecurityConf.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/SecurityConf.java new file mode 100644 index 0000000000..25d8026e4a --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/SecurityConf.java @@ -0,0 +1,25 @@ +package com.baeldung.xss; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConf extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) { + // Ignoring here is only for this example. Normally people would apply their own authentication/authorization policies + web.ignoring().antMatchers("/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + .xssProtection() + .and() + .contentSecurityPolicy("script-src 'self'"); + } +} diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSFilter.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSFilter.java new file mode 100644 index 0000000000..431ed4d120 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSFilter.java @@ -0,0 +1,44 @@ +package com.baeldung.xss; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import javax.servlet.Filter; +import javax.servlet.FilterConfig; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.ServletException; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class XSSFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void destroy() { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + XSSRequestWrapper wrappedRequest = new XSSRequestWrapper((HttpServletRequest) request); + + String body = IOUtils.toString(wrappedRequest.getReader()); + if (!StringUtils.isBlank(body)) { + body = XSSUtils.stripXSS(body); + wrappedRequest.resetInputStream(body.getBytes()); + } + + chain.doFilter(wrappedRequest, response); + } + +} \ No newline at end of file diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSRequestWrapper.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSRequestWrapper.java new file mode 100644 index 0000000000..8fe4e20b5c --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSRequestWrapper.java @@ -0,0 +1,123 @@ +package com.baeldung.xss; + +import org.apache.commons.codec.Charsets; +import org.apache.commons.io.IOUtils; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import static com.baeldung.xss.XSSUtils.stripXSS; + + +public class XSSRequestWrapper extends HttpServletRequestWrapper { + + private byte[] rawData; + private HttpServletRequest request; + private ResettableServletInputStream servletStream; + + public XSSRequestWrapper(HttpServletRequest request) { + super(request); + this.request = request; + this.servletStream = new ResettableServletInputStream(); + } + + public void resetInputStream(byte[] newRawData) { + rawData = newRawData; + servletStream.stream = new ByteArrayInputStream(newRawData); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (rawData == null) { + rawData = IOUtils.toByteArray(this.request.getReader(), Charsets.UTF_8); + servletStream.stream = new ByteArrayInputStream(rawData); + } + return servletStream; + } + + @Override + public BufferedReader getReader() throws IOException { + if (rawData == null) { + rawData = IOUtils.toByteArray(this.request.getReader(), Charsets.UTF_8); + servletStream.stream = new ByteArrayInputStream(rawData); + } + return new BufferedReader(new InputStreamReader(servletStream)); + } + + private class ResettableServletInputStream extends ServletInputStream { + + private InputStream stream; + + @Override + public int read() throws IOException { + return stream.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + } + + @Override + public String[] getParameterValues(String parameter) { + String[] values = super.getParameterValues(parameter); + if (values == null) { + return null; + } + int count = values.length; + String[] encodedValues = new String[count]; + for (int i = 0; i < count; i++) { + encodedValues[i] = stripXSS(values[i]); + } + return encodedValues; + } + + @Override + public String getParameter(String parameter) { + String value = super.getParameter(parameter); + return stripXSS(value); + } + + @Override + public String getHeader(String name) { + String value = super.getHeader(name); + return stripXSS(value); + } + + @Override + public Enumeration getHeaders(String name) { + List result = new ArrayList<>(); + Enumeration headers = super.getHeaders(name); + while (headers.hasMoreElements()) { + String header = headers.nextElement(); + String[] tokens = header.split(","); + for (String token : tokens) { + result.add(stripXSS(token)); + } + } + return Collections.enumeration(result); + } + +} diff --git a/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSUtils.java b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSUtils.java new file mode 100644 index 0000000000..51bcba8115 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/java/com/baeldung/xss/XSSUtils.java @@ -0,0 +1,19 @@ +package com.baeldung.xss; + +import org.jsoup.Jsoup; +import org.jsoup.safety.Whitelist; +import org.owasp.esapi.ESAPI; + +public class XSSUtils { + + public static String stripXSS(String value) { + if (value == null) { + return null; + } + value = ESAPI.encoder() + .canonicalize(value) + .replaceAll("\0", ""); + return Jsoup.clean(value, Whitelist.none()); + } + +} diff --git a/spring-security-modules/spring-5-security/src/main/resources/ESAPI.properties b/spring-security-modules/spring-5-security/src/main/resources/ESAPI.properties new file mode 100644 index 0000000000..a2746a4dbc --- /dev/null +++ b/spring-security-modules/spring-5-security/src/main/resources/ESAPI.properties @@ -0,0 +1,545 @@ +# +# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version +# +# This file is part of the Open Web Application Security Project (OWASP) +# Enterprise Security API (ESAPI) project. For details, please see +# https://owasp.org/www-project-enterprise-security-api/ +# +# Copyright (c) 2008,2009 - The OWASP Foundation +# +# DISCUSS: This may cause a major backwards compatibility issue, etc. but +# from a name space perspective, we probably should have prefaced +# all the property names with ESAPI or at least OWASP. Otherwise +# there could be problems is someone loads this properties file into +# the System properties. We could also put this file into the +# esapi.jar file (perhaps as a ResourceBundle) and then allow an external +# ESAPI properties be defined that would overwrite these defaults. +# That keeps the application's properties relatively simple as usually +# they will only want to override a few properties. If looks like we +# already support multiple override levels of this in the +# DefaultSecurityConfiguration class, but I'm suggesting placing the +# defaults in the esapi.jar itself. That way, if the jar is signed, +# we could detect if those properties had been tampered with. (The +# code to check the jar signatures is pretty simple... maybe 70-90 LOC, +# but off course there is an execution penalty (similar to the way +# that the separate sunjce.jar used to be when a class from it was +# first loaded). Thoughts? +############################################################################### +# +# WARNING: Operating system protection should be used to lock down the .esapi +# resources directory and all the files inside and all the directories all the +# way up to the root directory of the file system. Note that if you are using +# file-based implementations, that some files may need to be read-write as they +# get updated dynamically. +# +#=========================================================================== +# ESAPI Configuration +# +# If true, then print all the ESAPI properties set here when they are loaded. +# If false, they are not printed. Useful to reduce output when running JUnit tests. +# If you need to troubleshoot a properties related problem, turning this on may help. +# This is 'false' in the src/test/resources/.esapi version. It is 'true' by +# default for reasons of backward compatibility with earlier ESAPI versions. +ESAPI.printProperties=true + +# ESAPI is designed to be easily extensible. You can use the reference implementation +# or implement your own providers to take advantage of your enterprise's security +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like: +# +# String ciphertext = +# ESAPI.encryptor().encrypt("Secret message"); // Deprecated in 2.0 +# CipherText cipherText = +# ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred +# +# Below you can specify the classname for the provider that you wish to use in your +# application. The only requirement is that it implement the appropriate ESAPI interface. +# This allows you to switch security implementations in the future without rewriting the +# entire application. +# +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController +# FileBasedAuthenticator requires users.txt file in .esapi directory +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor + +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html +# Note that this is now considered deprecated! +ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory +#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +#ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory +# To use the new SLF4J logger in ESAPI (see GitHub issue #129), set +# ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory +# and do whatever other normal SLF4J configuration that you normally would do for your application. +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator + +#=========================================================================== +# ESAPI Authenticator +# +Authenticator.AllowedLoginAttempts=3 +Authenticator.MaxOldPasswordHashes=13 +Authenticator.UsernameParameterName=username +Authenticator.PasswordParameterName=password +# RememberTokenDuration (in days) +Authenticator.RememberTokenDuration=14 +# Session Timeouts (in minutes) +Authenticator.IdleTimeoutDuration=20 +Authenticator.AbsoluteTimeoutDuration=120 + +#=========================================================================== +# ESAPI Encoder +# +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks. +# Failure to canonicalize input is a very common mistake when implementing validation schemes. +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the +# following code to canonicalize data. +# +# ESAPI.Encoder().canonicalize( "%22hello world"" ); +# +# Multiple encoding is when a single encoding format is applied multiple times. Allowing +# multiple encoding is strongly discouraged. +Encoder.AllowMultipleEncoding=false + +# Mixed encoding is when multiple different encoding formats are applied, or when +# multiple formats are nested. Allowing multiple encoding is strongly discouraged. +Encoder.AllowMixedEncoding=false + +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important. +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec + + +#=========================================================================== +# ESAPI Encryption +# +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API. +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor +# There is not currently any support for key rotation, so be careful when changing your key and salt as it +# will invalidate all signed, encrypted, and hashed data. +# +# WARNING: Not all combinations of algorithms and key lengths are supported. +# If you choose to use a key length greater than 128, you MUST download the +# unlimited strength policy files and install in the lib directory of your JRE/JDK. +# See http://java.sun.com/javase/downloads/index.jsp for more information. +# +# ***** IMPORTANT: Do NOT forget to replace these with your own values! ***** +# To calculate these values, you can run: +# java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor +# +#Encryptor.MasterKey= +#Encryptor.MasterSalt= + +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric +# encryption and hashing. (That is it will look to this provider first, but it +# will defer to other providers if the requested algorithm is not implemented +# by this provider.) If left unset, ESAPI will just use your Java VM's current +# preferred JCE provider, which is generally set in the file +# "$JAVA_HOME/jre/lib/security/java.security". +# +# The main intent of this is to allow ESAPI symmetric encryption to be +# used with a FIPS 140-2 compliant crypto-module. For details, see the section +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in +# the ESAPI 2.0 Symmetric Encryption User Guide, at: +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html +# However, this property also allows you to easily use an alternate JCE provider +# such as "Bouncy Castle" without having to make changes to "java.security". +# See Javadoc for SecurityProviderLoader for further details. If you wish to use +# a provider that is not known to SecurityProviderLoader, you may specify the +# fully-qualified class name of the JCE provider class that implements +# java.security.Provider. If the name contains a '.', this is interpreted as +# a fully-qualified class name that implements java.security.Provider. +# +# NOTE: Setting this property has the side-effect of changing it in your application +# as well, so if you are using JCE in your application directly rather than +# through ESAPI (you wouldn't do that, would you? ;-), it will change the +# preferred JCE provider there as well. +# +# Default: Keeps the JCE provider set to whatever JVM sets it to. +Encryptor.PreferredJCEProvider= + +# AES is the most widely used and strongest encryption algorithm. This +# should agree with your Encryptor.CipherTransformation property. +# Warning: This property does not control the default reference implementation for +# ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped +# in the future. +# @deprecated +Encryptor.EncryptionAlgorithm=AES +# For ESAPI Java 2.0 - New encrypt / decrypt methods use this. +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding + +# Applies to ESAPI 2.0 and later only! +# Comma-separated list of cipher modes that provide *BOTH* +# confidentiality *AND* message authenticity. (NIST refers to such cipher +# modes as "combined modes" so that's what we shall call them.) If any of these +# cipher modes are used then no MAC is calculated and stored +# in the CipherText upon encryption. Likewise, if one of these +# cipher modes is used with decryption, no attempt will be made +# to validate the MAC contained in the CipherText object regardless +# of whether it contains one or not. Since the expectation is that +# these cipher modes support support message authenticity already, +# injecting a MAC in the CipherText object would be at best redundant. +# +# Note that as of JDK 1.5, the SunJCE provider does not support *any* +# of these cipher modes. Of these listed, only GCM and CCM are currently +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other +# padding modes. +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC + +# Applies to ESAPI 2.0 and later only! +# Additional cipher modes allowed for ESAPI 2.0 encryption. These +# cipher modes are in _addition_ to those specified by the property +# 'Encryptor.cipher_modes.combined_modes'. +# Note: We will add support for streaming modes like CFB & OFB once +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod' +# (probably in ESAPI 2.1). +# DISCUSS: Better name? +Encryptor.cipher_modes.additional_allowed=CBC + +# Default key size to use for cipher specified by Encryptor.EncryptionAlgorithm. +# Note that this MUST be a valid key size for the algorithm being used +# (as specified by Encryptor.EncryptionAlgorithm). So for example, if AES is used, +# it must be 128, 192, or 256. If DESede is chosen, then it must be either 112 or 168. +# +# Note that 128-bits is almost always sufficient and for AES it appears to be more +# somewhat more resistant to related key attacks than is 256-bit AES.) +# +# Defaults to 128-bits if left blank. +# +# NOTE: If you use a key size > 128-bits, then you MUST have the JCE Unlimited +# Strength Jurisdiction Policy files installed!!! +# +Encryptor.EncryptionKeyLength=128 + +# This is the _minimum_ key size (in bits) that we allow with ANY symmetric +# cipher for doing encryption. (There is no minimum for decryption.) +# +# Generally, if you only use one algorithm, this should be set the same as +# the Encryptor.EncryptionKeyLength property. +Encryptor.MinEncryptionKeyLength=128 + +# Because 2.x uses CBC mode by default, it requires an initialization vector (IV). +# (All cipher modes except ECB require an IV.) There are two choices: we can either +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While +# the IV does not need to be hidden from adversaries, it is important that the +# adversary not be allowed to choose it. Also, random IVs are generally much more +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes +# such as CFB and OFB use a different IV for each encryption with a given key so +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and +# uncomment the Encryptor.fixedIV. +# +# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.3 +# 'fixed' is deprecated as of 2.2 +# and will be removed in 2.3. +Encryptor.ChooseIVMethod=random + + +# If you choose to use a fixed IV, then you must place a fixed IV here that +# is known to all others who are sharing your secret key. The format should +# be a hex string that is the same length as the cipher block size for the +# cipher algorithm that you are using. The following is an *example* for AES +# from an AES test vector for AES-128/CBC as described in: +# NIST Special Publication 800-38A (2001 Edition) +# "Recommendation for Block Cipher Modes of Operation". +# (Note that the block size for AES is 16 bytes == 128 bits.) +# +# @Deprecated -- fixed IVs are deprecated as of the 2.2 release and support +# will be removed in the next release (tentatively, 2.3). +# If you MUST use this, at least replace this IV with one +# that your legacy application was using. +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f + +# Whether or not CipherText should use a message authentication code (MAC) with it. +# This prevents an adversary from altering the IV as well as allowing a more +# fool-proof way of determining the decryption failed because of an incorrect +# key being supplied. This refers to the "separate" MAC calculated and stored +# in CipherText, not part of any MAC that is calculated as a result of a +# "combined mode" cipher mode. +# +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also +# set this property to false. That is because ESAPI takes the master key and +# derives 2 keys from it--a key for the MAC and a key for encryption--and +# because ESAPI is not itself FIPS 140-2 verified such intermediary aterations +# to keys from FIPS approved sources would have the effect of making your FIPS +# approved key generation and thus your FIPS approved JCE provider unapproved! +# More details in +# documentation/esapi4java-core-2.0-readme-crypto-changes.html +# documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html +# You have been warned. +Encryptor.CipherText.useMAC=true + +# Whether or not the PlainText object may be overwritten and then marked +# eligible for garbage collection. If not set, this is still treated as 'true'. +Encryptor.PlainText.overwrite=true + +# Do not use DES except in a legacy situations. 56-bit is way too small key size. +#Encryptor.EncryptionKeyLength=56 +#Encryptor.MinEncryptionKeyLength=56 +#Encryptor.EncryptionAlgorithm=DES + +# TripleDES is considered strong enough for most purposes. +# Note: There is also a 112-bit version of DESede. Using the 168-bit version +# requires downloading the special jurisdiction policy from Sun. +#Encryptor.EncryptionKeyLength=168 +#Encryptor.MinEncryptionKeyLength=112 +#Encryptor.EncryptionAlgorithm=DESede + +Encryptor.HashAlgorithm=SHA-512 +Encryptor.HashIterations=1024 +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA +Encryptor.DigitalSignatureKeyLength=1024 +Encryptor.RandomAlgorithm=SHA1PRNG +Encryptor.CharacterEncoding=UTF-8 + +# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function +# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and +# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for +# the MAC, mostly to keep the overall size at a minimum.) +# +# Currently supported choices for JDK 1.5 and 1.6 are: +# HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and +# HmacSHA512 (512 bits). +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though +# the JDKs support it. See the ESAPI 2.0 Symmetric Encryption User Guide +# further details. +Encryptor.KDF.PRF=HmacSHA256 +#=========================================================================== +# ESAPI HttpUtilties +# +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods +# protect against malicious data from attackers, such as unprintable characters, escaped characters, +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies, +# headers, and CSRF tokens. +# +# Default file upload location (remember to escape backslashes with \\) +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload +HttpUtilities.UploadTempDir=C:\\temp +# Force flags on cookies, if you use HttpUtilities to set cookies +HttpUtilities.ForceHttpOnlySession=false +HttpUtilities.ForceSecureSession=false +HttpUtilities.ForceHttpOnlyCookies=true +HttpUtilities.ForceSecureCookies=true +# Maximum size of HTTP header key--the validator regex may have additional values. +HttpUtilities.MaxHeaderNameSize=256 +# Maximum size of HTTP header value--the validator regex may have additional values. +HttpUtilities.MaxHeaderValueSize=4096 +# Maximum size of JSESSIONID for the application--the validator regex may have additional values. +HttpUtilities.HTTPJSESSIONIDLENGTH=50 +# Maximum length of a URL (see https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers) +HttpUtilities.URILENGTH=2000 +# Maximum length of a redirect +HttpUtilities.maxRedirectLength=512 +# Maximum length for an http scheme +HttpUtilities.HTTPSCHEMELENGTH=10 +# Maximum length for an http host +HttpUtilities.HTTPHOSTLENGTH=100 +# Maximum length for an http path +HttpUtilities.HTTPPATHLENGTH=150 +#Maximum length for a context path +HttpUtilities.contextPathLength=150 +#Maximum length for an httpServletPath +HttpUtilities.HTTPSERVLETPATHLENGTH=100 +#Maximum length for an http query parameter name +HttpUtilities.httpQueryParamNameLength=100 +#Maximum length for an http query parameter -- old default was 2000, but that's the max length for a URL... +HttpUtilities.httpQueryParamValueLength=500 +# File upload configuration +HttpUtilities.ApprovedUploadExtensions=.pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.rtf,.txt,.jpg,.png +HttpUtilities.MaxUploadFileBytes=500000000 +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver, +# container, and any other technologies you may be using. Failure to do this may expose you +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization. +HttpUtilities.ResponseContentType=text/html; charset=UTF-8 +# This is the name of the cookie used to represent the HTTP session +# Typically this will be the default "JSESSIONID" +HttpUtilities.HttpSessionIdName=JSESSIONID +#Sets whether or not we will overwrite http status codes to 200. +HttpUtilities.OverwriteStatusCodes=true +#Sets the application's base character encoding. This is forked from the Java Encryptor property. +HttpUtilities.CharacterEncoding=UTF-8 + +#=========================================================================== +# ESAPI Executor +# CHECKME - This should be made OS independent. Don't use unsafe defaults. +# # Examples only -- do NOT blindly copy! +# For Windows: +# Executor.WorkingDirectory=C:\\Windows\\Temp +# Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe +# For *nux, MacOS: +# Executor.WorkingDirectory=/tmp +# Executor.ApprovedExecutables=/bin/bash +Executor.WorkingDirectory= +Executor.ApprovedExecutables= + + +#=========================================================================== +# ESAPI Logging +# Set the application name if these logs are combined with other applications +Logger.ApplicationName=ExampleApplication +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true +Logger.LogEncodingRequired=false +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. +Logger.LogApplicationName=true +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. +Logger.LogServerIP=true +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you +# want to place it in a specific directory. +Logger.LogFileName=ESAPI_logging_file +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) +Logger.MaxLogFileSize=10000000 +# Determines whether ESAPI should log the user info. +Logger.UserInfo=true +# Determines whether ESAPI should log the session id and client IP. +Logger.ClientInfo=true + +#=========================================================================== +# ESAPI Intrusion Detection +# +# Each event has a base to which .count, .interval, and .action are added +# The IntrusionException will fire if we receive "count" events within "interval" seconds +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable +# (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable +# +# Custom Events +# Names must start with "event." as the base +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here +# You can also disable intrusion detection completely by changing +# the following parameter to true +# +IntrusionDetector.Disable=false +# +IntrusionDetector.event.test.count=2 +IntrusionDetector.event.test.interval=10 +IntrusionDetector.event.test.actions=disable,log + +# Exception Events +# All EnterpriseSecurityExceptions are registered automatically +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException +# Use the fully qualified classname of the exception as the base + +# any intrusion is an attack +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1 +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1 +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout + +# for test purposes +# CHECKME: Shouldn't there be something in the property name itself that designates +# that these are for testing??? +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10 +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5 +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout + +# rapid validation errors indicate scans or attacks in progress +# org.owasp.esapi.errors.ValidationException.count=10 +# org.owasp.esapi.errors.ValidationException.interval=10 +# org.owasp.esapi.errors.ValidationException.actions=log,logout + +# sessions jumping between hosts indicates session hijacking +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2 +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10 +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout + + +#=========================================================================== +# ESAPI Validation +# +# The ESAPI Validator works on regular expressions with defined names. You can define names +# either here, or you may define application specific patterns in a separate file defined below. +# This allows enterprises to specify both organizational standards as well as application specific +# validation rules. +# +# Use '\p{L}' (without the quotes) within the character class to match +# any Unicode LETTER. You can also use a range, like: \u00C0-\u017F +# You can also use any of the regex flags as documented at +# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u) +# +Validator.ConfigurationFile=validation.properties + +# Validators used by ESAPI +Validator.AccountName=^[a-zA-Z0-9]{3,20}$ +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$ +Validator.RoleName=^[a-z]{1,20}$ + +#the word TEST below should be changed to your application +#name - only relative URL's are supported +Validator.Redirect=^\\/test.*$ + +# Global HTTP Validation Rules +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=] +Validator.HTTPScheme=^(http|https)$ +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$ +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ +# Note that headerName and Value length is also configured in the HTTPUtilities section +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,256}$ +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ +Validator.HTTPURL=^.*$ +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,32}$ + + +# Contributed by Fraenku@gmx.ch +# Github Issue 126 https://github.com/ESAPI/esapi-java-legacy/issues/126 +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$ +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$ +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$ +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$ +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$ + + +# Validation of file related input +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ + +# Validation of dates. Controls whether or not 'lenient' dates are accepted. +# See DataFormat.setLenient(boolean flag) for further details. +Validator.AcceptLenientDates=false + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw + +# With the fix for #310 to enable loading antisamy-esapi.xml from the classpath +# also an enhancement was made to be able to use a different filename for the configuration. +# You don't have to configure the filename here, but in that case the code will keep looking for antisamy-esapi.xml. +# This is the default behaviour of ESAPI. +# +#Validator.HtmlValidationConfigurationFile=antisamy-esapi.xml \ No newline at end of file diff --git a/spring-security-modules/spring-5-security/src/test/java/com/baeldung/xss/PersonControllerUnitTest.java b/spring-security-modules/spring-5-security/src/test/java/com/baeldung/xss/PersonControllerUnitTest.java new file mode 100644 index 0000000000..4e278ebf16 --- /dev/null +++ b/spring-security-modules/spring-5-security/src/test/java/com/baeldung/xss/PersonControllerUnitTest.java @@ -0,0 +1,64 @@ +package com.baeldung.xss; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class PersonControllerUnitTest { + + @LocalServerPort + int randomServerPort; + + @Test + public void givenRequestIsSuspicious_whenRequestIsPost_thenResponseIsClean() + throws IOException { + // given + String createPersonUrl; + RestTemplate restTemplate; + HttpHeaders headers; + UriComponentsBuilder builder; + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode personJsonObject = JsonNodeFactory.instance.objectNode(); + createPersonUrl = "http://localhost:" + randomServerPort + "/personService/person"; + restTemplate = new RestTemplate(); + headers = new HttpHeaders(); + + // when + personJsonObject.put("id", 1); + personJsonObject.put("firstName", "baeldung "); + personJsonObject.put("lastName", "baeldung click me!"); + + builder = UriComponentsBuilder.fromHttpUrl(createPersonUrl) + .queryParam("param", ""); + headers.add("header_4", "

Your search for 'flowers '"); + HttpEntity request = new HttpEntity<>(personJsonObject.toString(), headers); + + ResponseEntity personResultAsJsonStr = restTemplate.exchange(builder.toUriString(), + HttpMethod.POST, request, String.class); + JsonNode root = objectMapper.readTree(personResultAsJsonStr.getBody()); + + // then + assertThat(root.get("firstName").textValue()).isEqualTo("baeldung "); + assertThat(root.get("lastName").textValue()).isEqualTo("baeldung click me!"); + assertThat(root.get("param").textValue()).isEmpty(); + assertThat(root.get("header_1").textValue()).isEmpty(); + assertThat(root.get("header_2").textValue()).isEmpty(); + assertThat(root.get("header_3").textValue()).isEmpty(); + assertThat(root.get("header_4").textValue()).isEqualTo("Your search for 'flowers '"); + } +} diff --git a/spring-security-modules/spring-session/spring-session-jdbc/README.md b/spring-security-modules/spring-session/spring-session-jdbc/README.md index a31ee044e8..6af3f53137 100644 --- a/spring-security-modules/spring-session/spring-session-jdbc/README.md +++ b/spring-security-modules/spring-session/spring-session-jdbc/README.md @@ -3,5 +3,3 @@ This module contains articles about Spring Session with JDBC. ### Relevant Articles: - -- [Spring Session with JDBC](https://www.baeldung.com/spring-session-jdbc) diff --git a/spring-web-modules/spring-mvc-basics-4/README.md b/spring-web-modules/spring-mvc-basics-4/README.md index d0bca4a303..211564a363 100644 --- a/spring-web-modules/spring-mvc-basics-4/README.md +++ b/spring-web-modules/spring-mvc-basics-4/README.md @@ -5,7 +5,7 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring ### Relevant Articles: - [Quick Guide to Spring Controllers](https://www.baeldung.com/spring-controllers) -- [Model, ModelMap, and ModelView in Spring MVC](https://www.baeldung.com/spring-mvc-model-model-map-model-view) +- [Model, ModelMap, and ModelAndView in Spring MVC](https://www.baeldung.com/spring-mvc-model-model-map-model-view) - [Spring Web Contexts](https://www.baeldung.com/spring-web-contexts) - [Spring Optional Path variables](https://www.baeldung.com/spring-optional-path-variables) - [JSON Parameters with Spring MVC](https://www.baeldung.com/spring-mvc-send-json-parameters) 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-resttemplate-2/README.md b/spring-web-modules/spring-resttemplate-2/README.md index a903757bb4..ace7ae817b 100644 --- a/spring-web-modules/spring-resttemplate-2/README.md +++ b/spring-web-modules/spring-resttemplate-2/README.md @@ -10,3 +10,4 @@ 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) +- [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/stripe/README.md b/stripe/README.md index 9e41dcf945..36f0d6e3f3 100644 --- a/stripe/README.md +++ b/stripe/README.md @@ -5,4 +5,4 @@ This module contains articles about Stripe ### Relevant articles - [Introduction to the Stripe API for Java](https://www.baeldung.com/java-stripe-api) - +- [Viewing Contents of a JAR File](https://www.baeldung.com/java-view-jar-contents)