diff --git a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/MultilineUnitTest.java b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/MultilineUnitTest.java new file mode 100644 index 0000000000..4efd1ff362 --- /dev/null +++ b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/MultilineUnitTest.java @@ -0,0 +1,26 @@ +package com.baeldung.java14.newfeatues; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class MultilineUnitTest { + + @SuppressWarnings("preview") + String multiline = """ + A quick brown fox jumps over a lazy dog; \ + the lazy dog howls loudly."""; + + @SuppressWarnings("preview") + String anotherMultiline = """ + A quick brown fox jumps over a lazy dog; + the lazy dog howls loudly."""; + + @Test + public void givenMultilineString_whenSlashUsed_thenNoNewLine() { + assertFalse(multiline.contains("\n")); + assertTrue(anotherMultiline.contains("\n")); + } + +} diff --git a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/RecordUnitTest.java b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/RecordUnitTest.java new file mode 100644 index 0000000000..3d7a55d3e6 --- /dev/null +++ b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/RecordUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.java14.newfeatues; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class RecordUnitTest { + + @SuppressWarnings("preview") + public record User(int id, String password) { + }; + + private User user1 = new User(0, "UserOne"); + + @Test + public void givenRecord_whenObjInitialized_thenValuesCanBeFetchedWithGetters() { + + assertEquals(0, user1.id()); + assertEquals("UserOne", user1.password()); + } + + @Test + public void whenRecord_thenEqualsImplemented() { + + User user2 = user1; + + assertEquals(user1, user2); + } + + @Test + public void whenRecord_thenToStringImplemented() { + + assertTrue(user1.toString() + .contains("UserOne")); + } + +} diff --git a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/SwitchExprUnitTest.java b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/SwitchExprUnitTest.java new file mode 100644 index 0000000000..896b3ec7de --- /dev/null +++ b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/newfeatues/SwitchExprUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.java14.newfeatues; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class SwitchExprUnitTest { + + @Test + public void givenDay_whenSunday_thenWeekend() { + assertTrue(isTodayHolidayInJava8("SUNDAY")); + + assertTrue(isTodayHolidayInJava14("SUNDAY")); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> isTodayHolidayInJava8("SOMEDAY")); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> isTodayHolidayInJava14("SOMEDAY")); + } + + private boolean isTodayHolidayInJava8(String day) { + + boolean isTodayHoliday; + switch (day) { + case "MONDAY": + case "TUESDAY": + case "WEDNESDAY": + case "THURSDAY": + case "FRIDAY": + isTodayHoliday = false; + break; + case "SATURDAY": + case "SUNDAY": + isTodayHoliday = true; + break; + default: + throw new IllegalArgumentException("What's a " + day); + } + return isTodayHoliday; + } + + private boolean isTodayHolidayInJava14(String day) { + return switch (day) { + case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> false; + case "SATURDAY", "SUNDAY" -> true; + default -> throw new IllegalArgumentException("What's a " + day); + }; + } + +} diff --git a/core-java-modules/core-java-collections-4/README.md b/core-java-modules/core-java-collections-4/README.md new file mode 100644 index 0000000000..6e117c98b1 --- /dev/null +++ b/core-java-modules/core-java-collections-4/README.md @@ -0,0 +1,7 @@ +========= + +## Core Java Collections Cookbooks and Examples + +### Relevant Articles: + +- [ArrayList vs. LinkedList vs. HashMap in Java](https://www.baeldung.com/java-arraylist-vs-linkedlist-vs-hashmap) diff --git a/core-java-modules/core-java-collections-4/pom.xml b/core-java-modules/core-java-collections-4/pom.xml new file mode 100644 index 0000000000..0e3cabf40e --- /dev/null +++ b/core-java-modules/core-java-collections-4/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + core-java-collections-4 + 0.1.0-SNAPSHOT + core-java-collections-4 + jar + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + ../pom.xml + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + 3.18.0 + + + diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ArrayListUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ArrayListUnitTest.java new file mode 100644 index 0000000000..bc6a07d274 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ArrayListUnitTest.java @@ -0,0 +1,30 @@ +package com.baeldung.collections.comparation; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ArrayListUnitTest { + + @Test + void givenArrayList_whenItemAddedToSpecificIndex_thenItCanBeRetrieved() { + List list = new ArrayList<>(); + list.add("Daniel"); + list.add(0, "Marko"); + assertThat(list).hasSize(2); + assertThat(list.get(0)).isEqualTo("Marko"); + } + + @Test + void givenArrayList_whenItemRemovedViaIndex_thenListSizeIsReduced() { + List list = new ArrayList<>(Arrays.asList("Daniel", "Marko")); + list.remove(1); + assertThat(list).hasSize(1); + assertThat(list).doesNotContain("Marko"); + } + +} diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/HashMapUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/HashMapUnitTest.java new file mode 100644 index 0000000000..3b595472e0 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/HashMapUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.collections.comparation; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashMapUnitTest { + + @Test + void givenHashMap_whenItemAddedByKey_thenItCanBeRetrieved() { + Map map = new HashMap<>(); + map.put("123456", "Daniel"); + map.put("654321", "Marko"); + assertThat(map.get("654321")).isEqualTo("Marko"); + } + + @Test + void givenHashMap_whenItemRemovedByKey_thenMapSizeIsReduced() { + Map map = new HashMap<>(); + map.put("123456", "Daniel"); + map.put("654321", "Marko"); + map.remove("654321"); + assertThat(map).hasSize(1); + } + +} diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/LinkedListUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/LinkedListUnitTest.java new file mode 100644 index 0000000000..aa6b7fa923 --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/LinkedListUnitTest.java @@ -0,0 +1,41 @@ +package com.baeldung.collections.comparation; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LinkedListUnitTest { + + @Test + void givenLinkedList_whenItemIsAppended_thenItCanBeRetrieved() { + LinkedList list = new LinkedList<>(); + list.addLast("Daniel"); + list.addFirst("Marko"); + assertThat(list).hasSize(2); + assertThat(list.getLast()).isEqualTo("Daniel"); + } + + @Test + void givenLinkedList_whenItemIsRemoved_thenListSizeIsReduced() { + LinkedList list = new LinkedList<>(Arrays.asList("Daniel", "Marko", "David")); + list.removeFirst(); + list.removeLast(); + assertThat(list).hasSize(1); + assertThat(list).containsExactly("Marko"); + } + + @Test + void givenLinkedList_whenItemInserted_thenItCanBeRetrievedAndDeleted() { + LinkedList list = new LinkedList<>(); + list.push("Daniel"); + list.push("Marko"); + assertThat(list.poll()).isEqualTo("Marko"); + assertThat(list).hasSize(1); + } + +} diff --git a/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ListVsMapUnitTest.java b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ListVsMapUnitTest.java new file mode 100644 index 0000000000..dd6bf760fd --- /dev/null +++ b/core-java-modules/core-java-collections-4/src/test/java/com/baeldung/collections/comparation/ListVsMapUnitTest.java @@ -0,0 +1,32 @@ +package com.baeldung.collections.comparation; + +import static org.assertj.core.api.Assertions.*; +import org.junit.jupiter.api.Test; + +import java.util.*; + +class ListVsMapUnitTest { + + @Test + void givenList_whenIteratingTroughValues_thenEachValueIsPresent() { + List list = new ArrayList<>(); + list.add("Daniel"); + list.add("Marko"); + for (String name : list) { + assertThat(name).isIn(list); + } + assertThat(list).containsExactly("Daniel", "Marko"); + } + + @Test + void givenMap_whenIteratingTroughValues_thenEachValueIsPresent() { + Map map = new HashMap<>(); + map.put(1, "Daniel"); + map.put(2, "Marko"); + for (String name : map.values()) { + assertThat(name).isIn(map.values()); + } + assertThat(map.values()).containsExactlyInAnyOrder("Daniel", "Marko"); + } + +} diff --git a/core-java-modules/core-java-io-3/README.md b/core-java-modules/core-java-io-3/README.md index 36617a210e..d0ac5387a8 100644 --- a/core-java-modules/core-java-io-3/README.md +++ b/core-java-modules/core-java-io-3/README.md @@ -13,4 +13,5 @@ This module contains articles about core Java input and output (IO) - [Reading a Line at a Given Line Number From a File in Java](https://www.baeldung.com/java-read-line-at-number) - [Find the Last Modified File in a Directory with Java](https://www.baeldung.com/java-last-modified-file) - [Get a Filename Without the Extension in Java](https://www.baeldung.com/java-filename-without-extension) +- [Writing byte[] to a File in Java](https://www.baeldung.com/java-write-byte-array-file) - [[<-- Prev]](/core-java-modules/core-java-io-2) diff --git a/core-java-modules/core-java-io-3/src/test/java/com/baeldung/writebytearray/WriteByteArrayUnitTest.java b/core-java-modules/core-java-io-3/src/test/java/com/baeldung/writebytearray/WriteByteArrayUnitTest.java new file mode 100644 index 0000000000..ef8c8e2470 --- /dev/null +++ b/core-java-modules/core-java-io-3/src/test/java/com/baeldung/writebytearray/WriteByteArrayUnitTest.java @@ -0,0 +1,77 @@ +package com.baeldung.writebytearray; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import org.apache.commons.io.FileUtils; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import com.google.common.io.ByteSink; +import com.google.common.io.MoreFiles; + +public class WriteByteArrayUnitTest { + private static byte[] dataForWriting; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @BeforeClass + public static void setup() throws IOException { + dataForWriting = Files.readAllBytes(Paths.get("src/test/resources/example-image.jpg")); + } + + @Test + public void whenUsingFileOutputStream_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-fos.jpg"); + try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { + outputStream.write(dataForWriting); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } + } + + @Test + public void whenUsingNioFiles_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-nio-files.jpg"); + Files.write(outputFile.toPath(), dataForWriting); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } + + @Test + public void whenUsingGuavaFiles_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-guava-files.jpg"); + com.google.common.io.Files.write(dataForWriting, outputFile); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } + + @Test + public void whenUsingGuavaByteSink_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-guava-bs.jpg"); + ByteSink byteSink = com.google.common.io.Files.asByteSink(outputFile); + byteSink.write(dataForWriting); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } + + @Test + public void whenUsingGuavaByteSinkMoreFiles_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-guava-bs.jpg"); + ByteSink byteSink = MoreFiles.asByteSink(outputFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + byteSink.write(dataForWriting); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } + + @Test + public void whenUsingCommonsIo_thenByteArrayIsWritten() throws IOException { + File outputFile = tempFolder.newFile("example-file-utils.jpg"); + FileUtils.writeByteArrayToFile(outputFile, dataForWriting); + assertThat(outputFile).hasBinaryContent(dataForWriting); + } +} diff --git a/core-java-modules/core-java-io-3/src/test/resources/example-image.jpg b/core-java-modules/core-java-io-3/src/test/resources/example-image.jpg new file mode 100644 index 0000000000..43b8962793 Binary files /dev/null and b/core-java-modules/core-java-io-3/src/test/resources/example-image.jpg differ diff --git a/core-java-modules/core-java-security-2/README.md b/core-java-modules/core-java-security-2/README.md index bb5e204c98..9b99d624c9 100644 --- a/core-java-modules/core-java-security-2/README.md +++ b/core-java-modules/core-java-security-2/README.md @@ -14,4 +14,5 @@ This module contains articles about core Java Security - [Get a List of Trusted Certificates in Java](https://www.baeldung.com/java-list-trusted-certificates) - [Security Context Basics: User, Subject and Principal](https://www.baeldung.com/security-context-basics) - [Java AES Encryption and Decryption](https://www.baeldung.com/java-aes-encryption-decryption) +- [InvalidAlgorithmParameterException: Wrong IV Length](https://www.baeldung.com/java-invalidalgorithmparameter-exception) - More articles: [[<-- prev]](/core-java-modules/core-java-security) diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index 2f3965b237..0a9e818156 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -34,6 +34,7 @@ core-java-collections core-java-collections-2 core-java-collections-3 + core-java-collections-4 core-java-collections-array-list core-java-collections-list core-java-collections-list-2 diff --git a/java-collections-maps-3/src/main/java/com/baeldung/map/bytearrays/BytesKey.java b/java-collections-maps-3/src/main/java/com/baeldung/map/bytearrays/BytesKey.java new file mode 100644 index 0000000000..4bdcfe4b06 --- /dev/null +++ b/java-collections-maps-3/src/main/java/com/baeldung/map/bytearrays/BytesKey.java @@ -0,0 +1,28 @@ +package com.baeldung.map.bytearrays; + +import java.util.Arrays; + +public final class BytesKey { + private final byte[] array; + + public BytesKey(byte[] array) { + this.array = array; + } + + public byte[] getArray() { + return array.clone(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BytesKey bytesKey = (BytesKey) o; + return Arrays.equals(array, bytesKey.array); + } + + @Override + public int hashCode() { + return Arrays.hashCode(array); + } +} diff --git a/java-collections-maps-3/src/test/java/com/baeldung/map/bytearrays/ByteArrayKeyUnitTest.java b/java-collections-maps-3/src/test/java/com/baeldung/map/bytearrays/ByteArrayKeyUnitTest.java new file mode 100644 index 0000000000..8f5b89e11e --- /dev/null +++ b/java-collections-maps-3/src/test/java/com/baeldung/map/bytearrays/ByteArrayKeyUnitTest.java @@ -0,0 +1,104 @@ +package com.baeldung.map.bytearrays; + +import com.google.common.collect.ImmutableList; +import org.junit.jupiter.api.Test; + +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class ByteArrayKeyUnitTest { + @Test + void givenPrimitiveByteArrayKey_whenRetrievingFromMap_shouldRetrieveDifferentObjects() { + // given + byte[] key1 = {1, 2, 3}; + byte[] key2 = {1, 2, 3}; + String value1 = "value1"; + String value2 = "value2"; + Map map = new HashMap<>(); + map.put(key1, value1); + map.put(key2, value2); + + // when + String retrievedValue1 = map.get(key1); + String retrievedValue2 = map.get(key2); + String retrievedValue3 = map.get(new byte[]{1, 2, 3}); + + // then + assertThat(retrievedValue1).isEqualTo(value1); + assertThat(retrievedValue2).isEqualTo(value2); + assertThat(retrievedValue3).isNull(); + } + + @Test + void givenEncodedStringKey_whenRetrievingFromMap_shouldRetrieveLastPutObject() { + // given + String key1 = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); + String key2 = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); + String value1 = "value1"; + String value2 = "value2"; + Map map = new HashMap<>(); + map.put(key1, value1); + map.put(key2, value2); + + // when + String retrievedValue1 = map.get(key1); + String retrievedValue2 = map.get(key2); + + // then + assertThat(key1).isEqualTo(key2); + assertThat(retrievedValue1).isEqualTo(value2); + assertThat(retrievedValue2).isEqualTo(value2); + assertThat(retrievedValue1).isEqualTo(retrievedValue2); + } + + @Test + void givenByteListKey_whenRetrievingFromMap_shouldRetrieveLastPutObject() { + // given + List key1 = ImmutableList.of((byte)1, (byte)2, (byte)3); + List key2 = ImmutableList.of((byte)1, (byte)2, (byte)3); + String value1 = "value1"; + String value2 = "value2"; + Map, String> map = new HashMap<>(); + map.put(key1, value1); + map.put(key2, value2); + + // when + String retrievedValue1 = map.get(key1); + String retrievedValue2 = map.get(key2); + + // then + assertThat(key1).isEqualTo(key2); + assertThat(retrievedValue1).isEqualTo(value2); + assertThat(retrievedValue2).isEqualTo(value2); + assertThat(retrievedValue1).isEqualTo(retrievedValue2); + } + + @Test + void givenCustomWrapperKey_whenRetrievingFromMap_shouldRetrieveLastPutObject() { + // given + BytesKey key1 = new BytesKey(new byte[]{1, 2, 3}); + BytesKey key2 = new BytesKey(new byte[]{1, 2, 3}); + String value1 = "value1"; + String value2 = "value2"; + Map map = new HashMap<>(); + map.put(key1, value1); + map.put(key2, value2); + + // when + String retrievedValue1 = map.get(key1); + String retrievedValue2 = map.get(key2); + String retrievedValue3 = map.get(new BytesKey(new byte[]{1, 2, 3})); + + // then + assertThat(key1).isEqualTo(key2); + assertThat(retrievedValue1).isEqualTo(value2); + assertThat(retrievedValue2).isEqualTo(value2); + assertThat(retrievedValue1).isEqualTo(retrievedValue2); + assertThat(retrievedValue3).isEqualTo(value2); + + } +} diff --git a/java-numbers-4/src/test/java/com/baeldung/perfectsquare/PerfectSquareUnitTest.java b/java-numbers-4/src/test/java/com/baeldung/perfectsquare/PerfectSquareUnitTest.java index 334b95ea9d..83fce31047 100644 --- a/java-numbers-4/src/test/java/com/baeldung/perfectsquare/PerfectSquareUnitTest.java +++ b/java-numbers-4/src/test/java/com/baeldung/perfectsquare/PerfectSquareUnitTest.java @@ -6,22 +6,21 @@ import static org.junit.Assert.assertEquals; public class PerfectSquareUnitTest { @Test - public void test0xFFAssignedToInteger() { + public void testIsNumberPerfectSquare() { long n = 18676209273604L; //‬ 18676209273604 = 43621598 * 43621598 boolean expectedValue = true; assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingSqrt(n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingBinarySearch(1, Integer.MAX_VALUE, n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingNewtonMethod(n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareWithOptimization(n)); - } - @Test - public void test0xFFAssignedToByte() { - long n = 549790047707L; // prime number - boolean expectedValue = false; + n = 549790047707L; // prime number + expectedValue = false; assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingSqrt(n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingBinarySearch(1, Integer.MAX_VALUE, n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareByUsingNewtonMethod(n)); assertEquals(expectedValue, PerfectSquareUtil.isPerfectSquareWithOptimization(n)); } + + } diff --git a/persistence-modules/core-java-persistence-2/README.md b/persistence-modules/core-java-persistence-2/README.md index 467de757ce..36c33cc6e1 100644 --- a/persistence-modules/core-java-persistence-2/README.md +++ b/persistence-modules/core-java-persistence-2/README.md @@ -1,3 +1,4 @@ ### Relevant Articles: - [Getting Database URL From JDBC Connection Object](https://www.baeldung.com/jdbc-get-url-from-connection) +- [JDBC URL Format For Different Databases](https://www.baeldung.com/java-jdbc-url-format) diff --git a/persistence-modules/core-java-persistence-2/pom.xml b/persistence-modules/core-java-persistence-2/pom.xml index 9845d5009d..a1088b0801 100644 --- a/persistence-modules/core-java-persistence-2/pom.xml +++ b/persistence-modules/core-java-persistence-2/pom.xml @@ -20,10 +20,43 @@ h2 ${h2.version} + + org.postgresql + postgresql + ${postgresql.version} + test + + + mysql + mysql-connector-java + ${mysql.driver.version} + + + + com.microsoft.sqlserver + mssql-jdbc + ${mssql.driver.version} + + 1.4.200 + 42.2.5.jre7 + 8.4.1.jre11 + 10.2.0.4.0 + 8.0.22 - \ No newline at end of file + diff --git a/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcurlformat/JdbcUrlFormatLiveTest.java b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcurlformat/JdbcUrlFormatLiveTest.java new file mode 100644 index 0000000000..fc00119704 --- /dev/null +++ b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcurlformat/JdbcUrlFormatLiveTest.java @@ -0,0 +1,86 @@ +package com.baeldung.jdbcurlformat; + +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import static org.junit.Assert.assertNotNull; + +public class JdbcUrlFormatLiveTest { + @Test + public void givenOracleSID_thenCreateConnectionObject() { + String oracleJdbcUrl = "jdbc:oracle:thin:@myoracle.db.server:1521:my_sid"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(oracleJdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } + + @Test + public void givenOracleServiceName_thenCreateConnectionObject() { + String oracleJdbcUrl = "jdbc:oracle:thin:@//myoracle.db.server:1521/my_servicename"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(oracleJdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } + + @Test + public void givenOracleTnsnames_thenCreateConnectionObject() { + String oracleJdbcUrl = "jdbc:oracle:thin:@" + + "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" + + "(HOST=myoracle.db.server)(PORT=1521))" + + "(CONNECT_DATA=(SERVICE_NAME=my_servicename)))"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(oracleJdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } + + @Test + public void givenMysqlDb_thenCreateConnectionObject() { + String jdbcUrl = "jdbc:mysql://mysql.db.server:3306/my_database?useSSL=false&serverTimezone=UTC"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } + + @Test + public void givenMssqlDb_thenCreateConnectionObject() { + String jdbcUrl = "jdbc:sqlserver://mssql.db.server\\mssql_instance;databaseName=my_database"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } + + @Test + public void givenPostgreSqlDb_thenCreateConnectionObject() { + String jdbcUrl = "jdbc:postgresql://postgresql.db.server:5430/my_database?ssl=true&loglevel=2"; + String username = "dbUser"; + String password = "1234567"; + try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { + assertNotNull(conn); + } catch (SQLException e) { + System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage()); + } + } +} diff --git a/pom.xml b/pom.xml index 17e4fe1584..f6e3ddad6c 100644 --- a/pom.xml +++ b/pom.xml @@ -662,8 +662,7 @@ spring-mobile spring-mockito - - spring-mvc-basics + spring-mvc-basics-2 spring-mvc-basics-3 spring-mvc-basics-4 @@ -714,6 +713,7 @@ spring-vault spring-vertx + spring-web-modules spring-webflux-amqp spring-websockets @@ -1148,8 +1148,7 @@ spring-mobile spring-mockito - - spring-mvc-basics + spring-mvc-basics-2 spring-mvc-basics-3 spring-mvc-basics-4 @@ -1199,6 +1198,7 @@ spring-vault spring-vertx + spring-web-modules spring-webflux-amqp spring-websockets diff --git a/spring-apache-camel/pom.xml b/spring-apache-camel/pom.xml index 2d0d632546..9c7cc14381 100644 --- a/spring-apache-camel/pom.xml +++ b/spring-apache-camel/pom.xml @@ -47,6 +47,17 @@ camel-spring-javaconfig ${env.camel.version} + + org.apache.camel + camel-jackson + ${env.camel.version} + + + org.apache.camel + camel-test + ${env.camel.version} + test + diff --git a/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/Fruit.java b/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/Fruit.java new file mode 100644 index 0000000000..1932131ddd --- /dev/null +++ b/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/Fruit.java @@ -0,0 +1,24 @@ +package com.baeldung.camel.jackson; + +public class Fruit { + + private String name; + private int id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + +} diff --git a/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/FruitList.java b/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/FruitList.java new file mode 100644 index 0000000000..02f2b6feb0 --- /dev/null +++ b/spring-apache-camel/src/main/java/com/baeldung/camel/jackson/FruitList.java @@ -0,0 +1,17 @@ +package com.baeldung.camel.jackson; + +import java.util.List; + +public class FruitList { + + private List fruits; + + public List getFruits() { + return fruits; + } + + public void setFruits(List fruits) { + this.fruits = fruits; + } + +} diff --git a/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java b/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java new file mode 100644 index 0000000000..4810d7370e --- /dev/null +++ b/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java @@ -0,0 +1,60 @@ +package com.baeldung.camel.jackson; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jackson.ListJacksonDataFormat; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class FruitArrayJacksonUnmarshalUnitTest extends CamelTestSupport { + + @Test + public void givenJsonFruitArray_whenUnmarshalled_thenSuccess() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:marshalledObject"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(List.class); + + String json = readJsonFromFile("/json/fruit-array.json"); + template.sendBody("direct:jsonInput", json); + assertMockEndpointsSatisfied(); + + @SuppressWarnings("unchecked") + List fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(List.class); + assertNotNull("Fruit lists should not be null", fruitList); + + assertEquals("There should be two fruits", 2, fruitList.size()); + + Fruit fruit = fruitList.get(0); + assertEquals("Fruit name", "Banana", fruit.getName()); + assertEquals("Fruit id", 100, fruit.getId()); + + fruit = fruitList.get(1); + assertEquals("Fruit name", "Apple", fruit.getName()); + assertEquals("Fruit id", 101, fruit.getId()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + + from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class)) + .to("mock:marshalledObject"); + } + }; + } + + private String readJsonFromFile(String path) throws URISyntaxException, IOException { + URL resource = FruitArrayJacksonUnmarshalUnitTest.class.getResource(path); + return new String(Files.readAllBytes(Paths.get(resource.toURI()))); + } + +} diff --git a/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitListJacksonUnmarshalUnitTest.java b/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitListJacksonUnmarshalUnitTest.java new file mode 100644 index 0000000000..b5647f02f9 --- /dev/null +++ b/spring-apache-camel/src/test/java/com/baeldung/camel/jackson/FruitListJacksonUnmarshalUnitTest.java @@ -0,0 +1,59 @@ +package com.baeldung.camel.jackson; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jackson.JacksonDataFormat; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class FruitListJacksonUnmarshalUnitTest extends CamelTestSupport { + + @Test + public void givenJsonFruitList_whenUnmarshalled_thenSuccess() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:marshalledObject"); + mock.expectedMessageCount(1); + mock.message(0).body().isInstanceOf(FruitList.class); + + String json = readJsonFromFile("/json/fruit-list.json"); + template.sendBody("direct:jsonInput", json); + assertMockEndpointsSatisfied(); + + FruitList fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(FruitList.class); + assertNotNull("Fruit lists should not be null", fruitList); + + List fruits = fruitList.getFruits(); + assertEquals("There should be two fruits", 2, fruits.size()); + + Fruit fruit = fruits.get(0); + assertEquals("Fruit name", "Banana", fruit.getName()); + assertEquals("Fruit id", 100, fruit.getId()); + + fruit = fruits.get(1); + assertEquals("Fruit name", "Apple", fruit.getName()); + assertEquals("Fruit id", 101, fruit.getId()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:jsonInput").unmarshal(new JacksonDataFormat(FruitList.class)) + .to("mock:marshalledObject"); + } + }; + } + + private String readJsonFromFile(String path) throws URISyntaxException, IOException { + URL resource = FruitListJacksonUnmarshalUnitTest.class.getResource(path); + return new String(Files.readAllBytes(Paths.get(resource.toURI()))); + } + +} diff --git a/spring-apache-camel/src/test/resources/json/fruit-array.json b/spring-apache-camel/src/test/resources/json/fruit-array.json new file mode 100644 index 0000000000..0bd917c53f --- /dev/null +++ b/spring-apache-camel/src/test/resources/json/fruit-array.json @@ -0,0 +1,10 @@ +[ + { + "id": 100, + "name": "Banana" + }, + { + "id": 101, + "name": "Apple" + } +] \ No newline at end of file diff --git a/spring-apache-camel/src/test/resources/json/fruit-list.json b/spring-apache-camel/src/test/resources/json/fruit-list.json new file mode 100644 index 0000000000..357e08f7d5 --- /dev/null +++ b/spring-apache-camel/src/test/resources/json/fruit-list.json @@ -0,0 +1,12 @@ +{ + "fruits": [ + { + "id": 100, + "name": "Banana" + }, + { + "id": 101, + "name": "Apple" + } + ] +} \ No newline at end of file diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 44b3e28291..825af8fb9e 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -69,6 +69,7 @@ spring-boot-vue spring-boot-xml spring-boot-actuator + spring-boot-data-2 diff --git a/spring-boot-modules/spring-boot-data-2/pom.xml b/spring-boot-modules/spring-boot-data-2/pom.xml new file mode 100644 index 0000000000..199a204500 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/pom.xml @@ -0,0 +1,21 @@ + + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + ../ + + 4.0.0 + + spring-boot-data-2 + + + org.springframework.boot + spring-boot-starter-web + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/app/Application.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/app/Application.java new file mode 100644 index 0000000000..6f57a534a8 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/app/Application.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.jackson.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = "com.baeldung.boot.jackson.controller") +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java new file mode 100644 index 0000000000..d1875d03d9 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeConstants.java @@ -0,0 +1,13 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class CoffeeConstants { + + public static final String DATETIME_FORMAT = "dd-MM-yyyy HH:mm"; + public static final LocalDateTime FIXED_DATE = LocalDateTime.now(); + public static LocalDateTimeSerializer LOCAL_DATETIME_SERIALIZER = new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)); +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java new file mode 100644 index 0000000000..edb2b478fc --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeCustomizerConfig.java @@ -0,0 +1,18 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + +@Configuration +public class CoffeeCustomizerConfig { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { + return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL) + .serializers(LOCAL_DATETIME_SERIALIZER); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java new file mode 100644 index 0000000000..eff2b5c252 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeHttpConverterConfiguration.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + +@Configuration +public class CoffeeHttpConverterConfiguration { + + @Bean + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializers(LOCAL_DATETIME_SERIALIZER) + .serializationInclusion(JsonInclude.Include.NON_NULL); + return new MappingJackson2HttpMessageConverter(builder.build()); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java new file mode 100644 index 0000000000..8057fff3db --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeJacksonBuilderConfig.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + +@Configuration +public class CoffeeJacksonBuilderConfig { + + @Bean + @Primary + public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { + return new Jackson2ObjectMapperBuilder() + .serializers(LOCAL_DATETIME_SERIALIZER) + .serializationInclusion(JsonInclude.Include.NON_NULL); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java new file mode 100644 index 0000000000..f1ce6458ae --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeObjectMapperConfig.java @@ -0,0 +1,23 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + +@Configuration +public class CoffeeObjectMapperConfig { + + @Bean + @Primary + public ObjectMapper objectMapper() { + JavaTimeModule module = new JavaTimeModule(); + module.addSerializer(LOCAL_DATETIME_SERIALIZER); + return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL) + .registerModule(module); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java new file mode 100644 index 0000000000..fc157f8156 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/config/CoffeeRegisterModuleConfig.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.jackson.config; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.LOCAL_DATETIME_SERIALIZER; + +@Configuration +@PropertySource("classpath:coffee.properties") +public class CoffeeRegisterModuleConfig { + + @Bean + public Module javaTimeModule() { + JavaTimeModule module = new JavaTimeModule(); + module.addSerializer(LOCAL_DATETIME_SERIALIZER); + return module; + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java new file mode 100644 index 0000000000..23749b18a2 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/controller/CoffeeController.java @@ -0,0 +1,21 @@ +package com.baeldung.boot.jackson.controller; + +import com.baeldung.boot.jackson.model.Coffee; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; + +@RestController +public class CoffeeController { + + @GetMapping("/coffee") + public Coffee getCoffee( + @RequestParam(required = false) String brand, + @RequestParam(required = false) String name) { + return new Coffee().setBrand(brand) + .setDate(FIXED_DATE) + .setName(name); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/model/Coffee.java b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/model/Coffee.java new file mode 100644 index 0000000000..4df6b4bd6d --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/java/com/baeldung/boot/jackson/model/Coffee.java @@ -0,0 +1,39 @@ +package com.baeldung.boot.jackson.model; + +import java.time.LocalDateTime; + +public class Coffee { + + private String name; + + private String brand; + + private LocalDateTime date; + + public String getName() { + return name; + } + + public Coffee setName(String name) { + this.name = name; + return this; + } + + public String getBrand() { + return brand; + } + + public Coffee setBrand(String brand) { + this.brand = brand; + return this; + } + + public LocalDateTime getDate() { + return date; + } + + public Coffee setDate(LocalDateTime date) { + this.date = date; + return this; + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/main/resources/coffee.properties b/spring-boot-modules/spring-boot-data-2/src/main/resources/coffee.properties new file mode 100644 index 0000000000..269845cbf1 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/main/resources/coffee.properties @@ -0,0 +1 @@ +spring.jackson.default-property-inclusion=non_null \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java new file mode 100644 index 0000000000..f1bc35a8ce --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/AbstractCoffeeIntegrationTest.java @@ -0,0 +1,33 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeConstants; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; + +import java.time.format.DateTimeFormatter; + +import static com.baeldung.boot.jackson.config.CoffeeConstants.FIXED_DATE; +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public abstract class AbstractCoffeeIntegrationTest { + + @Autowired + protected TestRestTemplate restTemplate; + + @Test + public void whenGetCoffee_thenSerializedWithDateAndNonNull() { + String formattedDate = DateTimeFormatter.ofPattern(CoffeeConstants.DATETIME_FORMAT) + .format(FIXED_DATE); + + String brand = "Lavazza"; + String url = "/coffee?brand=" + brand; + + String response = restTemplate.getForObject(url, String.class); + + assertThat(response).isEqualTo( + "{\"brand\":\"" + brand + "\",\"date\":\"" + formattedDate + "\"}"); + } +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java new file mode 100644 index 0000000000..d690de1b9c --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeCustomizerIntegrationTest.java @@ -0,0 +1,8 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeCustomizerConfig; +import org.springframework.context.annotation.Import; + +@Import(CoffeeCustomizerConfig.class) +public class CoffeeCustomizerIntegrationTest extends AbstractCoffeeIntegrationTest { +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java new file mode 100644 index 0000000000..62b1d42152 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeHttpConverterIntegrationTest.java @@ -0,0 +1,8 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeHttpConverterConfiguration; +import org.springframework.context.annotation.Import; + +@Import(CoffeeHttpConverterConfiguration.class) +public class CoffeeHttpConverterIntegrationTest extends AbstractCoffeeIntegrationTest { +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java new file mode 100644 index 0000000000..52a55394c0 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeJacksonBuilderIntegrationTest.java @@ -0,0 +1,8 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeJacksonBuilderConfig; +import org.springframework.context.annotation.Import; + +@Import(CoffeeJacksonBuilderConfig.class) +public class CoffeeJacksonBuilderIntegrationTest extends AbstractCoffeeIntegrationTest { +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java new file mode 100644 index 0000000000..34743ceba5 --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeObjectMapperIntegrationTest.java @@ -0,0 +1,8 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeObjectMapperConfig; +import org.springframework.context.annotation.Import; + +@Import(CoffeeObjectMapperConfig.class) +public class CoffeeObjectMapperIntegrationTest extends AbstractCoffeeIntegrationTest { +} diff --git a/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java new file mode 100644 index 0000000000..69bbd5be2a --- /dev/null +++ b/spring-boot-modules/spring-boot-data-2/src/test/java/com/baeldung/boot/jackson/app/CoffeeRegisterModuleIntegrationTest.java @@ -0,0 +1,8 @@ +package com.baeldung.boot.jackson.app; + +import com.baeldung.boot.jackson.config.CoffeeRegisterModuleConfig; +import org.springframework.context.annotation.Import; + +@Import(CoffeeRegisterModuleConfig.class) +public class CoffeeRegisterModuleIntegrationTest extends AbstractCoffeeIntegrationTest { +} diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/reloading/configs/ReloadablePropertySource.java b/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/reloading/configs/ReloadablePropertySource.java index 6d76a2e1e2..5d4e170226 100644 --- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/reloading/configs/ReloadablePropertySource.java +++ b/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/reloading/configs/ReloadablePropertySource.java @@ -15,7 +15,7 @@ public class ReloadablePropertySource extends PropertySource { } public ReloadablePropertySource(String name, String path) { - super(StringUtils.isEmpty(name) ? path : name); + super(StringUtils.hasText(name) ? path : name); try { this.propertiesConfiguration = new PropertiesConfiguration(path); FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy(); diff --git a/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml b/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml index fc9bb42a95..61d462bb5a 100644 --- a/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml +++ b/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml @@ -6,7 +6,9 @@ spring: --- spring: - profiles: test + config: + activate: + on-profile: test name: test-YAML environment: testing enabled: false @@ -37,7 +39,9 @@ component: --- spring: - profiles: prod + config: + activate: + on-profile: prod name: prod-YAML environment: production enabled: true @@ -48,7 +52,9 @@ servers: --- spring: - profiles: dev + config: + activate: + on-profile: dev name: ${DEV_NAME:dev-YAML} environment: development enabled: true diff --git a/spring-cloud/spring-cloud-eureka/pom.xml b/spring-cloud/spring-cloud-eureka/pom.xml index 7af0c15352..9d7350e774 100644 --- a/spring-cloud/spring-cloud-eureka/pom.xml +++ b/spring-cloud/spring-cloud-eureka/pom.xml @@ -19,6 +19,7 @@ spring-cloud-eureka-server spring-cloud-eureka-client spring-cloud-eureka-feign-client + spring-cloud-eureka-feign-client-integration-test diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/pom.xml b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/pom.xml new file mode 100644 index 0000000000..3348dbb24f --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + spring-cloud-eureka-feign-client-integration-test + 1.0.0-SNAPSHOT + spring-cloud-eureka-feign-client-integration-test + jar + Spring Cloud Eureka - Feign Client Integration Tests + + + com.baeldung.spring.cloud + spring-cloud-eureka + 1.0.0-SNAPSHOT + + + + + + org.junit + junit-bom + ${junit-jupiter.version} + pom + import + + + org.springframework.cloud + spring-cloud-starter-parent + ${spring-cloud-dependencies.version} + pom + import + + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.boot + spring-boot-starter-web + + + + com.github.tomakehurst + wiremock + 2.27.2 + test + + + + org.projectlombok + lombok + + + + org.testcontainers + testcontainers + 1.14.3 + test + + + + org.awaitility + awaitility + 4.0.3 + test + + + + + + + + maven-surefire-plugin + + 1 + true + + + + + + diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/Application.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/Application.java new file mode 100644 index 0000000000..342e7e163b --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/Application.java @@ -0,0 +1,15 @@ +package com.baeldung.spring.cloud; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@EnableFeignClients +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/client/BooksClient.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/client/BooksClient.java new file mode 100644 index 0000000000..a263624b28 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/client/BooksClient.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.cloud.client; + +import com.baeldung.spring.cloud.model.Book; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; + +@FeignClient("books-service") +//@FeignClient(value="simple-books-client", url="${book.service.url}") +public interface BooksClient { + + @RequestMapping("/books") + List getBooks(); + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/model/Book.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/model/Book.java new file mode 100644 index 0000000000..64492f678d --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/main/java/com/baeldung/spring/cloud/model/Book.java @@ -0,0 +1,15 @@ +package com.baeldung.spring.cloud.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Book { + + private String title; + private String author; + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BookMocks.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BookMocks.java new file mode 100644 index 0000000000..2cce72e6cb --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BookMocks.java @@ -0,0 +1,27 @@ +package com.baeldung.spring.cloud.client; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +import java.io.IOException; + +import static java.nio.charset.Charset.defaultCharset; +import static org.springframework.util.StreamUtils.copyToString; + +public class BookMocks { + + public static void setupMockBooksResponse(WireMockServer mockService) throws IOException { + mockService.stubFor(WireMock.get(WireMock.urlEqualTo("/books")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody( + copyToString( + BookMocks.class.getClassLoader().getResourceAsStream("payload/get-books-response.json"), + defaultCharset())))); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BooksClientIntegrationTest.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BooksClientIntegrationTest.java new file mode 100644 index 0000000000..2842eef435 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/BooksClientIntegrationTest.java @@ -0,0 +1,53 @@ +package com.baeldung.spring.cloud.client; + +import com.baeldung.spring.cloud.model.Book; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.io.IOException; + +import static com.baeldung.spring.cloud.client.BookMocks.setupMockBooksResponse; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +@ActiveProfiles("test") +@EnableConfigurationProperties +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { WireMockConfig.class }) +class BooksClientIntegrationTest { + + @Autowired + private WireMockServer mockBooksService; + + @Autowired + private BooksClient booksClient; + + @BeforeEach + void setUp() throws IOException { + setupMockBooksResponse(mockBooksService); + } + + @Test + public void whenGetBooks_thenBooksShouldBeReturned() { + assertFalse(booksClient.getBooks().isEmpty()); + } + + @Test + public void whenGetBooks_thenTheCorrectBooksShouldBeReturned() { + assertTrue(booksClient.getBooks() + .containsAll(asList( + new Book("Dune", "Frank Herbert"), + new Book("Foundation", "Isaac Asimov")))); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/EurekaContainerConfig.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/EurekaContainerConfig.java new file mode 100644 index 0000000000..6747d14b88 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/EurekaContainerConfig.java @@ -0,0 +1,39 @@ +package com.baeldung.spring.cloud.client; + +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ActiveProfiles; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.lifecycle.Startables; + +import java.util.stream.Stream; + +@TestConfiguration +@ActiveProfiles("eureka-test") +public class EurekaContainerConfig { + + public static class Initializer implements ApplicationContextInitializer { + + public static GenericContainer eurekaServer + = new GenericContainer("springcloud/eureka") + .withExposedPorts(8761); + + @Override + public void initialize(@NotNull ConfigurableApplicationContext configurableApplicationContext) { + + Startables.deepStart(Stream.of(eurekaServer)).join(); + + TestPropertyValues + .of("eureka.client.serviceUrl.defaultZone=http://localhost:" + + eurekaServer.getFirstMappedPort().toString() + + "/eureka") + .applyTo(configurableApplicationContext); + + } + + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/LoadBalancerBooksClientIntegrationTest.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/LoadBalancerBooksClientIntegrationTest.java new file mode 100644 index 0000000000..f05df11ba3 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/LoadBalancerBooksClientIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung.spring.cloud.client; + +import com.baeldung.spring.cloud.model.Book; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.io.IOException; + +import static com.baeldung.spring.cloud.client.BookMocks.setupMockBooksResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.moreThan; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +@ActiveProfiles("ribbon-test") +@EnableConfigurationProperties +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { RibbonTestConfig.class }) +class LoadBalancerBooksClientIntegrationTest { + + @Autowired + private WireMockServer mockBooksService; + + @Autowired + private WireMockServer secondMockBooksService; + + @Autowired + private BooksClient booksClient; + + @BeforeEach + void setUp() throws IOException { + setupMockBooksResponse(mockBooksService); + setupMockBooksResponse(secondMockBooksService); + } + + @Test + void whenGetBooks_thenRequestsAreLoadBalanced() { + for (int k = 0; k < 10; k++) { + booksClient.getBooks(); + } + + mockBooksService.verify( + moreThan(0), getRequestedFor(WireMock.urlEqualTo("/books"))); + secondMockBooksService.verify( + moreThan(0), getRequestedFor(WireMock.urlEqualTo("/books"))); + } + + @Test + public void whenGetBooks_thenTheCorrectBooksShouldBeReturned() { + assertTrue(booksClient.getBooks() + .containsAll(asList( + new Book("Dune", "Frank Herbert"), + new Book("Foundation", "Isaac Asimov")))); + } +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/MockBookServiceConfig.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/MockBookServiceConfig.java new file mode 100644 index 0000000000..1fff2ec9c0 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/MockBookServiceConfig.java @@ -0,0 +1,22 @@ +package com.baeldung.spring.cloud.client; + +import com.baeldung.spring.cloud.model.Book; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; + +@Configuration +@RestController +@ActiveProfiles("eureka-test") +public class MockBookServiceConfig { + + @RequestMapping("/books") + public List getBooks() { + return Collections.singletonList(new Book("Hitchhiker's guide to the galaxy", "Douglas Adams")); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/RibbonTestConfig.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/RibbonTestConfig.java new file mode 100644 index 0000000000..273ba182b1 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/RibbonTestConfig.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.cloud.client; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.netflix.ribbon.StaticServerList; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ActiveProfiles; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +@TestConfiguration +@ActiveProfiles("ribbon-test") +public class RibbonTestConfig { + + @Autowired + private WireMockServer mockBooksService; + + @Autowired + private WireMockServer secondMockBooksService; + + @Bean(initMethod = "start", destroyMethod = "stop") + public WireMockServer mockBooksService() { + return new WireMockServer(options().dynamicPort()); + } + + @Bean(name="secondMockBooksService", initMethod = "start", destroyMethod = "stop") + public WireMockServer secondBooksMockService() { + return new WireMockServer(options().dynamicPort()); + } + + @Bean + public ServerList ribbonServerList() { + return new StaticServerList<>( + new Server("localhost", mockBooksService.port()), + new Server("localhost", secondMockBooksService.port())); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/ServiceDiscoveryBooksClientIntegrationTest.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/ServiceDiscoveryBooksClientIntegrationTest.java new file mode 100644 index 0000000000..027579d20d --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/ServiceDiscoveryBooksClientIntegrationTest.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.cloud.client; + +import com.baeldung.spring.cloud.Application; +import com.baeldung.spring.cloud.model.Book; +import com.netflix.discovery.EurekaClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Lazy; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ActiveProfiles("eureka-test") +@EnableConfigurationProperties +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = { MockBookServiceConfig.class }, initializers = { EurekaContainerConfig.Initializer.class }) +class ServiceDiscoveryBooksClientIntegrationTest { + + @Autowired + private BooksClient booksClient; + + @Lazy + @Autowired + private EurekaClient eurekaClient; + + @BeforeEach + void setUp() { + await().atMost(60, SECONDS).until(() -> eurekaClient.getApplications().size() > 0); + } + + @Test + public void whenGetBooks_thenTheCorrectBooksAreReturned() { + List books = booksClient.getBooks(); + + assertEquals(1, books.size()); + assertEquals( + new Book("Hitchhiker's guide to the galaxy", "Douglas Adams"), + books.stream().findFirst().get()); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/WireMockConfig.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/WireMockConfig.java new file mode 100644 index 0000000000..82b7cddede --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/java/com/baeldung/spring/cloud/client/WireMockConfig.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.cloud.client; + +import com.github.tomakehurst.wiremock.WireMockServer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ActiveProfiles; + +@TestConfiguration +@ActiveProfiles("test") +public class WireMockConfig { + + @Bean(initMethod = "start", destroyMethod = "stop") + public WireMockServer mockBooksService() { + return new WireMockServer(9561); + } + +} diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-eureka-test.yml b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-eureka-test.yml new file mode 100644 index 0000000000..6f6af6a080 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-eureka-test.yml @@ -0,0 +1,3 @@ +spring: + application: + name: books-service diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-ribbon-test.yml b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-ribbon-test.yml new file mode 100644 index 0000000000..84a78d0ec7 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-ribbon-test.yml @@ -0,0 +1,3 @@ +eureka: + client: + enabled: false diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-test.yml b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-test.yml new file mode 100644 index 0000000000..dce11adf69 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/application-test.yml @@ -0,0 +1,11 @@ +#book: +# service: +# url: http://localhost:9561 + +books-service: + ribbon: + listOfServers: http://localhost:9561 + +eureka: + client: + enabled: false diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/payload/get-books-response.json b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/payload/get-books-response.json new file mode 100644 index 0000000000..b4223ff8f2 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client-integration-test/src/test/resources/payload/get-books-response.json @@ -0,0 +1,10 @@ +[ + { + "title": "Dune", + "author": "Frank Herbert" + }, + { + "title": "Foundation", + "author": "Isaac Asimov" + } +] diff --git a/spring-resttemplate-2/pom.xml b/spring-resttemplate-2/pom.xml index 2aed154be6..1404a35f33 100644 --- a/spring-resttemplate-2/pom.xml +++ b/spring-resttemplate-2/pom.xml @@ -71,16 +71,16 @@ ch.qos.logback logback-classic - + - + - - org.springframework.boot - spring-boot-maven-plugin - + + org.springframework.boot + spring-boot-maven-plugin + - + \ No newline at end of file diff --git a/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerService.java b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerService.java new file mode 100644 index 0000000000..751e234e8b --- /dev/null +++ b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerService.java @@ -0,0 +1,16 @@ +package com.baeldung.resttemplate.json.consumer.service; + +import java.util.List; + +public interface UserConsumerService { + + List processUserDataFromObjectArray(); + + List processUserDataFromUserArray(); + + List processUserDataFromUserList(); + + List processNestedUserDataFromUserArray(); + + List processNestedUserDataFromUserList(); +} diff --git a/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImpl.java b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImpl.java new file mode 100644 index 0000000000..dc1566d971 --- /dev/null +++ b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImpl.java @@ -0,0 +1,90 @@ +package com.baeldung.resttemplate.json.consumer.service; + +import com.baeldung.resttemplate.json.model.Address; +import com.baeldung.resttemplate.json.model.User; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class UserConsumerServiceImpl implements UserConsumerService { + + private static final String BASE_URL = "http://localhost:8080/users"; + private final RestTemplate restTemplate; + private static final ObjectMapper mapper = new ObjectMapper(); + + public UserConsumerServiceImpl(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public List processUserDataFromObjectArray() { + ResponseEntity responseEntity = restTemplate.getForEntity(BASE_URL, Object[].class); + Object[] objects = responseEntity.getBody(); + return Arrays.stream(objects) + .map(object -> mapper.convertValue(object, User.class)) + .map(User::getName) + .collect(Collectors.toList()); + } + + @Override + public List processUserDataFromUserArray() { + ResponseEntity responseEntity = restTemplate.getForEntity(BASE_URL, User[].class); + User[] userArray = responseEntity.getBody(); + return Arrays.stream(userArray) + .map(User::getName) + .collect(Collectors.toList()); + } + + @Override + public List processUserDataFromUserList() { + ResponseEntity> responseEntity = + restTemplate.exchange( + BASE_URL, + HttpMethod.GET, + null, + new ParameterizedTypeReference>() {} + ); + List users = responseEntity.getBody(); + return users.stream() + .map(User::getName) + .collect(Collectors.toList()); + } + + @Override + public List processNestedUserDataFromUserArray() { + ResponseEntity responseEntity = restTemplate.getForEntity(BASE_URL, User[].class); + User[] userArray = responseEntity.getBody(); + //we can get more info if we need : + MediaType contentType = responseEntity.getHeaders().getContentType(); + HttpStatus statusCode = responseEntity.getStatusCode(); + + return Arrays.stream(userArray) + .flatMap(user -> user.getAddressList().stream()) + .map(Address::getPostCode) + .collect(Collectors.toList()); + } + + @Override + public List processNestedUserDataFromUserList() { + ResponseEntity> responseEntity = + restTemplate.exchange( + BASE_URL, + HttpMethod.GET, + null, + new ParameterizedTypeReference>() {} + ); + List userList = responseEntity.getBody(); + return userList.stream() + .flatMap(user -> user.getAddressList().stream()) + .map(Address::getPostCode) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/Address.java b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/Address.java new file mode 100644 index 0000000000..f41ff4d8ea --- /dev/null +++ b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/Address.java @@ -0,0 +1,29 @@ +package com.baeldung.resttemplate.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Address { + private final String addressLine1; + private final String addressLine2; + private final String town; + private final String postCode; + + @JsonCreator + public Address( + @JsonProperty("addressLine1") String addressLine1, + @JsonProperty("addressLine2") String addressLine2, + @JsonProperty("town") String town, + @JsonProperty("postCode") String postCode) { + this.addressLine1 = addressLine1; + this.addressLine2 = addressLine2; + this.town = town; + this.postCode = postCode; + } + public String getPostCode() { + return postCode; + } +} diff --git a/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/User.java b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/User.java new file mode 100644 index 0000000000..8e02ef7787 --- /dev/null +++ b/spring-resttemplate-2/src/main/java/com/baeldung/resttemplate/json/model/User.java @@ -0,0 +1,30 @@ +package com.baeldung.resttemplate.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class User { + private final int id; + private final String name; + private final List
addressList; + + @JsonCreator + public User( + @JsonProperty("id") int id, + @JsonProperty("name") String name, + @JsonProperty("addressList") List
addressList) { + this.id = id; + this.name = name; + this.addressList = addressList; + } + + public String getName() { + return name; + } + + public List
getAddressList() { return addressList; } +} diff --git a/spring-resttemplate-2/src/test/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImplUnitTest.java b/spring-resttemplate-2/src/test/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImplUnitTest.java new file mode 100644 index 0000000000..4cc58e30f5 --- /dev/null +++ b/spring-resttemplate-2/src/test/java/com/baeldung/resttemplate/json/consumer/service/UserConsumerServiceImplUnitTest.java @@ -0,0 +1,127 @@ +package com.baeldung.resttemplate.json.consumer.service; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.ExpectedCount; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; + +@SpringBootTest +public class UserConsumerServiceImplUnitTest { + + private static String USER_JSON = "[{\"id\":1,\"name\":\"user1\",\"addressList\":[{\"addressLine1\":\"address1_addressLine1\",\"addressLine2\":\"address1_addressLine2\",\"town\":\"address1_town\",\"postCode\":\"user1_address1_postCode\"}," + + "{\"addressLine1\":\"address2_addressLine1\",\"addressLine2\":\"address2_addressLine2\",\"town\":\"address2_town\",\"postCode\":\"user1_address2_postCode\"}]}," + + "{\"id\":2,\"name\":\"user2\",\"addressList\":[{\"addressLine1\":\"address1_addressLine1\",\"addressLine2\":\"address1_addressLine2\",\"town\":\"address1_town\",\"postCode\":\"user2_address1_postCode\"}]}]"; + + private MockRestServiceServer mockServer; + private final RestTemplate restTemplate = new RestTemplate(); + private final UserConsumerService tested = new UserConsumerServiceImpl(restTemplate); + + @Before + public void init() { + mockServer = MockRestServiceServer.createServer(restTemplate); + } + + @Test + public void whenProcessUserDataAsObjects_thenOK() { + String url = "http://localhost:8080/users"; + List expected = Arrays.asList("user1", "user2"); + + mockServer.expect(ExpectedCount.once(), + requestTo(url)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(USER_JSON)); + + List actual = tested.processUserDataFromObjectArray(); + + mockServer.verify(); + assertThat(actual).containsExactly(expected.get(0), expected.get(1)); + } + + @Test + public void whenProcessUserDataAsArray_thenOK() { + String url = "http://localhost:8080/users"; + List expected = Arrays.asList("user1", "user2"); + + mockServer.expect(ExpectedCount.once(), + requestTo(url)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(USER_JSON)); + + List actual = tested.processUserDataFromUserArray(); + + mockServer.verify(); + assertThat(actual).containsExactly(expected.get(0), expected.get(1)); + } + + @Test + public void whenProcessUserDataAsList_thenOK() { + String url = "http://localhost:8080/users"; + List expected = Arrays.asList("user1", "user2"); + + mockServer.expect(ExpectedCount.once(), + requestTo(url)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(USER_JSON)); + + List actual = tested.processUserDataFromUserList(); + + mockServer.verify(); + assertThat(actual).containsExactly(expected.get(0), expected.get(1)); + } + + + @Test + public void whenProcessNestedUserDataFromArray_thenOK() { + String url = "http://localhost:8080/users"; + List expected = Arrays.asList("user1_address1_postCode", "user1_address2_postCode", "user2_address1_postCode"); + + mockServer.expect(ExpectedCount.once(), + requestTo(url)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(USER_JSON)); + + List actual = tested.processNestedUserDataFromUserArray(); + + mockServer.verify(); + assertThat(actual).containsExactly(expected.get(0), expected.get(1), expected.get(2)); + } + + @Test + public void whenProcessNestedUserDataFromList_thenOK() { + String url = "http://localhost:8080/users"; + List expected = Arrays.asList("user1_address1_postCode", "user1_address2_postCode", "user2_address1_postCode"); + + mockServer.expect(ExpectedCount.once(), + requestTo(url)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withStatus(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON) + .body(USER_JSON)); + + List actual = tested.processNestedUserDataFromUserList(); + + mockServer.verify(); + assertThat(actual).containsExactly(expected.get(0), expected.get(1), expected.get(2)); + } +} \ No newline at end of file diff --git a/spring-web-modules/pom.xml b/spring-web-modules/pom.xml new file mode 100644 index 0000000000..29a90b5bd2 --- /dev/null +++ b/spring-web-modules/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + spring-web-modules + 0.0.1-SNAPSHOT + spring-web-modules + pom + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + spring-mvc-basics + + + diff --git a/spring-mvc-basics/.gitignore b/spring-web-modules/spring-mvc-basics/.gitignore similarity index 100% rename from spring-mvc-basics/.gitignore rename to spring-web-modules/spring-mvc-basics/.gitignore diff --git a/spring-mvc-basics/README.md b/spring-web-modules/spring-mvc-basics/README.md similarity index 95% rename from spring-mvc-basics/README.md rename to spring-web-modules/spring-mvc-basics/README.md index dea18a1619..49d23aef25 100644 --- a/spring-mvc-basics/README.md +++ b/spring-web-modules/spring-mvc-basics/README.md @@ -4,7 +4,7 @@ This module contains articles about the basics of Spring MVC. Articles about mor their own module. ### The Course -The "REST With Spring" Classes: http://bit.ly/restwithspring +The "REST With Spring" Classes: https://bit.ly/restwithspring ### Relevant Articles: - [Spring MVC Tutorial](https://www.baeldung.com/spring-mvc-tutorial) diff --git a/spring-mvc-basics/pom.xml b/spring-web-modules/spring-mvc-basics/pom.xml similarity index 97% rename from spring-mvc-basics/pom.xml rename to spring-web-modules/spring-mvc-basics/pom.xml index cd486cb1d3..ac92c7bfe5 100644 --- a/spring-mvc-basics/pom.xml +++ b/spring-web-modules/spring-mvc-basics/pom.xml @@ -12,7 +12,7 @@ com.baeldung parent-boot-2 0.0.1-SNAPSHOT - ../parent-boot-2 + ../../parent-boot-2 diff --git a/spring-mvc-basics/src/main/java/com/baeldung/Application.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/Application.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/Application.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/Application.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/config/MainWebAppInitializer.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/config/MainWebAppInitializer.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/config/MainWebAppInitializer.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/config/MainWebAppInitializer.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatch.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatch.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatch.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatch.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatchValidator.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatchValidator.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatchValidator.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/customvalidator/FieldsValueMatchValidator.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionExampleController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionExampleController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionExampleController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionExampleController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/model/Book.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/Book.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/model/Book.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/Book.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/model/Employee.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/Employee.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/model/Employee.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/Employee.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/model/NewUserForm.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/NewUserForm.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/model/NewUserForm.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/NewUserForm.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/model/User.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/User.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/model/User.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/User.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/model/ValidatedPhone.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/ValidatedPhone.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/model/ValidatedPhone.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/model/ValidatedPhone.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/services/UserService.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/services/UserService.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/services/UserService.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/services/UserService.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/spring/web/config/WebConfig.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/spring/web/config/WebConfig.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/spring/web/config/WebConfig.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/spring/web/config/WebConfig.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/EmployeeController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/EmployeeController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/EmployeeController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/EmployeeController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/MultipartController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/MultipartController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/MultipartController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/MultipartController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/NewUserController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/NewUserController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/NewUserController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/NewUserController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/RequestMappingShortcutsController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/RequestMappingShortcutsController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/RequestMappingShortcutsController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/RequestMappingShortcutsController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ResponseStatusRestController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ResponseStatusRestController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/ResponseStatusRestController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ResponseStatusRestController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SampleController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SampleController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/SampleController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SampleController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookRestController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookRestController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookRestController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/SimpleBookRestController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserRestController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserRestController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserRestController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/UserRestController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/BeanNameHandlerMappingController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/BeanNameHandlerMappingController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/BeanNameHandlerMappingController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/BeanNameHandlerMappingController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/SimpleUrlMappingController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/SimpleUrlMappingController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/SimpleUrlMappingController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/SimpleUrlMappingController.java diff --git a/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/WelcomeController.java b/spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/WelcomeController.java similarity index 100% rename from spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/WelcomeController.java rename to spring-web-modules/spring-mvc-basics/src/main/java/com/baeldung/web/controller/handlermapping/WelcomeController.java diff --git a/spring-mvc-basics/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics/src/main/resources/application.properties similarity index 100% rename from spring-mvc-basics/src/main/resources/application.properties rename to spring-web-modules/spring-mvc-basics/src/main/resources/application.properties diff --git a/spring-mvc-basics/src/main/resources/mvc-configuration.xml b/spring-web-modules/spring-mvc-basics/src/main/resources/mvc-configuration.xml similarity index 100% rename from spring-mvc-basics/src/main/resources/mvc-configuration.xml rename to spring-web-modules/spring-mvc-basics/src/main/resources/mvc-configuration.xml diff --git a/spring-mvc-basics/src/main/resources/themes/default.properties b/spring-web-modules/spring-mvc-basics/src/main/resources/themes/default.properties similarity index 100% rename from spring-mvc-basics/src/main/resources/themes/default.properties rename to spring-web-modules/spring-mvc-basics/src/main/resources/themes/default.properties diff --git a/spring-mvc-basics/src/main/resources/themes/example.properties b/spring-web-modules/spring-mvc-basics/src/main/resources/themes/example.properties similarity index 100% rename from spring-mvc-basics/src/main/resources/themes/example.properties rename to spring-web-modules/spring-mvc-basics/src/main/resources/themes/example.properties diff --git a/spring-mvc-basics/src/main/resources/views.properties b/spring-web-modules/spring-mvc-basics/src/main/resources/views.properties similarity index 100% rename from spring-mvc-basics/src/main/resources/views.properties rename to spring-web-modules/spring-mvc-basics/src/main/resources/views.properties diff --git a/spring-mvc-basics/src/main/resources/views.xml b/spring-web-modules/spring-mvc-basics/src/main/resources/views.xml similarity index 100% rename from spring-mvc-basics/src/main/resources/views.xml rename to spring-web-modules/spring-mvc-basics/src/main/resources/views.xml diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeHome.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeHome.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeHome.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeHome.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeView.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeView.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeView.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/employeeView.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/index.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/index.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/index.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/index.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/phoneHome.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/phoneHome.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/phoneHome.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/phoneHome.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/sample.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/sample.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/sample.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/sample.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view/userHome.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/userHome.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view/userHome.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view/userHome.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view2/sample2.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view2/sample2.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view2/sample2.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view2/sample2.jsp diff --git a/spring-mvc-basics/src/main/webapp/WEB-INF/view3/sample3.jsp b/spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view3/sample3.jsp similarity index 100% rename from spring-mvc-basics/src/main/webapp/WEB-INF/view3/sample3.jsp rename to spring-web-modules/spring-mvc-basics/src/main/webapp/WEB-INF/view3/sample3.jsp diff --git a/spring-mvc-basics/src/test/java/com/baeldung/SpringContextTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/SpringContextTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/SpringContextTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/SpringContextTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/config/BeanNameUrlHandlerMappingConfig.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/BeanNameUrlHandlerMappingConfig.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/config/BeanNameUrlHandlerMappingConfig.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/BeanNameUrlHandlerMappingConfig.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingDefaultConfig.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingDefaultConfig.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingDefaultConfig.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingDefaultConfig.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingPrioritiesConfig.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingPrioritiesConfig.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingPrioritiesConfig.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/HandlerMappingPrioritiesConfig.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/config/SimpleUrlHandlerMappingConfig.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/SimpleUrlHandlerMappingConfig.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/config/SimpleUrlHandlerMappingConfig.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/config/SimpleUrlHandlerMappingConfig.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionControllerIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionControllerIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionControllerIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/exception/HttpMediaTypeNotAcceptableExceptionControllerIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/BeanNameMappingConfigIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/BeanNameMappingConfigIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/handlermappings/BeanNameMappingConfigIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/BeanNameMappingConfigIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingDefaultConfigIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingDefaultConfigIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingDefaultConfigIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingDefaultConfigIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingPriorityConfigIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingPriorityConfigIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingPriorityConfigIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/HandlerMappingPriorityConfigIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/SimpleUrlMappingConfigIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/SimpleUrlMappingConfigIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/handlermappings/SimpleUrlMappingConfigIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/handlermappings/SimpleUrlMappingConfigIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ClassValidationMvcIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ClassValidationMvcIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/ClassValidationMvcIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ClassValidationMvcIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/CustomMVCValidatorIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/CustomMVCValidatorIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/CustomMVCValidatorIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/CustomMVCValidatorIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerContentNegotiationIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerContentNegotiationIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerContentNegotiationIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerContentNegotiationIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerModelAttributeIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerModelAttributeIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerModelAttributeIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/EmployeeControllerModelAttributeIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/RequestMapingShortcutsIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ResponseStatusRestControllerIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ResponseStatusRestControllerIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/ResponseStatusRestControllerIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/ResponseStatusRestControllerIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SampleControllerLiveTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SampleControllerLiveTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/SampleControllerLiveTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SampleControllerLiveTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookControllerIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookControllerIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookControllerIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookControllerIntegrationTest.java diff --git a/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookRestControllerIntegrationTest.java b/spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookRestControllerIntegrationTest.java similarity index 100% rename from spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookRestControllerIntegrationTest.java rename to spring-web-modules/spring-mvc-basics/src/test/java/com/baeldung/web/controller/SimpleBookRestControllerIntegrationTest.java diff --git a/spring-mvc-basics/src/test/resources/BeanNameUrlHandlerMappingConfig.xml b/spring-web-modules/spring-mvc-basics/src/test/resources/BeanNameUrlHandlerMappingConfig.xml similarity index 100% rename from spring-mvc-basics/src/test/resources/BeanNameUrlHandlerMappingConfig.xml rename to spring-web-modules/spring-mvc-basics/src/test/resources/BeanNameUrlHandlerMappingConfig.xml diff --git a/spring-mvc-basics/src/test/resources/ControllerClassNameHandlerMappingConfig.xml b/spring-web-modules/spring-mvc-basics/src/test/resources/ControllerClassNameHandlerMappingConfig.xml similarity index 100% rename from spring-mvc-basics/src/test/resources/ControllerClassNameHandlerMappingConfig.xml rename to spring-web-modules/spring-mvc-basics/src/test/resources/ControllerClassNameHandlerMappingConfig.xml diff --git a/spring-mvc-basics/src/test/resources/HandlerMappingConfiguringPriorities.xml b/spring-web-modules/spring-mvc-basics/src/test/resources/HandlerMappingConfiguringPriorities.xml similarity index 100% rename from spring-mvc-basics/src/test/resources/HandlerMappingConfiguringPriorities.xml rename to spring-web-modules/spring-mvc-basics/src/test/resources/HandlerMappingConfiguringPriorities.xml diff --git a/spring-mvc-basics/src/test/resources/SimpleUrlHandlerMappingConfig.xml b/spring-web-modules/spring-mvc-basics/src/test/resources/SimpleUrlHandlerMappingConfig.xml similarity index 100% rename from spring-mvc-basics/src/test/resources/SimpleUrlHandlerMappingConfig.xml rename to spring-web-modules/spring-mvc-basics/src/test/resources/SimpleUrlHandlerMappingConfig.xml diff --git a/spring-websockets/pom.xml b/spring-websockets/pom.xml index ddfd512476..d2a32a8eb6 100644 --- a/spring-websockets/pom.xml +++ b/spring-websockets/pom.xml @@ -18,6 +18,15 @@ org.springframework.boot spring-boot-starter-websocket + + io.projectreactor + reactor-core + + + com.github.javafaker + javafaker + 1.0.2 + com.google.code.gson gson diff --git a/spring-websockets/src/main/java/com/baeldung/SpringBootApp.java b/spring-websockets/src/main/java/com/baeldung/SpringBootApp.java index ea2a461dfc..3a98746748 100644 --- a/spring-websockets/src/main/java/com/baeldung/SpringBootApp.java +++ b/spring-websockets/src/main/java/com/baeldung/SpringBootApp.java @@ -3,8 +3,10 @@ package com.baeldung; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class SpringBootApp extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); diff --git a/spring-websockets/src/main/java/com/baeldung/websockets/BotsController.java b/spring-websockets/src/main/java/com/baeldung/websockets/BotsController.java new file mode 100644 index 0000000000..3f268f3794 --- /dev/null +++ b/spring-websockets/src/main/java/com/baeldung/websockets/BotsController.java @@ -0,0 +1,21 @@ +package com.baeldung.websockets; + +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.stereotype.Controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Controller +public class BotsController { + + @MessageMapping("/chatwithbots") + @SendTo("/topic/pushmessages") + public OutputMessage send(final Message message) throws Exception { + + final String time = new SimpleDateFormat("HH:mm").format(new Date()); + return new OutputMessage(message.getFrom(), message.getText(), time); + } + +} diff --git a/spring-websockets/src/main/java/com/baeldung/websockets/ReactiveScheduledPushMessages.java b/spring-websockets/src/main/java/com/baeldung/websockets/ReactiveScheduledPushMessages.java new file mode 100644 index 0000000000..36b1b886fc --- /dev/null +++ b/spring-websockets/src/main/java/com/baeldung/websockets/ReactiveScheduledPushMessages.java @@ -0,0 +1,32 @@ +package com.baeldung.websockets; + +import com.github.javafaker.Faker; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; + +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.Date; + +@Service +public class ReactiveScheduledPushMessages implements InitializingBean { + + private final SimpMessagingTemplate simpMessagingTemplate; + + private final Faker faker; + + public ReactiveScheduledPushMessages(SimpMessagingTemplate simpMessagingTemplate) { + this.simpMessagingTemplate = simpMessagingTemplate; + this.faker = new Faker(); + } + + @Override + public void afterPropertiesSet() throws Exception { + Flux.interval(Duration.ofSeconds(4L)) + .map((n) -> new OutputMessage(faker.backToTheFuture().character(), faker.backToTheFuture().quote(), + new SimpleDateFormat("HH:mm").format(new Date()))) + .subscribe(message -> simpMessagingTemplate.convertAndSend("/topic/pushmessages", message)); + } +} diff --git a/spring-websockets/src/main/java/com/baeldung/websockets/ScheduledPushMessages.java b/spring-websockets/src/main/java/com/baeldung/websockets/ScheduledPushMessages.java new file mode 100644 index 0000000000..2468b69713 --- /dev/null +++ b/spring-websockets/src/main/java/com/baeldung/websockets/ScheduledPushMessages.java @@ -0,0 +1,32 @@ +package com.baeldung.websockets; + + +import com.github.javafaker.Faker; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Controller; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Service +public class ScheduledPushMessages { + + private final SimpMessagingTemplate simpMessagingTemplate; + + private final Faker faker; + + public ScheduledPushMessages(SimpMessagingTemplate simpMessagingTemplate) { + this.simpMessagingTemplate = simpMessagingTemplate; + faker = new Faker(); + } + + @Scheduled(fixedRate = 5000) + public void sendMessage() { + final String time = new SimpleDateFormat("HH:mm").format(new Date()); + simpMessagingTemplate.convertAndSend("/topic/pushmessages", + new OutputMessage("Chuck Norris", faker.chuckNorris().fact(), time)); + } + +} diff --git a/spring-websockets/src/main/java/com/baeldung/websockets/WebSocketConfig.java b/spring-websockets/src/main/java/com/baeldung/websockets/WebSocketConfig.java index 7b53dbc3f3..6179ec9c0d 100644 --- a/spring-websockets/src/main/java/com/baeldung/websockets/WebSocketConfig.java +++ b/spring-websockets/src/main/java/com/baeldung/websockets/WebSocketConfig.java @@ -20,6 +20,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { public void registerStompEndpoints(final StompEndpointRegistry registry) { registry.addEndpoint("/chat"); registry.addEndpoint("/chat").withSockJS(); + registry.addEndpoint("/chatwithbots"); + registry.addEndpoint("/chatwithbots").withSockJS(); } } \ No newline at end of file diff --git a/spring-websockets/src/main/webapp/bots.html b/spring-websockets/src/main/webapp/bots.html new file mode 100644 index 0000000000..38570d420c --- /dev/null +++ b/spring-websockets/src/main/webapp/bots.html @@ -0,0 +1,88 @@ + + + Chat WebSocket + + + + + + + + + + +
+ + +
+ +
+
+
+ + +
+
+
+ + +

+
+
+ + + \ No newline at end of file