diff --git a/cas/cas-secured-app/pom.xml b/cas/cas-secured-app/pom.xml
index 426d65c32b..bcce82c94c 100644
--- a/cas/cas-secured-app/pom.xml
+++ b/cas/cas-secured-app/pom.xml
@@ -16,10 +16,6 @@
../../parent-boot-2
-
- 2.2.6.RELEASE
-
-
org.springframework.boot
diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml
index 00579c49b2..48474a5eef 100644
--- a/core-java-modules/core-java-8-2/pom.xml
+++ b/core-java-modules/core-java-8-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-8-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-8-datetime-2/pom.xml b/core-java-modules/core-java-8-datetime-2/pom.xml
index ce98b72781..629ce5234d 100644
--- a/core-java-modules/core-java-8-datetime-2/pom.xml
+++ b/core-java-modules/core-java-8-datetime-2/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-8-datetime
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-8-datetime/pom.xml b/core-java-modules/core-java-8-datetime/pom.xml
index ce98b72781..629ce5234d 100644
--- a/core-java-modules/core-java-8-datetime/pom.xml
+++ b/core-java-modules/core-java-8-datetime/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-8-datetime
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-8/pom.xml b/core-java-modules/core-java-8/pom.xml
index a434be028d..557f9e0dce 100644
--- a/core-java-modules/core-java-8/pom.xml
+++ b/core-java-modules/core-java-8/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-8
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-9-improvements/README.md b/core-java-modules/core-java-9-improvements/README.md
index c89d0e3c09..3b0bdcd651 100644
--- a/core-java-modules/core-java-9-improvements/README.md
+++ b/core-java-modules/core-java-9-improvements/README.md
@@ -9,3 +9,4 @@ This module contains articles about the improvements to core Java features intro
- [Java 9 Stream API Improvements](https://www.baeldung.com/java-9-stream-api)
- [Java 9 java.util.Objects Additions](https://www.baeldung.com/java-9-objects-new)
- [Java 9 CompletableFuture API Improvements](https://www.baeldung.com/java-9-completablefuture)
+- [Java InputStream to Byte Array and ByteBuffer](https://www.baeldung.com/convert-input-stream-to-array-of-bytes)
diff --git a/core-java-modules/core-java-9-improvements/pom.xml b/core-java-modules/core-java-9-improvements/pom.xml
index d1c6bac9ec..5cc4fce7a9 100644
--- a/core-java-modules/core-java-9-improvements/pom.xml
+++ b/core-java-modules/core-java-9-improvements/pom.xml
@@ -33,6 +33,11 @@
guava
${guava.version}
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
org.junit.platform
junit-platform-runner
diff --git a/core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteArrayUnitTest.java b/core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteArrayUnitTest.java
new file mode 100644
index 0000000000..b64709be09
--- /dev/null
+++ b/core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteArrayUnitTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.java9.io.conversion;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+import com.google.common.io.ByteSource;
+import com.google.common.io.ByteStreams;
+
+public class InputStreamToByteArrayUnitTest {
+
+ @Test
+ public final void givenUsingPlainJavaOnFixedSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
+ final InputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
+ final byte[] targetArray = new byte[initialStream.available()];
+ initialStream.read(targetArray);
+ }
+
+ @Test
+ public final void givenUsingPlainJavaOnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
+ final InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
+
+ final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int nRead;
+ final byte[] data = new byte[1024];
+ while ((nRead = is.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+
+ buffer.flush();
+ final byte[] byteArray = buffer.toByteArray();
+ }
+
+ @Test
+ public void givenUsingPlainJava9_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
+ final InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
+
+ byte[] data = is.readAllBytes();
+ }
+
+ @Test
+ public final void givenUsingGuava_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
+ final InputStream initialStream = ByteSource.wrap(new byte[] { 0, 1, 2 })
+ .openStream();
+ final byte[] targetArray = ByteStreams.toByteArray(initialStream);
+ }
+
+ @Test
+ public final void givenUsingCommonsIO_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
+ final InputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
+ final byte[] targetArray = IOUtils.toByteArray(initialStream);
+ }
+}
diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtobytes/InputStreamToByteBufferUnitTest.java b/core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteBufferUnitTest.java
similarity index 97%
rename from core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtobytes/InputStreamToByteBufferUnitTest.java
rename to core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteBufferUnitTest.java
index c10aaae22a..f97352ac27 100644
--- a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtobytes/InputStreamToByteBufferUnitTest.java
+++ b/core-java-modules/core-java-9-improvements/src/test/java/com/baeldung/java9/io/conversion/InputStreamToByteBufferUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.inputstreamtobytes;
+package com.baeldung.java9.io.conversion;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
diff --git a/core-java-modules/core-java-9-streams/pom.xml b/core-java-modules/core-java-9-streams/pom.xml
index 7865b336a7..8c1af89b24 100644
--- a/core-java-modules/core-java-9-streams/pom.xml
+++ b/core-java-modules/core-java-9-streams/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-9-streams
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-annotations/pom.xml b/core-java-modules/core-java-annotations/pom.xml
index 8fc4c15cde..92ba4991bb 100644
--- a/core-java-modules/core-java-annotations/pom.xml
+++ b/core-java-modules/core-java-annotations/pom.xml
@@ -10,10 +10,10 @@
jar
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-arrays-convert/pom.xml b/core-java-modules/core-java-arrays-convert/pom.xml
index bd50289f47..67dc645936 100644
--- a/core-java-modules/core-java-arrays-convert/pom.xml
+++ b/core-java-modules/core-java-arrays-convert/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-guides/pom.xml b/core-java-modules/core-java-arrays-guides/pom.xml
index ef718d5117..df8639820d 100644
--- a/core-java-modules/core-java-arrays-guides/pom.xml
+++ b/core-java-modules/core-java-arrays-guides/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-multidimensional/pom.xml b/core-java-modules/core-java-arrays-multidimensional/pom.xml
index 6e49a20521..d90853678c 100644
--- a/core-java-modules/core-java-arrays-multidimensional/pom.xml
+++ b/core-java-modules/core-java-arrays-multidimensional/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-operations-advanced/pom.xml b/core-java-modules/core-java-arrays-operations-advanced/pom.xml
index 8989e91189..d73fdcee28 100644
--- a/core-java-modules/core-java-arrays-operations-advanced/pom.xml
+++ b/core-java-modules/core-java-arrays-operations-advanced/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-operations-basic/pom.xml b/core-java-modules/core-java-arrays-operations-basic/pom.xml
index 4480c14bb2..73588d662a 100644
--- a/core-java-modules/core-java-arrays-operations-basic/pom.xml
+++ b/core-java-modules/core-java-arrays-operations-basic/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-sorting/pom.xml b/core-java-modules/core-java-arrays-sorting/pom.xml
index 127d921b2a..d5e2beaac4 100644
--- a/core-java-modules/core-java-arrays-sorting/pom.xml
+++ b/core-java-modules/core-java-arrays-sorting/pom.xml
@@ -5,8 +5,8 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
- ../pom.xml
+ 0.0.1-SNAPSHOT
+ ../
4.0.0
diff --git a/core-java-modules/core-java-collections-2/pom.xml b/core-java-modules/core-java-collections-2/pom.xml
index 3a7c70b1a2..d163aabdbc 100644
--- a/core-java-modules/core-java-collections-2/pom.xml
+++ b/core-java-modules/core-java-collections-2/pom.xml
@@ -7,12 +7,11 @@
core-java-collections-2
core-java-collections-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-3/pom.xml b/core-java-modules/core-java-collections-3/pom.xml
index 1e1695c8bc..bd991bfefa 100644
--- a/core-java-modules/core-java-collections-3/pom.xml
+++ b/core-java-modules/core-java-collections-3/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-array-list/pom.xml b/core-java-modules/core-java-collections-array-list/pom.xml
index 74a6513cac..81ee4eff55 100644
--- a/core-java-modules/core-java-collections-array-list/pom.xml
+++ b/core-java-modules/core-java-collections-array-list/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-array-list
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list-2/pom.xml b/core-java-modules/core-java-collections-list-2/pom.xml
index 3184da1294..230787d14d 100644
--- a/core-java-modules/core-java-collections-list-2/pom.xml
+++ b/core-java-modules/core-java-collections-list-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list-3/pom.xml b/core-java-modules/core-java-collections-list-3/pom.xml
index 090e756ac6..373190a130 100644
--- a/core-java-modules/core-java-collections-list-3/pom.xml
+++ b/core-java-modules/core-java-collections-list-3/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list/pom.xml b/core-java-modules/core-java-collections-list/pom.xml
index e6dce5a0db..509f58ea61 100644
--- a/core-java-modules/core-java-collections-list/pom.xml
+++ b/core-java-modules/core-java-collections-list/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps-2/pom.xml b/core-java-modules/core-java-collections-maps-2/pom.xml
index a08a4ac072..a64a11c6ea 100644
--- a/core-java-modules/core-java-collections-maps-2/pom.xml
+++ b/core-java-modules/core-java-collections-maps-2/pom.xml
@@ -7,12 +7,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps-3/pom.xml b/core-java-modules/core-java-collections-maps-3/pom.xml
index 95414c12c2..f547968b22 100644
--- a/core-java-modules/core-java-collections-maps-3/pom.xml
+++ b/core-java-modules/core-java-collections-maps-3/pom.xml
@@ -7,12 +7,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps/pom.xml b/core-java-modules/core-java-collections-maps/pom.xml
index c0dd705c1c..742e264504 100644
--- a/core-java-modules/core-java-collections-maps/pom.xml
+++ b/core-java-modules/core-java-collections-maps/pom.xml
@@ -6,12 +6,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps
jar
-
-
- com.baeldung
- parent-java
+
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-set/pom.xml b/core-java-modules/core-java-collections-set/pom.xml
index c89ba0c091..7c55f2ff2a 100644
--- a/core-java-modules/core-java-collections-set/pom.xml
+++ b/core-java-modules/core-java-collections-set/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-set
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections/pom.xml b/core-java-modules/core-java-collections/pom.xml
index 515d19d7fb..e1219c4713 100644
--- a/core-java-modules/core-java-collections/pom.xml
+++ b/core-java-modules/core-java-collections/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-2/pom.xml b/core-java-modules/core-java-concurrency-2/pom.xml
index dfb5674c8e..75fd3890b3 100644
--- a/core-java-modules/core-java-concurrency-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-2/pom.xml b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
index 8752e7b7db..2f374bffac 100644
--- a/core-java-modules/core-java-concurrency-advanced-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-3/README.md b/core-java-modules/core-java-concurrency-advanced-3/README.md
index dfd264116c..e33b6ab692 100644
--- a/core-java-modules/core-java-concurrency-advanced-3/README.md
+++ b/core-java-modules/core-java-concurrency-advanced-3/README.md
@@ -12,4 +12,5 @@ This module contains articles about advanced topics about multithreading with co
- [Asynchronous Programming in Java](https://www.baeldung.com/java-asynchronous-programming)
- [Java Thread Deadlock and Livelock](https://www.baeldung.com/java-deadlock-livelock)
- [Guide to AtomicStampedReference in Java](https://www.baeldung.com/java-atomicstampedreference)
+- [The ABA Problem in Concurrency](https://www.baeldung.com/cs/aba-concurrency)
- [[<-- previous]](/core-java-modules/core-java-concurrency-advanced-2)
diff --git a/core-java-modules/core-java-concurrency-advanced-3/pom.xml b/core-java-modules/core-java-concurrency-advanced-3/pom.xml
index cf81214125..32267fb800 100644
--- a/core-java-modules/core-java-concurrency-advanced-3/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced-3/pom.xml
@@ -9,12 +9,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/exchanger/ExchangerPipeLineManualTest.java b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/exchanger/ExchangerPipeLineManualTest.java
index 093580654b..af8b8b9aa1 100644
--- a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/exchanger/ExchangerPipeLineManualTest.java
+++ b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/exchanger/ExchangerPipeLineManualTest.java
@@ -40,15 +40,15 @@ public class ExchangerPipeLineManualTest {
Runnable processor = () -> {
Queue processorBuffer = new ConcurrentLinkedQueue<>();
- Queue writterBuffer = new ConcurrentLinkedQueue<>();
+ Queue writerBuffer = new ConcurrentLinkedQueue<>();
try {
processorBuffer = readerExchanger.exchange(processorBuffer);
while (true) {
- writterBuffer.add(processorBuffer.poll());
+ writerBuffer.add(processorBuffer.poll());
if (processorBuffer.isEmpty()) {
try {
processorBuffer = readerExchanger.exchange(processorBuffer);
- writterBuffer = writerExchanger.exchange(writterBuffer);
+ writerBuffer = writerExchanger.exchange(writerBuffer);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
@@ -62,13 +62,13 @@ public class ExchangerPipeLineManualTest {
};
Runnable writer = () -> {
- Queue writterBuffer = new ConcurrentLinkedQueue<>();
+ Queue writerBuffer = new ConcurrentLinkedQueue<>();
try {
- writterBuffer = writerExchanger.exchange(writterBuffer);
+ writerBuffer = writerExchanger.exchange(writerBuffer);
while (true) {
- System.out.println(writterBuffer.poll());
- if (writterBuffer.isEmpty()) {
- writterBuffer = writerExchanger.exchange(writterBuffer);
+ System.out.println(writerBuffer.poll());
+ if (writerBuffer.isEmpty()) {
+ writerBuffer = writerExchanger.exchange(writerBuffer);
}
}
} catch (InterruptedException e) {
diff --git a/core-java-modules/core-java-concurrency-advanced/pom.xml b/core-java-modules/core-java-concurrency-advanced/pom.xml
index d39712468f..67db486121 100644
--- a/core-java-modules/core-java-concurrency-advanced/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-basic-2/pom.xml b/core-java-modules/core-java-concurrency-basic-2/pom.xml
index 8c9bbef54c..adc4fd33e3 100644
--- a/core-java-modules/core-java-concurrency-basic-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-basic/pom.xml b/core-java-modules/core-java-concurrency-basic/pom.xml
index c15200da1f..29d393805b 100644
--- a/core-java-modules/core-java-concurrency-basic/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-collections/pom.xml b/core-java-modules/core-java-concurrency-collections/pom.xml
index 5c038639a7..31f5a0fca8 100644
--- a/core-java-modules/core-java-concurrency-collections/pom.xml
+++ b/core-java-modules/core-java-concurrency-collections/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-collections
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-date-operations-1/pom.xml b/core-java-modules/core-java-date-operations-1/pom.xml
index 54cbc79678..e12e4aa4ee 100644
--- a/core-java-modules/core-java-date-operations-1/pom.xml
+++ b/core-java-modules/core-java-date-operations-1/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-date-operations-1
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-date-operations-2/pom.xml b/core-java-modules/core-java-date-operations-2/pom.xml
index ea5f852b0d..5861a9ab98 100644
--- a/core-java-modules/core-java-date-operations-2/pom.xml
+++ b/core-java-modules/core-java-date-operations-2/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-date-operations-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-datetime-conversion/pom.xml b/core-java-modules/core-java-datetime-conversion/pom.xml
index e2dd579335..79d1394576 100644
--- a/core-java-modules/core-java-datetime-conversion/pom.xml
+++ b/core-java-modules/core-java-datetime-conversion/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-datetime-conversion
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-datetime-string/pom.xml b/core-java-modules/core-java-datetime-string/pom.xml
index ceb7641320..c1181f670a 100644
--- a/core-java-modules/core-java-datetime-string/pom.xml
+++ b/core-java-modules/core-java-datetime-string/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-datetime-string
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-exceptions-2/pom.xml b/core-java-modules/core-java-exceptions-2/pom.xml
index 915ec1da69..a53bf37b77 100644
--- a/core-java-modules/core-java-exceptions-2/pom.xml
+++ b/core-java-modules/core-java-exceptions-2/pom.xml
@@ -7,12 +7,11 @@
core-java-exceptions-2
core-java-exceptions-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-exceptions/pom.xml b/core-java-modules/core-java-exceptions/pom.xml
index 0778b6b5a3..b708aff6b9 100644
--- a/core-java-modules/core-java-exceptions/pom.xml
+++ b/core-java-modules/core-java-exceptions/pom.xml
@@ -9,12 +9,11 @@
0.1.0-SNAPSHOT
core-java-exceptions
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-function/pom.xml b/core-java-modules/core-java-function/pom.xml
index 1a853d5580..0eb34bed7b 100644
--- a/core-java-modules/core-java-function/pom.xml
+++ b/core-java-modules/core-java-function/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-function
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-2/pom.xml b/core-java-modules/core-java-io-2/pom.xml
index ec27c76435..bdc2ee37f5 100644
--- a/core-java-modules/core-java-io-2/pom.xml
+++ b/core-java-modules/core-java-io-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-apis/pom.xml b/core-java-modules/core-java-io-apis/pom.xml
index 9628027309..9350e4b527 100644
--- a/core-java-modules/core-java-io-apis/pom.xml
+++ b/core-java-modules/core-java-io-apis/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-apis
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-conversions-2/README.md b/core-java-modules/core-java-io-conversions-2/README.md
index 4a28bf37c5..5cb9c21c54 100644
--- a/core-java-modules/core-java-io-conversions-2/README.md
+++ b/core-java-modules/core-java-io-conversions-2/README.md
@@ -4,6 +4,5 @@ This module contains articles about core Java input/output(IO) conversions.
### Relevant Articles:
- [Java InputStream to String](https://www.baeldung.com/convert-input-stream-to-string)
-- [Java InputStream to Byte Array and ByteBuffer](https://www.baeldung.com/convert-input-stream-to-array-of-bytes)
- [Java – Write an InputStream to a File](https://www.baeldung.com/convert-input-stream-to-a-file)
- More articles: [[<-- prev]](/core-java-modules/core-java-io-conversions)
diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml
index e95d1f4b67..5312ad645d 100644
--- a/core-java-modules/core-java-io-conversions-2/pom.xml
+++ b/core-java-modules/core-java-io-conversions-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-conversions-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
index c8c711e328..c34c32891f 100644
--- a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
+++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
@@ -2,7 +2,6 @@ package com.baeldung.inputstreamtostring;
import com.google.common.base.Charsets;
import com.google.common.io.ByteSource;
-import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
@@ -152,42 +151,6 @@ public class JavaInputStreamToXUnitTest {
assertThat(result, equalTo(originalString));
}
- // tests - InputStream to byte[]
-
- @Test
- public final void givenUsingPlainJavaOnFixedSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
- final InputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
- final byte[] targetArray = new byte[initialStream.available()];
- initialStream.read(targetArray);
- }
-
- @Test
- public final void givenUsingPlainJavaOnUnknownSizeStream_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
- final InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
-
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- int nRead;
- final byte[] data = new byte[1024];
- while ((nRead = is.read(data, 0, data.length)) != -1) {
- buffer.write(data, 0, nRead);
- }
-
- buffer.flush();
- final byte[] byteArray = buffer.toByteArray();
- }
-
- @Test
- public final void givenUsingGuava_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
- final InputStream initialStream = ByteSource.wrap(new byte[] { 0, 1, 2 }).openStream();
- final byte[] targetArray = ByteStreams.toByteArray(initialStream);
- }
-
- @Test
- public final void givenUsingCommonsIO_whenConvertingAnInputStreamToAByteArray_thenCorrect() throws IOException {
- final InputStream initialStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
- final byte[] targetArray = IOUtils.toByteArray(initialStream);
- }
-
// tests - InputStream to File
@Test
diff --git a/core-java-modules/core-java-io-conversions/pom.xml b/core-java-modules/core-java-io-conversions/pom.xml
index f5ccaa45a3..0012b02d7e 100644
--- a/core-java-modules/core-java-io-conversions/pom.xml
+++ b/core-java-modules/core-java-io-conversions/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-conversions
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io/pom.xml b/core-java-modules/core-java-io/pom.xml
index 103a809f90..ccfb57e909 100644
--- a/core-java-modules/core-java-io/pom.xml
+++ b/core-java-modules/core-java-io/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml
index 1d87bcda5f..6e9d713d7c 100644
--- a/core-java-modules/core-java-jar/pom.xml
+++ b/core-java-modules/core-java-jar/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-jar
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-jndi/pom.xml b/core-java-modules/core-java-jndi/pom.xml
index 4a491a1a47..030a5f5d50 100644
--- a/core-java-modules/core-java-jndi/pom.xml
+++ b/core-java-modules/core-java-jndi/pom.xml
@@ -12,7 +12,7 @@
com.baeldung.core-java-modules
core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/core-java-modules/core-java-jpms/pom.xml b/core-java-modules/core-java-jpms/pom.xml
index 4610baab49..5809c0f579 100644
--- a/core-java-modules/core-java-jpms/pom.xml
+++ b/core-java-modules/core-java-jpms/pom.xml
@@ -12,7 +12,7 @@
com.baeldung.core-java-modules
core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/core-java-modules/core-java-jvm/pom.xml b/core-java-modules/core-java-jvm/pom.xml
index edf7a4f3c5..f3e5470a61 100644
--- a/core-java-modules/core-java-jvm/pom.xml
+++ b/core-java-modules/core-java-jvm/pom.xml
@@ -10,10 +10,10 @@
jar
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
- ../../
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
diff --git a/core-java-modules/core-java-lambdas/pom.xml b/core-java-modules/core-java-lambdas/pom.xml
index 421ca2f394..318b04fcf5 100644
--- a/core-java-modules/core-java-lambdas/pom.xml
+++ b/core-java-modules/core-java-lambdas/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-lambdas
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-2/pom.xml b/core-java-modules/core-java-lang-2/pom.xml
index 5aa80ce3df..5f2d4ec901 100644
--- a/core-java-modules/core-java-lang-2/pom.xml
+++ b/core-java-modules/core-java-lang-2/pom.xml
@@ -8,19 +8,23 @@
0.1.0-SNAPSHOT
core-java-lang-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
org.apache.commons
commons-lang3
- 3.9
+ ${commons-lang3.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
commons-beanutils
@@ -65,6 +69,8 @@
1.19
3.12.2
1.9.4
+ 3.10
+ 29.0-jre
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEquals.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEquals.java
new file mode 100644
index 0000000000..e3a61fc05a
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEquals.java
@@ -0,0 +1,51 @@
+package com.baeldung.comparing;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+public class PersonWithEquals {
+ private String firstName;
+ private String lastName;
+ private LocalDate birthDate;
+
+ public PersonWithEquals(String firstName, String lastName) {
+ if (firstName == null || lastName == null) {
+ throw new NullPointerException("Names can't be null");
+ }
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public PersonWithEquals(String firstName, String lastName, LocalDate birthDate) {
+ this(firstName, lastName);
+
+ this.birthDate = birthDate;
+ }
+
+ public String firstName() {
+ return firstName;
+ }
+
+ public String lastName() {
+ return lastName;
+ }
+
+ public LocalDate birthDate() {
+ return birthDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PersonWithEquals that = (PersonWithEquals) o;
+ return firstName.equals(that.firstName) &&
+ lastName.equals(that.lastName) &&
+ Objects.equals(birthDate, that.birthDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(firstName, lastName);
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparable.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparable.java
new file mode 100644
index 0000000000..5611ce8a09
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparable.java
@@ -0,0 +1,62 @@
+package com.baeldung.comparing;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+public class PersonWithEqualsAndComparable implements Comparable {
+ private String firstName;
+ private String lastName;
+ private LocalDate birthDate;
+
+ public PersonWithEqualsAndComparable(String firstName, String lastName) {
+ if (firstName == null || lastName == null) {
+ throw new NullPointerException("Names can't be null");
+ }
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public PersonWithEqualsAndComparable(String firstName, String lastName, LocalDate birthDate) {
+ this(firstName, lastName);
+
+ this.birthDate = birthDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PersonWithEqualsAndComparable that = (PersonWithEqualsAndComparable) o;
+ return firstName.equals(that.firstName) &&
+ lastName.equals(that.lastName) &&
+ Objects.equals(birthDate, that.birthDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(firstName, lastName);
+ }
+
+ @Override
+ public int compareTo(PersonWithEqualsAndComparable o) {
+ int lastNamesComparison = this.lastName.compareTo(o.lastName);
+ if (lastNamesComparison == 0) {
+ int firstNamesComparison = this.firstName.compareTo(o.firstName);
+ if (firstNamesComparison == 0) {
+ if (this.birthDate != null && o.birthDate != null) {
+ return this.birthDate.compareTo(o.birthDate);
+ } else if (this.birthDate != null) {
+ return 1;
+ } else if (o.birthDate != null) {
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ return firstNamesComparison;
+ }
+ } else {
+ return lastNamesComparison;
+ }
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparableUsingComparator.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparableUsingComparator.java
new file mode 100644
index 0000000000..ed322cb353
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndComparableUsingComparator.java
@@ -0,0 +1,60 @@
+package com.baeldung.comparing;
+
+import java.time.LocalDate;
+import java.util.Comparator;
+import java.util.Objects;
+
+public class PersonWithEqualsAndComparableUsingComparator implements Comparable {
+ private String firstName;
+ private String lastName;
+ private LocalDate birthDate;
+
+ public PersonWithEqualsAndComparableUsingComparator(String firstName, String lastName) {
+ if (firstName == null || lastName == null) {
+ throw new NullPointerException("Names can't be null");
+ }
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public PersonWithEqualsAndComparableUsingComparator(String firstName, String lastName, LocalDate birthDate) {
+ this(firstName, lastName);
+
+ this.birthDate = birthDate;
+ }
+
+ public String firstName() {
+ return firstName;
+ }
+
+ public String lastName() {
+ return lastName;
+ }
+
+ public LocalDate birthDate() {
+ return birthDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PersonWithEqualsAndComparableUsingComparator that = (PersonWithEqualsAndComparableUsingComparator) o;
+ return firstName.equals(that.firstName) &&
+ lastName.equals(that.lastName) &&
+ Objects.equals(birthDate, that.birthDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(firstName, lastName);
+ }
+
+ @Override
+ public int compareTo(PersonWithEqualsAndComparableUsingComparator o) {
+ return Comparator.comparing(PersonWithEqualsAndComparableUsingComparator::lastName)
+ .thenComparing(PersonWithEqualsAndComparableUsingComparator::firstName)
+ .thenComparing(PersonWithEqualsAndComparableUsingComparator::birthDate, Comparator.nullsLast(Comparator.naturalOrder()))
+ .compare(this, o);
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndWrongComparable.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndWrongComparable.java
new file mode 100644
index 0000000000..e0bdaa413a
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithEqualsAndWrongComparable.java
@@ -0,0 +1,44 @@
+package com.baeldung.comparing;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+public class PersonWithEqualsAndWrongComparable implements Comparable {
+ private String firstName;
+ private String lastName;
+ private LocalDate birthDate;
+
+ public PersonWithEqualsAndWrongComparable(String firstName, String lastName) {
+ if (firstName == null || lastName == null) {
+ throw new NullPointerException("Names can't be null");
+ }
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public PersonWithEqualsAndWrongComparable(String firstName, String lastName, LocalDate birthDate) {
+ this(firstName, lastName);
+
+ this.birthDate = birthDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PersonWithEqualsAndWrongComparable that = (PersonWithEqualsAndWrongComparable) o;
+ return firstName.equals(that.firstName) &&
+ lastName.equals(that.lastName) &&
+ Objects.equals(birthDate, that.birthDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(firstName, lastName);
+ }
+
+ @Override
+ public int compareTo(PersonWithEqualsAndWrongComparable o) {
+ return this.lastName.compareTo(o.lastName);
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithoutEquals.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithoutEquals.java
new file mode 100644
index 0000000000..bb4c6b958b
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/comparing/PersonWithoutEquals.java
@@ -0,0 +1,11 @@
+package com.baeldung.comparing;
+
+public class PersonWithoutEquals {
+ private String firstName;
+ private String lastName;
+
+ public PersonWithoutEquals(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java
new file mode 100644
index 0000000000..8d7c626521
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java
@@ -0,0 +1,36 @@
+package com.baeldung.inttoenum;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum PizzaStatus {
+ ORDERED(5),
+ READY(2),
+ DELIVERED(0);
+
+ private int timeToDelivery;
+
+ PizzaStatus(int timeToDelivery) {
+ this.timeToDelivery = timeToDelivery;
+ }
+
+ public int getTimeToDelivery() {
+ return timeToDelivery;
+ }
+
+ private static Map timeToDeliveryToEnumValuesMapping = new HashMap<>();
+
+ static {
+ PizzaStatus[] pizzaStatuses = PizzaStatus.values();
+ for (int pizzaStatusIndex = 0; pizzaStatusIndex < pizzaStatuses.length; pizzaStatusIndex++) {
+ timeToDeliveryToEnumValuesMapping.put(
+ pizzaStatuses[pizzaStatusIndex].getTimeToDelivery(),
+ pizzaStatuses[pizzaStatusIndex]
+ );
+ }
+ }
+
+ public static PizzaStatus castIntToEnum(int timeToDelivery) {
+ return timeToDeliveryToEnumValuesMapping.get(timeToDelivery);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ApacheCommonsObjectUtilsUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ApacheCommonsObjectUtilsUnitTest.java
new file mode 100644
index 0000000000..33b8dcb3fc
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ApacheCommonsObjectUtilsUnitTest.java
@@ -0,0 +1,59 @@
+package com.baeldung.comparing;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ApacheCommonsObjectUtilsUnitTest {
+
+ @Test
+ void givenTwoStringsWithSameValues_whenApacheCommonsEqualityMethods_thenEqualsTrueNotEqualsFalse() {
+ String a = new String("Hello!");
+ String b = new String("Hello!");
+
+ assertThat(ObjectUtils.equals(a, b)).isTrue();
+ assertThat(ObjectUtils.notEqual(a, b)).isFalse();
+ }
+
+ @Test
+ void givenTwoStringsWithDifferentValues_whenApacheCommonsEqualityMethods_thenEqualsFalseNotEqualsTrue() {
+ String a = new String("Hello!");
+ String b = new String("Hello World!");
+
+ assertThat(ObjectUtils.equals(a, b)).isFalse();
+ assertThat(ObjectUtils.notEqual(a, b)).isTrue();
+ }
+
+ @Test
+ void givenTwoStringsWithConsecutiveValues_whenApacheCommonsCompare_thenNegative() {
+ String first = new String("Hello!");
+ String second = new String("How are you?");
+
+ assertThat(ObjectUtils.compare(first, second)).isNegative();
+ }
+
+ @Test
+ void givenTwoStringsWithSameValues_whenApacheCommonsEqualityMethods_thenEqualsFalseNotEqualsTrue() {
+ String first = new String("Hello!");
+ String second = new String("Hello!");
+
+ assertThat(ObjectUtils.compare(first, second)).isZero();
+ }
+
+ @Test
+ void givenTwoStringsWithConsecutiveValues_whenApacheCommonsCompareReversed_thenPositive() {
+ String first = new String("Hello!");
+ String second = new String("How are you?");
+
+ assertThat(ObjectUtils.compare(second, first)).isPositive();
+ }
+
+ @Test
+ void givenTwoStringsOneNull_whenApacheCommonsCompare_thenPositive() {
+ String first = new String("Hello!");
+ String second = null;
+
+ assertThat(ObjectUtils.compare(first, second, false)).isPositive();
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparableInterfaceUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparableInterfaceUnitTest.java
new file mode 100644
index 0000000000..281c4a0201
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparableInterfaceUnitTest.java
@@ -0,0 +1,107 @@
+package com.baeldung.comparing;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ComparableInterfaceUnitTest {
+
+ @Test
+ void givenTwoConsecutiveStrings_whenCompareTo_thenNegative() {
+ String first = "Google";
+ String second = "Microsoft";
+
+ assertThat(first.compareTo(second)).isNegative();
+ }
+
+ @Test
+ void givenTwoEqualsStrings_whenCompareTo_thenZero() {
+ String first = "Google";
+ String second = "Google";
+
+ assertThat(first.compareTo(second)).isZero();
+ }
+
+ @Test
+ void givenTwoConsecutiveStrings_whenReversedCompareTo_thenPositive() {
+ String first = "Google";
+ String second = "Microsoft";
+
+ assertThat(second.compareTo(first)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndWrongComparableAndConsecutiveLastNames_whenCompareTo_thenNegative() {
+ PersonWithEqualsAndWrongComparable richard = new PersonWithEqualsAndWrongComparable("Richard", "Jefferson");
+ PersonWithEqualsAndWrongComparable joe = new PersonWithEqualsAndWrongComparable("Joe", "Portman");
+
+ assertThat(richard.compareTo(joe)).isNegative();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndWrongComparableAndSameLastNames_whenReversedCompareTo_thenZero() {
+ PersonWithEqualsAndWrongComparable richard = new PersonWithEqualsAndWrongComparable("Richard", "Jefferson");
+ PersonWithEqualsAndWrongComparable mike = new PersonWithEqualsAndWrongComparable("Mike", "Jefferson");
+
+ assertThat(richard.compareTo(mike)).isZero();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndWrongComparableAndConsecutiveLastNames_whenReversedCompareTo_thenPositive() {
+ PersonWithEqualsAndWrongComparable richard = new PersonWithEqualsAndWrongComparable("Richard", "Jefferson");
+ PersonWithEqualsAndWrongComparable joe = new PersonWithEqualsAndWrongComparable("Joe", "Portman");
+
+ assertThat(joe.compareTo(richard)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndWrongComparableAndSameLastNames_whenSortedSet_thenProblem() {
+ PersonWithEqualsAndWrongComparable richard = new PersonWithEqualsAndWrongComparable("Richard", "Jefferson");
+ PersonWithEqualsAndWrongComparable mike = new PersonWithEqualsAndWrongComparable("Mike", "Jefferson");
+
+ SortedSet people = new TreeSet<>();
+ people.add(richard);
+ people.add(mike);
+
+ assertThat(people).containsExactly(richard);
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableAndConsecutiveLastNames_whenCompareTo_thenNegative() {
+ PersonWithEqualsAndComparable richard = new PersonWithEqualsAndComparable("Richard", "Jefferson");
+ PersonWithEqualsAndComparable joe = new PersonWithEqualsAndComparable("Joe", "Portman");
+
+ assertThat(richard.compareTo(joe)).isNegative();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableAndSameLastNames_whenReversedCompareTo_thenZero() {
+ PersonWithEqualsAndComparable richard = new PersonWithEqualsAndComparable("Richard", "Jefferson");
+ PersonWithEqualsAndComparable mike = new PersonWithEqualsAndComparable("Mike", "Jefferson");
+
+ assertThat(richard.compareTo(mike)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableAndConsecutiveLastNames_whenReversedCompareTo_thenPositive() {
+ PersonWithEqualsAndComparable richard = new PersonWithEqualsAndComparable("Richard", "Jefferson");
+ PersonWithEqualsAndComparable joe = new PersonWithEqualsAndComparable("Joe", "Portman");
+
+ assertThat(joe.compareTo(richard)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableAndSameLastNames_whenSortedSet_thenProblem() {
+ PersonWithEqualsAndComparable richard = new PersonWithEqualsAndComparable("Richard", "Jefferson");
+ PersonWithEqualsAndComparable mike = new PersonWithEqualsAndComparable("Mike", "Jefferson");
+
+ SortedSet people = new TreeSet<>();
+ people.add(richard);
+ people.add(mike);
+
+ assertThat(people).containsExactly(mike, richard);
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparatorInterfaceUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparatorInterfaceUnitTest.java
new file mode 100644
index 0000000000..769ae60bed
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ComparatorInterfaceUnitTest.java
@@ -0,0 +1,81 @@
+package com.baeldung.comparing;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ComparatorInterfaceUnitTest {
+
+ @Test
+ void givenListOfTwoPersonWithEqualsAndComparatorByFirstName_whenSort_thenSortedByFirstNames() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals allan = new PersonWithEquals("Allan", "Dale");
+
+ List people = new ArrayList<>();
+ people.add(joe);
+ people.add(allan);
+
+ Comparator compareByFirstNames = new Comparator() {
+ @Override
+ public int compare(PersonWithEquals o1, PersonWithEquals o2) {
+ return o1.firstName().compareTo(o2.firstName());
+ }
+ };
+ people.sort(compareByFirstNames);
+
+ assertThat(people).containsExactly(allan, joe);
+ }
+
+ @Test
+ void givenListOfTwoPersonWithEqualsAndComparatorByFirstNameFunctionalStyle_whenSort_thenSortedByFirstNames() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals allan = new PersonWithEquals("Allan", "Dale");
+
+ List people = new ArrayList<>();
+ people.add(joe);
+ people.add(allan);
+
+ Comparator compareByFirstNames = Comparator.comparing(PersonWithEquals::firstName);
+ people.sort(compareByFirstNames);
+
+ assertThat(people).containsExactly(allan, joe);
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableUsingComparatorAndConsecutiveLastNames_whenCompareTo_thenNegative() {
+ PersonWithEqualsAndComparableUsingComparator richard = new PersonWithEqualsAndComparableUsingComparator("Richard", "Jefferson");
+ PersonWithEqualsAndComparableUsingComparator joe = new PersonWithEqualsAndComparableUsingComparator("Joe", "Portman");
+
+ assertThat(richard.compareTo(joe)).isNegative();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableUsingComparatorAndSameLastNames_whenReversedCompareTo_thenZero() {
+ PersonWithEqualsAndComparableUsingComparator richard = new PersonWithEqualsAndComparableUsingComparator("Richard", "Jefferson");
+ PersonWithEqualsAndComparableUsingComparator mike = new PersonWithEqualsAndComparableUsingComparator("Mike", "Jefferson");
+
+ assertThat(richard.compareTo(mike)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableUsingComparatorAndConsecutiveLastNames_whenReversedCompareTo_thenPositive() {
+ PersonWithEqualsAndComparableUsingComparator richard = new PersonWithEqualsAndComparableUsingComparator("Richard", "Jefferson");
+ PersonWithEqualsAndComparableUsingComparator joe = new PersonWithEqualsAndComparableUsingComparator("Joe", "Portman");
+
+ assertThat(joe.compareTo(richard)).isPositive();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsAndComparableUsingComparatorAndSameLastNames_whenSortedSet_thenProblem() {
+ PersonWithEqualsAndComparableUsingComparator richard = new PersonWithEqualsAndComparableUsingComparator("Richard", "Jefferson");
+ PersonWithEqualsAndComparableUsingComparator mike = new PersonWithEqualsAndComparableUsingComparator("Mike", "Jefferson");
+
+ SortedSet people = new TreeSet<>();
+ people.add(richard);
+ people.add(mike);
+
+ assertThat(people).containsExactly(mike, richard);
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualityOperatorUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualityOperatorUnitTest.java
new file mode 100644
index 0000000000..ebcf83ef5b
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualityOperatorUnitTest.java
@@ -0,0 +1,116 @@
+package com.baeldung.comparing;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class EqualityOperatorUnitTest {
+
+ @Test
+ void givenTwoIntsWithSameValues_whenEqualityOperators_thenConsideredSame() {
+ int a = 1;
+ int b = 1;
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoIntsWithDifferentValues_whenEqualityOperators_thenNotConsideredSame() {
+ int a = 1;
+ int b = 2;
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+
+ @Test
+ void givenTwoIntsWithSameValuesOneWrapped_whenEqualityOperators_thenConsideredSame() {
+ int a = 1;
+ Integer b = new Integer(1);
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoIntsWithDifferentValuesOneWrapped_whenEqualityOperators_thenNotConsideredSame() {
+ int a = 1;
+ Integer b = new Integer(2);
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+
+ @Test
+ void givenTwoIntegersWithSameValues_whenEqualityOperators_thenNotConsideredSame() {
+ Integer a = new Integer(1);
+ Integer b = new Integer(1);
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+
+ @Test
+ void givenTwoIntegersWithDifferentValues_whenEqualityOperators_thenNotConsideredSame() {
+ Integer a = new Integer(1);
+ Integer b = new Integer(2);
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+
+ @Test
+ void givenTwoIntegersWithSameReference_whenEqualityOperators_thenConsideredSame() {
+ Integer a = new Integer(1);
+ Integer b = a;
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoIntegersFromValueOfWithSameValues_whenEqualityOperators_thenConsideredSame() {
+ Integer a = Integer.valueOf(1);
+ Integer b = Integer.valueOf(1);
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoStringsWithSameValues_whenEqualityOperators_thenNotConsideredSame() {
+ String a = new String("Hello!");
+ String b = new String("Hello!");
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+
+ @Test
+ void givenTwoStringsFromLiteralsWithSameValues_whenEqualityOperators_thenConsideredSame() {
+ String a = "Hello!";
+ String b = "Hello!";
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoNullObjects_whenEqualityOperators_thenConsideredSame() {
+ Object a = null;
+ Object b = null;
+
+ assertThat(a == b).isTrue();
+ assertThat(a != b).isFalse();
+ }
+
+ @Test
+ void givenTwoObjectsOneNull_whenEqualityOperators_thenNotConsideredSame() {
+ Object a = null;
+ Object b = "Hello!";
+
+ assertThat(a == b).isFalse();
+ assertThat(a != b).isTrue();
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualsMethodUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualsMethodUnitTest.java
new file mode 100644
index 0000000000..a69ac38916
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/EqualsMethodUnitTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.comparing;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class EqualsMethodUnitTest {
+
+ @Test
+ void givenTwoIntegersWithSameValue_whenEquals_thenTrue() {
+ Integer a = new Integer(1);
+ Integer b = new Integer(1);
+
+ assertThat(a.equals(b)).isTrue();
+ }
+
+ @Test
+ void givenTwoStringsWithSameValue_whenEquals_thenTrue() {
+ String a = new String("Hello!");
+ String b = new String("Hello!");
+
+ assertThat(a.equals(b)).isTrue();
+ }
+
+ @Test
+ void givenTwoStringsWithDifferentValue_whenEquals_thenFalse() {
+ String a = new String("Hello!");
+ String b = new String("Hello World!");
+
+ assertThat(a.equals(b)).isFalse();
+ }
+
+ @Test
+ void givenTwoObjectsFirstNull_whenEquals_thenNullPointerExceptionThrown() {
+ Object a = null;
+ Object b = new String("Hello!");
+
+ assertThrows(NullPointerException.class, () -> a.equals(b));
+ }
+
+ @Test
+ void givenTwoObjectsSecondNull_whenEquals_thenFalse() {
+ Object a = new String("Hello!");
+ Object b = null;
+
+ assertThat(a.equals(b)).isFalse();
+ }
+
+ @Test
+ void givenTwoPersonWithoutEqualsWithSameNames_whenEquals_thenFalse() {
+ PersonWithoutEquals joe = new PersonWithoutEquals("Joe", "Portman");
+ PersonWithoutEquals joeAgain = new PersonWithoutEquals("Joe", "Portman");
+
+ assertThat(joe.equals(joeAgain)).isFalse();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsWithSameNames_whenEquals_thenTrue() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals joeAgain = new PersonWithEquals("Joe", "Portman");
+
+ assertThat(joe.equals(joeAgain)).isTrue();
+ }
+
+ @Test
+ void givenTwoPersonWittEqualsWithDifferentNames_whenEquals_thenFalse() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals natalie = new PersonWithEquals("Natalie", "Portman");
+
+ assertThat(joe.equals(natalie)).isFalse();
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/GuavaUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/GuavaUnitTest.java
new file mode 100644
index 0000000000..5c8591e134
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/GuavaUnitTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.comparing;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.primitives.Ints;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class GuavaUnitTest {
+
+ @Nested
+ class ObjectsEqualMethod {
+ @Test
+ void givenTwoStringsWithSameValues_whenObjectsEqualMethods_thenTrue() {
+ String a = new String("Hello!");
+ String b = new String("Hello!");
+
+ assertThat(Objects.equal(a, b)).isTrue();
+ }
+
+ @Test
+ void givenTwoStringsWithDifferentValues_whenObjectsEqualMethods_thenFalse() {
+ String a = new String("Hello!");
+ String b = new String("Hello World!");
+
+ assertThat(Objects.equal(a, b)).isFalse();
+ }
+ }
+
+ @Nested
+ class ComparisonMethods {
+ @Test
+ void givenTwoIntsWithConsecutiveValues_whenIntsCompareMethods_thenNegative() {
+ int first = 1;
+ int second = 2;
+ assertThat(Ints.compare(first, second)).isNegative();
+ }
+
+ @Test
+ void givenTwoIntsWithSameValues_whenIntsCompareMethods_thenZero() {
+ int first = 1;
+ int second = 1;
+
+ assertThat(Ints.compare(first, second)).isZero();
+ }
+
+ @Test
+ void givenTwoIntsWithConsecutiveValues_whenIntsCompareMethodsReversed_thenNegative() {
+ int first = 1;
+ int second = 2;
+
+ assertThat(Ints.compare(second, first)).isPositive();
+ }
+ }
+
+ @Nested
+ class ComparisonChainClass {
+ @Test
+ void givenTwoPersonWithEquals_whenComparisonChainByLastNameThenFirstName_thenSortedJoeFirstAndNatalieSecond() {
+ PersonWithEquals natalie = new PersonWithEquals("Natalie", "Portman");
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+
+ int comparisonResult = ComparisonChain.start()
+ .compare(natalie.lastName(), joe.lastName())
+ .compare(natalie.firstName(), joe.firstName())
+ .result();
+
+ assertThat(comparisonResult).isPositive();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ObjectsEqualsStaticMethodUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ObjectsEqualsStaticMethodUnitTest.java
new file mode 100644
index 0000000000..5ac89da2be
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/comparing/ObjectsEqualsStaticMethodUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.comparing;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ObjectsEqualsStaticMethodUnitTest {
+
+ @Test
+ void givenTwoPersonWithEqualsWithSameNames_whenObjectsEquals_thenTrue() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals joeAgain = new PersonWithEquals("Joe", "Portman");
+
+ assertThat(Objects.equals(joe, joeAgain)).isTrue();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsWithDifferentNames_whenObjectsEquals_thenFalse() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals natalie = new PersonWithEquals("Natalie", "Portman");
+
+ assertThat(Objects.equals(joe, natalie)).isFalse();
+ }
+
+ @Test
+ void givenTwoPersonWithEqualsFirstNull_whenObjectsEquals_thenFalse() {
+ PersonWithEquals nobody = null;
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+
+ assertThat(Objects.equals(nobody, joe)).isFalse();
+ }
+
+ @Test
+ void givenTwoObjectsSecondtNull_whenObjectsEquals_thenFalse() {
+ PersonWithEquals joe = new PersonWithEquals("Joe", "Portman");
+ PersonWithEquals nobody = null;
+
+ assertThat(Objects.equals(joe, nobody)).isFalse();
+ }
+
+ @Test
+ void givenTwoObjectsNull_whenObjectsEquals_thenTrue() {
+ PersonWithEquals nobody = null;
+ PersonWithEquals nobodyAgain = null;
+
+ assertThat(Objects.equals(nobody, nobodyAgain)).isTrue();
+ }
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java
new file mode 100644
index 0000000000..876c230827
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.inttoenum;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class IntToEnumUnitTest {
+
+ @Test
+ public void whenIntToEnumUsingValuesMethod_thenReturnEnumObject() {
+ int timeToDeliveryForOrderedPizzaStatus = 5;
+ PizzaStatus[] pizzaStatuses = PizzaStatus.values();
+ PizzaStatus pizzaOrderedStatus = null;
+ for (int pizzaStatusIndex = 0; pizzaStatusIndex < pizzaStatuses.length; pizzaStatusIndex++) {
+ if (pizzaStatuses[pizzaStatusIndex].getTimeToDelivery() == timeToDeliveryForOrderedPizzaStatus) {
+ pizzaOrderedStatus = pizzaStatuses[pizzaStatusIndex];
+ }
+ }
+ assertEquals(pizzaOrderedStatus, PizzaStatus.ORDERED);
+ }
+
+ @Test
+ public void whenIntToEnumUsingMap_thenReturnEnumObject() {
+ int timeToDeliveryForOrderedPizzaStatus = 5;
+ assertEquals(PizzaStatus.castIntToEnum(timeToDeliveryForOrderedPizzaStatus), PizzaStatus.ORDERED);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-math-2/pom.xml b/core-java-modules/core-java-lang-math-2/pom.xml
index 92ebcc6a94..e2cced4fbf 100644
--- a/core-java-modules/core-java-lang-math-2/pom.xml
+++ b/core-java-modules/core-java-lang-math-2/pom.xml
@@ -5,12 +5,11 @@
core-java-lang-math-2
0.0.1-SNAPSHOT
core-java-lang-math-2
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-math/pom.xml b/core-java-modules/core-java-lang-math/pom.xml
index bcb5cf39d2..81ff0d43ea 100644
--- a/core-java-modules/core-java-lang-math/pom.xml
+++ b/core-java-modules/core-java-lang-math/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-lang-math
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-oop-constructors/pom.xml b/core-java-modules/core-java-lang-oop-constructors/pom.xml
index 76507103ea..e54286a822 100644
--- a/core-java-modules/core-java-lang-oop-constructors/pom.xml
+++ b/core-java-modules/core-java-lang-oop-constructors/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-lang-oop-generics/pom.xml b/core-java-modules/core-java-lang-oop-generics/pom.xml
index ae141ecda2..65a0aeac59 100644
--- a/core-java-modules/core-java-lang-oop-generics/pom.xml
+++ b/core-java-modules/core-java-lang-oop-generics/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java
new file mode 100644
index 0000000000..2021f42239
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java
@@ -0,0 +1,18 @@
+package com.baeldung.supertype;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public abstract class TypeReference {
+
+ private final Type type;
+
+ public TypeReference() {
+ Type superclass = getClass().getGenericSuperclass();
+ type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ }
+
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java
new file mode 100644
index 0000000000..24e3b698e2
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.supertype;
+
+import org.junit.Test;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TypeReferenceUnitTest {
+
+ @Test
+ public void givenGenericToken_whenUsingSuperTypeToken_thenPreservesTheTypeInfo() {
+ TypeReference
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+
org.awaitility
awaitility
@@ -184,6 +189,7 @@
RELEASE
3.0
1.8.1
+ 2.5.0
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java
new file mode 100644
index 0000000000..8c1351642f
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java
@@ -0,0 +1,28 @@
+package com.baeldung.kafka.consumer;
+
+class CountryPopulation {
+
+ private String country;
+ private Integer population;
+
+ public CountryPopulation(String country, Integer population) {
+ this.country = country;
+ this.population = population;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Integer getPopulation() {
+ return population;
+ }
+
+ public void setPopulation(Integer population) {
+ this.population = population;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java
new file mode 100644
index 0000000000..ba4dfe6f3b
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java
@@ -0,0 +1,60 @@
+package com.baeldung.kafka.consumer;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.stream.StreamSupport;
+
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.errors.WakeupException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CountryPopulationConsumer {
+
+ private static Logger logger = LoggerFactory.getLogger(CountryPopulationConsumer.class);
+
+ private Consumer consumer;
+ private java.util.function.Consumer exceptionConsumer;
+ private java.util.function.Consumer countryPopulationConsumer;
+
+ public CountryPopulationConsumer(
+ Consumer consumer, java.util.function.Consumer exceptionConsumer,
+ java.util.function.Consumer countryPopulationConsumer) {
+ this.consumer = consumer;
+ this.exceptionConsumer = exceptionConsumer;
+ this.countryPopulationConsumer = countryPopulationConsumer;
+ }
+
+ void startBySubscribing(String topic) {
+ consume(() -> consumer.subscribe(Collections.singleton(topic)));
+ }
+
+ void startByAssigning(String topic, int partition) {
+ consume(() -> consumer.assign(Collections.singleton(new TopicPartition(topic, partition))));
+ }
+
+ private void consume(Runnable beforePollingTask) {
+ try {
+ beforePollingTask.run();
+ while (true) {
+ ConsumerRecords records = consumer.poll(Duration.ofMillis(1000));
+ StreamSupport.stream(records.spliterator(), false)
+ .map(record -> new CountryPopulation(record.key(), record.value()))
+ .forEach(countryPopulationConsumer);
+ consumer.commitSync();
+ }
+ } catch (WakeupException e) {
+ logger.info("Shutting down...");
+ } catch (RuntimeException ex) {
+ exceptionConsumer.accept(ex);
+ } finally {
+ consumer.close();
+ }
+ }
+
+ public void stop() {
+ consumer.wakeup();
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java b/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java
new file mode 100644
index 0000000000..1b49c71716
--- /dev/null
+++ b/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.kafka.consumer;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.consumer.MockConsumer;
+import org.apache.kafka.clients.consumer.OffsetResetStrategy;
+import org.apache.kafka.common.KafkaException;
+import org.apache.kafka.common.TopicPartition;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CountryPopulationConsumerUnitTest {
+
+ private static final String TOPIC = "topic";
+ private static final int PARTITION = 0;
+
+ private CountryPopulationConsumer countryPopulationConsumer;
+
+ private List updates;
+ private Throwable pollException;
+
+ private MockConsumer consumer;
+
+ @BeforeEach
+ void setUp() {
+ consumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST);
+ updates = new ArrayList<>();
+ countryPopulationConsumer = new CountryPopulationConsumer(consumer, ex -> this.pollException = ex, updates::add);
+ }
+
+ @Test
+ void whenStartingByAssigningTopicPartition_thenExpectUpdatesAreConsumedCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> consumer.addRecord(record(TOPIC, PARTITION, "Romania", 19_410_000)));
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, PARTITION);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startByAssigning(TOPIC, PARTITION);
+
+ // THEN
+ assertThat(updates).hasSize(1);
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ @Test
+ void whenStartingBySubscribingToTopic_thenExpectUpdatesAreConsumedCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> {
+ consumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC, 0)));
+ consumer.addRecord(record(TOPIC, PARTITION, "Romania", 19_410_000));
+ });
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, PARTITION);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startBySubscribing(TOPIC);
+
+ // THEN
+ assertThat(updates).hasSize(1);
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ @Test
+ void whenStartingBySubscribingToTopicAndExceptionOccurs_thenExpectExceptionIsHandledCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> consumer.setPollException(new KafkaException("poll exception")));
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, 0);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startBySubscribing(TOPIC);
+
+ // THEN
+ assertThat(pollException).isInstanceOf(KafkaException.class).hasMessage("poll exception");
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ private ConsumerRecord record(String topic, int partition, String country, int population) {
+ return new ConsumerRecord<>(topic, partition, 0, country, population);
+ }
+}
\ No newline at end of file
diff --git a/libraries-http-2/pom.xml b/libraries-http-2/pom.xml
index c0a4f6455d..73fe6c66bd 100644
--- a/libraries-http-2/pom.xml
+++ b/libraries-http-2/pom.xml
@@ -35,6 +35,37 @@
${mockwebserver.version}
test
+
+
+ org.eclipse.jetty
+ jetty-reactive-httpclient
+ ${jetty.httpclient.version}
+
+
+ org.eclipse.jetty
+ jetty-server
+ ${jetty.server.version}
+
+
+ io.reactivex.rxjava2
+ rxjava
+ ${rxjava2.version}
+
+
+ org.springframework
+ spring-webflux
+ ${spring.webflux.version}
+
+
+ io.projectreactor
+ reactor-core
+ ${reactor.version}
+
+
+ org.reactivestreams
+ reactive-streams
+ ${reactive.stream.version}
+
@@ -42,6 +73,12 @@
2.8.5
3.14.2
2.9.8
+ 1.0.3
+ 9.4.19.v20190610
+ 2.2.11
+ 5.1.9.RELEASE
+ 1.0.3
+ 3.2.12.RELEASE
diff --git a/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/BlockingSubscriber.java b/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/BlockingSubscriber.java
new file mode 100644
index 0000000000..6986172cd3
--- /dev/null
+++ b/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/BlockingSubscriber.java
@@ -0,0 +1,35 @@
+package com.baeldung.jetty.httpclient;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.reactive.client.ReactiveResponse;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+public class BlockingSubscriber implements Subscriber {
+ BlockingQueue sink = new LinkedBlockingQueue<>(1);
+
+ @Override
+ public void onSubscribe(Subscription subscription) {
+ subscription.request(1);
+ }
+
+ @Override
+ public void onNext(ReactiveResponse response) {
+ sink.offer(response);
+ }
+
+ @Override
+ public void onError(Throwable failure) {
+ }
+
+ @Override
+ public void onComplete() {
+ }
+
+ public ReactiveResponse block() throws InterruptedException {
+ return sink.poll(5, TimeUnit.SECONDS);
+ }
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/RequestHandler.java b/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/RequestHandler.java
new file mode 100644
index 0000000000..c3dbff9b11
--- /dev/null
+++ b/libraries-http-2/src/main/java/com/baeldung/jetty/httpclient/RequestHandler.java
@@ -0,0 +1,21 @@
+package com.baeldung.jetty.httpclient;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.IO;
+
+public class RequestHandler extends AbstractHandler {
+
+ @Override
+ public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ jettyRequest.setHandled(true);
+ response.setContentType(request.getContentType());
+ IO.copy(request.getInputStream(), response.getOutputStream());
+ }
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/AbstractUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/AbstractUnitTest.java
new file mode 100644
index 0000000000..4a3e67a7c5
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/AbstractUnitTest.java
@@ -0,0 +1,54 @@
+package com.baeldung.jetty.httpclient;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class AbstractUnitTest {
+
+ protected HttpClient httpClient;
+ protected Server server;
+ protected static final String CONTENT = "Hello World!";
+ protected final int port = 9080;
+
+ @Before
+ public void init() {
+ startServer(new RequestHandler());
+ startClient();
+ }
+
+ private void startClient() {
+ httpClient = new HttpClient();
+ try {
+ httpClient.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void startServer(Handler handler) {
+ server = new Server(port);
+ server.setHandler(handler);
+ try {
+ server.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @After
+ public void dispose() throws Exception {
+ if (httpClient != null) {
+ httpClient.stop();
+ }
+ if (server != null) {
+ server.stop();
+ }
+ }
+
+ protected String uri() {
+ return "http://localhost:" + port;
+ }
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ProjectReactorUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ProjectReactorUnitTest.java
new file mode 100644
index 0000000000..6d79773609
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ProjectReactorUnitTest.java
@@ -0,0 +1,30 @@
+package com.baeldung.jetty.httpclient;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.reactive.client.ReactiveRequest;
+import org.eclipse.jetty.reactive.client.ReactiveResponse;
+import org.junit.Assert;
+import org.junit.Test;
+import org.reactivestreams.Publisher;
+
+import reactor.core.publisher.Mono;
+
+public class ProjectReactorUnitTest extends AbstractUnitTest {
+
+ @Test
+ public void givenReactiveClient_whenRequested_shouldReturn200() throws Exception {
+
+ Request request = httpClient.newRequest(uri());
+ ReactiveRequest reactiveRequest = ReactiveRequest.newBuilder(request)
+ .build();
+ Publisher publisher = reactiveRequest.response();
+
+ ReactiveResponse response = Mono.from(publisher)
+ .block();
+
+ Assert.assertNotNull(response);
+ Assert.assertEquals(response.getStatus(), HttpStatus.OK_200);
+
+ }
+}
diff --git a/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ReactiveStreamsUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ReactiveStreamsUnitTest.java
new file mode 100644
index 0000000000..3db4553c86
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/ReactiveStreamsUnitTest.java
@@ -0,0 +1,28 @@
+package com.baeldung.jetty.httpclient;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.reactive.client.ReactiveRequest;
+import org.eclipse.jetty.reactive.client.ReactiveResponse;
+import org.junit.Assert;
+import org.junit.Test;
+import org.reactivestreams.Publisher;
+
+public class ReactiveStreamsUnitTest extends AbstractUnitTest {
+
+ @Test
+ public void givenReactiveClient_whenRequested_shouldReturn200() throws Exception {
+
+ Request request = httpClient.newRequest(uri());
+ ReactiveRequest reactiveRequest = ReactiveRequest.newBuilder(request)
+ .build();
+ Publisher publisher = reactiveRequest.response();
+
+ BlockingSubscriber subscriber = new BlockingSubscriber();
+ publisher.subscribe(subscriber);
+ ReactiveResponse response = subscriber.block();
+ Assert.assertNotNull(response);
+ Assert.assertEquals(response.getStatus(), HttpStatus.OK_200);
+ }
+
+}
diff --git a/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/RxJava2UnitTest.java b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/RxJava2UnitTest.java
new file mode 100644
index 0000000000..dabd768702
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/RxJava2UnitTest.java
@@ -0,0 +1,67 @@
+package com.baeldung.jetty.httpclient;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.reactive.client.ReactiveRequest;
+import org.eclipse.jetty.reactive.client.ReactiveRequest.Event.Type;
+import org.eclipse.jetty.reactive.client.ReactiveResponse;
+import org.junit.Assert;
+import org.junit.Test;
+import org.reactivestreams.Publisher;
+import org.springframework.http.MediaType;
+
+import io.reactivex.Flowable;
+import io.reactivex.Single;
+
+public class RxJava2UnitTest extends AbstractUnitTest {
+
+ @Test
+ public void givenReactiveClient_whenRequestedWithBody_ShouldReturnBody() throws Exception {
+
+ Request request = httpClient.newRequest(uri());
+ ReactiveRequest reactiveRequest = ReactiveRequest.newBuilder(request)
+ .content(ReactiveRequest.Content.fromString(CONTENT, MediaType.TEXT_PLAIN_VALUE, UTF_8))
+ .build();
+ Publisher publisher = reactiveRequest.response(ReactiveResponse.Content.asString());
+
+ String responseContent = Single.fromPublisher(publisher)
+ .blockingGet();
+
+ Assert.assertEquals(CONTENT, responseContent);
+ }
+
+ @Test
+ public void givenReactiveClient_whenRequested_ShouldPrintEvents() throws Exception {
+ ReactiveRequest request = ReactiveRequest.newBuilder(httpClient, uri())
+ .content(ReactiveRequest.Content.fromString(CONTENT, MediaType.TEXT_PLAIN_VALUE, UTF_8))
+ .build();
+ Publisher requestEvents = request.requestEvents();
+ Publisher responseEvents = request.responseEvents();
+
+ List requestEventTypes = new ArrayList<>();
+ List responseEventTypes = new ArrayList<>();
+
+ Flowable.fromPublisher(requestEvents)
+ .map(ReactiveRequest.Event::getType)
+ .subscribe(requestEventTypes::add);
+
+ Flowable.fromPublisher(responseEvents)
+ .map(ReactiveResponse.Event::getType)
+ .subscribe(responseEventTypes::add);
+
+ Single response = Single.fromPublisher(request.response());
+ int actualStatus = response.blockingGet()
+ .getStatus();
+
+ Assert.assertEquals(6, requestEventTypes.size());
+ Assert.assertEquals(5, responseEventTypes.size());
+
+ Assert.assertEquals(actualStatus, HttpStatus.OK_200);
+ }
+
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/SpringWebFluxUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/SpringWebFluxUnitTest.java
new file mode 100644
index 0000000000..4a1a9bb2b5
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/jetty/httpclient/SpringWebFluxUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.jetty.httpclient;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.reactive.ClientHttpConnector;
+import org.springframework.http.client.reactive.JettyClientHttpConnector;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import reactor.core.publisher.Mono;
+
+public class SpringWebFluxUnitTest extends AbstractUnitTest {
+
+ @Test
+ public void givenReactiveClient_whenRequested_shouldReturnResponse() throws Exception {
+
+ HttpClient httpClient = new HttpClient();
+ httpClient.start();
+
+ ClientHttpConnector clientConnector = new JettyClientHttpConnector(httpClient);
+ WebClient client = WebClient.builder()
+ .clientConnector(clientConnector)
+ .build();
+ String responseContent = client.post()
+ .uri(uri())
+ .contentType(MediaType.TEXT_PLAIN)
+ .body(BodyInserters.fromPublisher(Mono.just(CONTENT), String.class))
+ .retrieve()
+ .bodyToMono(String.class)
+ .block();
+ Assert.assertNotNull(responseContent);
+ Assert.assertEquals(CONTENT, responseContent);
+ }
+}
\ No newline at end of file
diff --git a/libraries-testing/pom.xml b/libraries-testing/pom.xml
index ad6c81a3d6..89cb0bd494 100644
--- a/libraries-testing/pom.xml
+++ b/libraries-testing/pom.xml
@@ -195,7 +195,7 @@
0.8.1
4.3.8.RELEASE
4.1.1
- 3.6.2
+ 3.14.0
2.0.0.0
1.4.200
2.7.0
diff --git a/maven-java-11/multimodule-maven-project/entitymodule/pom.xml b/maven-java-11/multimodule-maven-project/entitymodule/pom.xml
new file mode 100644
index 0000000000..228619ed74
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/entitymodule/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+ com.baeldung.entitymodule
+ entitymodule
+ 1.0
+ entitymodule
+ jar
+
+
+ com.baeldung.multimodule-maven-project
+ multimodule-maven-project
+ 1.0
+
+
+
+ 11
+ 11
+
+
+
diff --git a/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/com/baeldung/entity/User.java b/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/com/baeldung/entity/User.java
new file mode 100644
index 0000000000..22022a2e6d
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/com/baeldung/entity/User.java
@@ -0,0 +1,19 @@
+package com.baeldung.entity;
+
+public class User {
+
+ private final String name;
+
+ public User(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" + "name=" + name + '}';
+ }
+}
diff --git a/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/module-info.java b/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/module-info.java
new file mode 100644
index 0000000000..67a3097352
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/entitymodule/src/main/java/module-info.java
@@ -0,0 +1,3 @@
+module com.baeldung.entity {
+ exports com.baeldung.entity;
+}
diff --git a/maven-java-11/multimodule-maven-project/mainappmodule/pom.xml b/maven-java-11/multimodule-maven-project/mainappmodule/pom.xml
new file mode 100644
index 0000000000..1f4493c34c
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/mainappmodule/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+ com.baeldung.mainappmodule
+ mainappmodule
+ 1.0
+ mainappmodule
+ jar
+
+
+ com.baeldung.multimodule-maven-project
+ multimodule-maven-project
+ 1.0
+
+
+
+
+ com.baeldung.entitymodule
+ entitymodule
+ ${entitymodule.version}
+
+
+ com.baeldung.daomodule
+ daomodule
+ ${daomodule.version}
+
+
+ com.baeldung.userdaomodule
+ userdaomodule
+ ${userdaomodule.version}
+
+
+
+
+ 1.0
+ 1.0
+ 1.0
+
+
+
diff --git a/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/com/baeldung/mainapp/Application.java b/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/com/baeldung/mainapp/Application.java
new file mode 100644
index 0000000000..0c0df7461b
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/com/baeldung/mainapp/Application.java
@@ -0,0 +1,19 @@
+package com.baeldung.mainapp;
+
+import com.baeldung.dao.Dao;
+import com.baeldung.entity.User;
+import com.baeldung.userdao.UserDao;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Application {
+
+ public static void main(String[] args) {
+ Map users = new HashMap<>();
+ users.put(1, new User("Julie"));
+ users.put(2, new User("David"));
+ Dao userDao = new UserDao(users);
+ userDao.findAll().forEach(System.out::println);
+ }
+
+}
diff --git a/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/module-info.java b/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/module-info.java
new file mode 100644
index 0000000000..c688fcf7de
--- /dev/null
+++ b/maven-java-11/multimodule-maven-project/mainappmodule/src/main/java/module-info.java
@@ -0,0 +1,6 @@
+module com.baeldung.mainapp {
+ requires com.baeldung.entity;
+ requires com.baeldung.userdao;
+ requires com.baeldung.dao;
+ uses com.baeldung.dao.Dao;
+}
diff --git a/patterns/cqrs-es/README.md b/patterns/cqrs-es/README.md
new file mode 100644
index 0000000000..92570280ab
--- /dev/null
+++ b/patterns/cqrs-es/README.md
@@ -0,0 +1,5 @@
+This module contains articles about composing together CQRS and Event Sourcing
+
+## Relevant Articles
+
+- [CQRS and Event Sourcing in Java](https://www.baeldung.com/cqrs-event-sourcing-java)
diff --git a/patterns/cqrs-es/pom.xml b/patterns/cqrs-es/pom.xml
new file mode 100644
index 0000000000..3c54038837
--- /dev/null
+++ b/patterns/cqrs-es/pom.xml
@@ -0,0 +1,30 @@
+
+ 4.0.0
+ cqrs-es
+ 1.0-SNAPSHOT
+ cqrs-es
+
+ com.baeldung
+ patterns
+ 1.0.0-SNAPSHOT
+
+
+ 1.8
+ 1.8
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.12
+
+
+ junit
+ junit
+ 4.13
+ test
+
+
+
\ No newline at end of file
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/aggregates/UserAggregate.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/aggregates/UserAggregate.java
new file mode 100644
index 0000000000..96476dc15a
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/aggregates/UserAggregate.java
@@ -0,0 +1,30 @@
+package com.baeldung.patterns.cqrs.aggregates;
+
+import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
+import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
+import com.baeldung.patterns.cqrs.repository.UserWriteRepository;
+import com.baeldung.patterns.domain.User;
+
+public class UserAggregate {
+
+ private UserWriteRepository writeRepository;
+
+ public UserAggregate(UserWriteRepository repository) {
+ this.writeRepository = repository;
+ }
+
+ public User handleCreateUserCommand(CreateUserCommand command) {
+ User user = new User(command.getUserId(), command.getFirstName(), command.getLastName());
+ writeRepository.addUser(user.getUserid(), user);
+ return user;
+ }
+
+ public User handleUpdateUserCommand(UpdateUserCommand command) {
+ User user = writeRepository.getUser(command.getUserId());
+ user.setAddresses(command.getAddresses());
+ user.setContacts(command.getContacts());
+ writeRepository.addUser(user.getUserid(), user);
+ return user;
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/CreateUserCommand.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/CreateUserCommand.java
new file mode 100644
index 0000000000..362804c610
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/CreateUserCommand.java
@@ -0,0 +1,14 @@
+package com.baeldung.patterns.cqrs.commands;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class CreateUserCommand {
+
+ private String userId;
+ private String firstName;
+ private String lastName;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/UpdateUserCommand.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/UpdateUserCommand.java
new file mode 100644
index 0000000000..58a4889458
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/commands/UpdateUserCommand.java
@@ -0,0 +1,20 @@
+package com.baeldung.patterns.cqrs.commands;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class UpdateUserCommand {
+
+ private String userId;
+ private Set addresses = new HashSet<>();
+ private Set contacts = new HashSet<>();
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projections/UserProjection.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projections/UserProjection.java
new file mode 100644
index 0000000000..40a56e727f
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projections/UserProjection.java
@@ -0,0 +1,37 @@
+package com.baeldung.patterns.cqrs.projections;
+
+import java.util.Set;
+
+import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
+import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
+import com.baeldung.patterns.cqrs.repository.UserReadRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.UserAddress;
+import com.baeldung.patterns.domain.UserContact;
+
+public class UserProjection {
+
+ private UserReadRepository repository;
+
+ public UserProjection(UserReadRepository repository) {
+ this.repository = repository;
+ }
+
+ public Set handle(ContactByTypeQuery query) throws Exception {
+ UserContact userContact = repository.getUserContact(query.getUserId());
+ if (userContact == null)
+ throw new Exception("User does not exist.");
+ return userContact.getContactByType()
+ .get(query.getContactType());
+ }
+
+ public Set handle(AddressByRegionQuery query) throws Exception {
+ UserAddress userAddress = repository.getUserAddress(query.getUserId());
+ if (userAddress == null)
+ throw new Exception("User does not exist.");
+ return userAddress.getAddressByRegion()
+ .get(query.getState());
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projectors/UserProjector.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projectors/UserProjector.java
new file mode 100644
index 0000000000..0344c352d9
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/projectors/UserProjector.java
@@ -0,0 +1,49 @@
+package com.baeldung.patterns.cqrs.projectors;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.baeldung.patterns.cqrs.repository.UserReadRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+import com.baeldung.patterns.domain.UserAddress;
+import com.baeldung.patterns.domain.UserContact;
+
+public class UserProjector {
+
+ UserReadRepository readRepository = new UserReadRepository();
+
+ public UserProjector(UserReadRepository readRepository) {
+ this.readRepository = readRepository;
+ }
+
+ public void project(User user) {
+ UserContact userContact = Optional.ofNullable(readRepository.getUserContact(user.getUserid()))
+ .orElse(new UserContact());
+ Map> contactByType = new HashMap<>();
+ for (Contact contact : user.getContacts()) {
+ Set contacts = Optional.ofNullable(contactByType.get(contact.getType()))
+ .orElse(new HashSet<>());
+ contacts.add(contact);
+ contactByType.put(contact.getType(), contacts);
+ }
+ userContact.setContactByType(contactByType);
+ readRepository.addUserContact(user.getUserid(), userContact);
+
+ UserAddress userAddress = Optional.ofNullable(readRepository.getUserAddress(user.getUserid()))
+ .orElse(new UserAddress());
+ Map> addressByRegion = new HashMap<>();
+ for (Address address : user.getAddresses()) {
+ Set addresses = Optional.ofNullable(addressByRegion.get(address.getState()))
+ .orElse(new HashSet<>());
+ addresses.add(address);
+ addressByRegion.put(address.getState(), addresses);
+ }
+ userAddress.setAddressByRegion(addressByRegion);
+ readRepository.addUserAddress(user.getUserid(), userAddress);
+ }
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/AddressByRegionQuery.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/AddressByRegionQuery.java
new file mode 100644
index 0000000000..4a0f0769f2
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/AddressByRegionQuery.java
@@ -0,0 +1,12 @@
+package com.baeldung.patterns.cqrs.queries;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class AddressByRegionQuery {
+
+ private String userId;
+ private String state;
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/ContactByTypeQuery.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/ContactByTypeQuery.java
new file mode 100644
index 0000000000..b6c271b472
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/queries/ContactByTypeQuery.java
@@ -0,0 +1,12 @@
+package com.baeldung.patterns.cqrs.queries;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class ContactByTypeQuery {
+
+ private String userId;
+ private String contactType;
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserReadRepository.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserReadRepository.java
new file mode 100644
index 0000000000..a7ef2f6f96
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserReadRepository.java
@@ -0,0 +1,31 @@
+package com.baeldung.patterns.cqrs.repository;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.baeldung.patterns.domain.UserAddress;
+import com.baeldung.patterns.domain.UserContact;
+
+public class UserReadRepository {
+
+ private Map userAddress = new HashMap<>();
+
+ private Map userContact = new HashMap<>();
+
+ public void addUserAddress(String id, UserAddress user) {
+ userAddress.put(id, user);
+ }
+
+ public UserAddress getUserAddress(String id) {
+ return userAddress.get(id);
+ }
+
+ public void addUserContact(String id, UserContact user) {
+ userContact.put(id, user);
+ }
+
+ public UserContact getUserContact(String id) {
+ return userContact.get(id);
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserWriteRepository.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserWriteRepository.java
new file mode 100644
index 0000000000..8636e36225
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/cqrs/repository/UserWriteRepository.java
@@ -0,0 +1,20 @@
+package com.baeldung.patterns.cqrs.repository;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.baeldung.patterns.domain.User;
+
+public class UserWriteRepository {
+
+ private Map store = new HashMap<>();
+
+ public void addUser(String id, User user) {
+ store.put(id, user);
+ }
+
+ public User getUser(String id) {
+ return store.get(id);
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/repository/UserRepository.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/repository/UserRepository.java
new file mode 100644
index 0000000000..b22d40e6e0
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/repository/UserRepository.java
@@ -0,0 +1,20 @@
+package com.baeldung.patterns.crud.repository;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.baeldung.patterns.domain.User;
+
+public class UserRepository {
+
+ private Map store = new HashMap<>();
+
+ public void addUser(String id, User user) {
+ store.put(id, user);
+ }
+
+ public User getUser(String id) {
+ return store.get(id);
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/service/UserService.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/service/UserService.java
new file mode 100644
index 0000000000..d21a796304
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/crud/service/UserService.java
@@ -0,0 +1,55 @@
+package com.baeldung.patterns.crud.service;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.baeldung.patterns.crud.repository.UserRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+
+public class UserService {
+
+ private UserRepository repository;
+
+ public UserService(UserRepository repository) {
+ this.repository = repository;
+ }
+
+ public void createUser(String userId, String firstName, String lastName) {
+ User user = new User(userId, firstName, lastName);
+ repository.addUser(userId, user);
+ }
+
+ public void updateUser(String userId, Set contacts, Set addresses) throws Exception {
+ User user = repository.getUser(userId);
+ if (user == null)
+ throw new Exception("User does not exist.");
+ user.setContacts(contacts);
+ user.setAddresses(addresses);
+ repository.addUser(userId, user);
+ }
+
+ public Set getContactByType(String userId, String contactType) throws Exception {
+ User user = repository.getUser(userId);
+ if (user == null)
+ throw new Exception("User does not exit.");
+ Set contacts = user.getContacts();
+ return contacts.stream()
+ .filter(c -> c.getType()
+ .equals(contactType))
+ .collect(Collectors.toSet());
+ }
+
+ public Set getAddressByRegion(String userId, String state) throws Exception {
+ User user = repository.getUser(userId);
+ if (user == null)
+ throw new Exception("User does not exist.");
+ Set addresses = user.getAddresses();
+ return addresses.stream()
+ .filter(a -> a.getState()
+ .equals(state))
+ .collect(Collectors.toSet());
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Address.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Address.java
new file mode 100644
index 0000000000..dfaa724434
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Address.java
@@ -0,0 +1,14 @@
+package com.baeldung.patterns.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Address {
+
+ private String city;
+ private String state;
+ private String postcode;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Contact.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Contact.java
new file mode 100644
index 0000000000..a151cdb4ff
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/Contact.java
@@ -0,0 +1,13 @@
+package com.baeldung.patterns.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Contact {
+
+ private String type;
+ private String detail;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/User.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/User.java
new file mode 100644
index 0000000000..8f59723894
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/User.java
@@ -0,0 +1,22 @@
+package com.baeldung.patterns.domain;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import lombok.Data;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+@Data
+@RequiredArgsConstructor
+public class User {
+ @NonNull
+ private String userid;
+ @NonNull
+ private String firstname;
+ @NonNull
+ private String lastname;
+ private Set contacts = new HashSet<>();
+ private Set addresses = new HashSet<>();
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserAddress.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserAddress.java
new file mode 100644
index 0000000000..8105a53265
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserAddress.java
@@ -0,0 +1,14 @@
+package com.baeldung.patterns.domain;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import lombok.Data;
+
+@Data
+public class UserAddress {
+
+ private Map> addressByRegion = new HashMap<>();
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserContact.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserContact.java
new file mode 100644
index 0000000000..1768dadc39
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/domain/UserContact.java
@@ -0,0 +1,14 @@
+package com.baeldung.patterns.domain;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import lombok.Data;
+
+@Data
+public class UserContact {
+
+ private Map> contactByType = new HashMap<>();
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/Event.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/Event.java
new file mode 100644
index 0000000000..4718450cac
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/Event.java
@@ -0,0 +1,15 @@
+package com.baeldung.patterns.es.events;
+
+import java.util.Date;
+import java.util.UUID;
+
+import lombok.ToString;
+
+@ToString
+public abstract class Event {
+
+ public final UUID id = UUID.randomUUID();
+
+ public final Date created = new Date();
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressAddedEvent.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressAddedEvent.java
new file mode 100644
index 0000000000..caa640798b
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressAddedEvent.java
@@ -0,0 +1,16 @@
+package com.baeldung.patterns.es.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class UserAddressAddedEvent extends Event {
+
+ private String city;
+ private String state;
+ private String postCode;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressRemovedEvent.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressRemovedEvent.java
new file mode 100644
index 0000000000..f0702d8810
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserAddressRemovedEvent.java
@@ -0,0 +1,16 @@
+package com.baeldung.patterns.es.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class UserAddressRemovedEvent extends Event {
+
+ private String city;
+ private String state;
+ private String postCode;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactAddedEvent.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactAddedEvent.java
new file mode 100644
index 0000000000..0bce2f997e
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactAddedEvent.java
@@ -0,0 +1,15 @@
+package com.baeldung.patterns.es.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class UserContactAddedEvent extends Event {
+
+ private String contactType;
+ private String contactDetails;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactRemovedEvent.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactRemovedEvent.java
new file mode 100644
index 0000000000..9c043971e7
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserContactRemovedEvent.java
@@ -0,0 +1,15 @@
+package com.baeldung.patterns.es.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class UserContactRemovedEvent extends Event {
+
+ private String contactType;
+ private String contactDetails;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserCreatedEvent.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserCreatedEvent.java
new file mode 100644
index 0000000000..8e725cd667
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/events/UserCreatedEvent.java
@@ -0,0 +1,16 @@
+package com.baeldung.patterns.es.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class UserCreatedEvent extends Event {
+
+ private String userId;
+ private String firstName;
+ private String lastName;
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/repository/EventStore.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/repository/EventStore.java
new file mode 100644
index 0000000000..87914d8c12
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/repository/EventStore.java
@@ -0,0 +1,29 @@
+package com.baeldung.patterns.es.repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baeldung.patterns.es.events.Event;
+
+public class EventStore {
+
+ private Map> store = new HashMap<>();
+
+ public void addEvent(String id, Event event) {
+ List events = store.get(id);
+ if (events == null) {
+ events = new ArrayList();
+ events.add(event);
+ store.put(id, events);
+ } else {
+ events.add(event);
+ }
+ }
+
+ public List getEvents(String id) {
+ return store.get(id);
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserService.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserService.java
new file mode 100644
index 0000000000..9650c4294f
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserService.java
@@ -0,0 +1,72 @@
+package com.baeldung.patterns.es.service;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+import com.baeldung.patterns.es.events.UserAddressAddedEvent;
+import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
+import com.baeldung.patterns.es.events.UserContactAddedEvent;
+import com.baeldung.patterns.es.events.UserContactRemovedEvent;
+import com.baeldung.patterns.es.events.UserCreatedEvent;
+import com.baeldung.patterns.es.repository.EventStore;
+
+public class UserService {
+
+ private EventStore repository;
+
+ public UserService(EventStore repository) {
+ this.repository = repository;
+ }
+
+ public void createUser(String userId, String firstName, String lastName) {
+ repository.addEvent(userId, new UserCreatedEvent(userId, firstName, lastName));
+ }
+
+ public void updateUser(String userId, Set contacts, Set addresses) throws Exception {
+ User user = UserUtility.recreateUserState(repository, userId);
+ if (user == null)
+ throw new Exception("User does not exist.");
+
+ user.getContacts()
+ .stream()
+ .filter(c -> !contacts.contains(c))
+ .forEach(c -> repository.addEvent(userId, new UserContactRemovedEvent(c.getType(), c.getDetail())));
+ contacts.stream()
+ .filter(c -> !user.getContacts()
+ .contains(c))
+ .forEach(c -> repository.addEvent(userId, new UserContactAddedEvent(c.getType(), c.getDetail())));
+ user.getAddresses()
+ .stream()
+ .filter(a -> !addresses.contains(a))
+ .forEach(a -> repository.addEvent(userId, new UserAddressRemovedEvent(a.getCity(), a.getState(), a.getPostcode())));
+ addresses.stream()
+ .filter(a -> !user.getAddresses()
+ .contains(a))
+ .forEach(a -> repository.addEvent(userId, new UserAddressAddedEvent(a.getCity(), a.getState(), a.getPostcode())));
+ }
+
+ public Set getContactByType(String userId, String contactType) throws Exception {
+ User user = UserUtility.recreateUserState(repository, userId);
+ if (user == null)
+ throw new Exception("User does not exist.");
+ return user.getContacts()
+ .stream()
+ .filter(c -> c.getType()
+ .equals(contactType))
+ .collect(Collectors.toSet());
+ }
+
+ public Set getAddressByRegion(String userId, String state) throws Exception {
+ User user = UserUtility.recreateUserState(repository, userId);
+ if (user == null)
+ throw new Exception("User does not exist.");
+ return user.getAddresses()
+ .stream()
+ .filter(a -> a.getState()
+ .equals(state))
+ .collect(Collectors.toSet());
+ }
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserUtility.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserUtility.java
new file mode 100644
index 0000000000..e44e404588
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/es/service/UserUtility.java
@@ -0,0 +1,62 @@
+package com.baeldung.patterns.es.service;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+import com.baeldung.patterns.es.events.Event;
+import com.baeldung.patterns.es.events.UserAddressAddedEvent;
+import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
+import com.baeldung.patterns.es.events.UserContactAddedEvent;
+import com.baeldung.patterns.es.events.UserContactRemovedEvent;
+import com.baeldung.patterns.es.events.UserCreatedEvent;
+import com.baeldung.patterns.es.repository.EventStore;
+
+public class UserUtility {
+
+ public static User recreateUserState(EventStore store, String userId) {
+ User user = null;
+
+ List events = store.getEvents(userId);
+ for (Event event : events) {
+ if (event instanceof UserCreatedEvent) {
+ UserCreatedEvent e = (UserCreatedEvent) event;
+ user = new User(UUID.randomUUID()
+ .toString(), e.getFirstName(), e.getLastName());
+ }
+ if (event instanceof UserAddressAddedEvent) {
+ UserAddressAddedEvent e = (UserAddressAddedEvent) event;
+ Address address = new Address(e.getCity(), e.getState(), e.getPostCode());
+ if (user != null)
+ user.getAddresses()
+ .add(address);
+ }
+ if (event instanceof UserAddressRemovedEvent) {
+ UserAddressRemovedEvent e = (UserAddressRemovedEvent) event;
+ Address address = new Address(e.getCity(), e.getState(), e.getPostCode());
+ if (user != null)
+ user.getAddresses()
+ .remove(address);
+ }
+ if (event instanceof UserContactAddedEvent) {
+ UserContactAddedEvent e = (UserContactAddedEvent) event;
+ Contact contact = new Contact(e.getContactType(), e.getContactDetails());
+ if (user != null)
+ user.getContacts()
+ .add(contact);
+ }
+ if (event instanceof UserContactRemovedEvent) {
+ UserContactRemovedEvent e = (UserContactRemovedEvent) event;
+ Contact contact = new Contact(e.getContactType(), e.getContactDetails());
+ if (user != null)
+ user.getContacts()
+ .remove(contact);
+ }
+ }
+
+ return user;
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/aggregates/UserAggregate.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/aggregates/UserAggregate.java
new file mode 100644
index 0000000000..04bfeb634d
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/aggregates/UserAggregate.java
@@ -0,0 +1,87 @@
+package com.baeldung.patterns.escqrs.aggregates;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
+import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+import com.baeldung.patterns.es.events.Event;
+import com.baeldung.patterns.es.events.UserAddressAddedEvent;
+import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
+import com.baeldung.patterns.es.events.UserContactAddedEvent;
+import com.baeldung.patterns.es.events.UserContactRemovedEvent;
+import com.baeldung.patterns.es.events.UserCreatedEvent;
+import com.baeldung.patterns.es.repository.EventStore;
+import com.baeldung.patterns.es.service.UserUtility;
+
+public class UserAggregate {
+
+ private EventStore writeRepository;
+
+ public UserAggregate(EventStore repository) {
+ this.writeRepository = repository;
+ }
+
+ public List handleCreateUserCommand(CreateUserCommand command) {
+ UserCreatedEvent event = new UserCreatedEvent(command.getUserId(), command.getFirstName(), command.getLastName());
+ writeRepository.addEvent(command.getUserId(), event);
+ return Arrays.asList(event);
+ }
+
+ public List handleUpdateUserCommand(UpdateUserCommand command) {
+ User user = UserUtility.recreateUserState(writeRepository, command.getUserId());
+ List events = new ArrayList<>();
+
+ List contactsToRemove = user.getContacts()
+ .stream()
+ .filter(c -> !command.getContacts()
+ .contains(c))
+ .collect(Collectors.toList());
+ for (Contact contact : contactsToRemove) {
+ UserContactRemovedEvent contactRemovedEvent = new UserContactRemovedEvent(contact.getType(), contact.getDetail());
+ events.add(contactRemovedEvent);
+ writeRepository.addEvent(command.getUserId(), contactRemovedEvent);
+ }
+
+ List contactsToAdd = command.getContacts()
+ .stream()
+ .filter(c -> !user.getContacts()
+ .contains(c))
+ .collect(Collectors.toList());
+ for (Contact contact : contactsToAdd) {
+ UserContactAddedEvent contactAddedEvent = new UserContactAddedEvent(contact.getType(), contact.getDetail());
+ events.add(contactAddedEvent);
+ writeRepository.addEvent(command.getUserId(), contactAddedEvent);
+ }
+
+ List addressesToRemove = user.getAddresses()
+ .stream()
+ .filter(a -> !command.getAddresses()
+ .contains(a))
+ .collect(Collectors.toList());
+ for (Address address : addressesToRemove) {
+ UserAddressRemovedEvent addressRemovedEvent = new UserAddressRemovedEvent(address.getCity(), address.getState(), address.getPostcode());
+ events.add(addressRemovedEvent);
+ writeRepository.addEvent(command.getUserId(), addressRemovedEvent);
+ }
+
+ List addressesToAdd = command.getAddresses()
+ .stream()
+ .filter(a -> !user.getAddresses()
+ .contains(a))
+ .collect(Collectors.toList());
+ for (Address address : addressesToAdd) {
+ UserAddressAddedEvent addressAddedEvent = new UserAddressAddedEvent(address.getCity(), address.getState(), address.getPostcode());
+ events.add(addressAddedEvent);
+ writeRepository.addEvent(command.getUserId(), addressAddedEvent);
+ }
+
+ return events;
+ }
+
+}
diff --git a/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/projectors/UserProjector.java b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/projectors/UserProjector.java
new file mode 100644
index 0000000000..d388abe7cb
--- /dev/null
+++ b/patterns/cqrs-es/src/main/java/com/baeldung/patterns/escqrs/projectors/UserProjector.java
@@ -0,0 +1,91 @@
+package com.baeldung.patterns.escqrs.projectors;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import com.baeldung.patterns.cqrs.repository.UserReadRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.UserAddress;
+import com.baeldung.patterns.domain.UserContact;
+import com.baeldung.patterns.es.events.Event;
+import com.baeldung.patterns.es.events.UserAddressAddedEvent;
+import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
+import com.baeldung.patterns.es.events.UserContactAddedEvent;
+import com.baeldung.patterns.es.events.UserContactRemovedEvent;
+
+public class UserProjector {
+
+ UserReadRepository readRepository = new UserReadRepository();
+
+ public UserProjector(UserReadRepository readRepository) {
+ this.readRepository = readRepository;
+ }
+
+ public void project(String userId, List events) {
+
+ for (Event event : events) {
+ if (event instanceof UserAddressAddedEvent)
+ apply(userId, (UserAddressAddedEvent) event);
+ if (event instanceof UserAddressRemovedEvent)
+ apply(userId, (UserAddressRemovedEvent) event);
+ if (event instanceof UserContactAddedEvent)
+ apply(userId, (UserContactAddedEvent) event);
+ if (event instanceof UserContactRemovedEvent)
+ apply(userId, (UserContactRemovedEvent) event);
+ }
+
+ }
+
+ public void apply(String userId, UserAddressAddedEvent event) {
+ Address address = new Address(event.getCity(), event.getState(), event.getPostCode());
+ UserAddress userAddress = Optional.ofNullable(readRepository.getUserAddress(userId))
+ .orElse(new UserAddress());
+ Set addresses = Optional.ofNullable(userAddress.getAddressByRegion()
+ .get(address.getState()))
+ .orElse(new HashSet<>());
+ addresses.add(address);
+ userAddress.getAddressByRegion()
+ .put(address.getState(), addresses);
+ readRepository.addUserAddress(userId, userAddress);
+ }
+
+ public void apply(String userId, UserAddressRemovedEvent event) {
+ Address address = new Address(event.getCity(), event.getState(), event.getPostCode());
+ UserAddress userAddress = readRepository.getUserAddress(userId);
+ if (userAddress != null) {
+ Set addresses = userAddress.getAddressByRegion()
+ .get(address.getState());
+ if (addresses != null)
+ addresses.remove(address);
+ readRepository.addUserAddress(userId, userAddress);
+ }
+ }
+
+ public void apply(String userId, UserContactAddedEvent event) {
+ Contact contact = new Contact(event.getContactType(), event.getContactDetails());
+ UserContact userContact = Optional.ofNullable(readRepository.getUserContact(userId))
+ .orElse(new UserContact());
+ Set contacts = Optional.ofNullable(userContact.getContactByType()
+ .get(contact.getType()))
+ .orElse(new HashSet<>());
+ contacts.add(contact);
+ userContact.getContactByType()
+ .put(contact.getType(), contacts);
+ readRepository.addUserContact(userId, userContact);
+ }
+
+ public void apply(String userId, UserContactRemovedEvent event) {
+ Contact contact = new Contact(event.getContactType(), event.getContactDetails());
+ UserContact userContact = readRepository.getUserContact(userId);
+ if (userContact != null) {
+ Set contacts = userContact.getContactByType()
+ .get(contact.getType());
+ if (contacts != null)
+ contacts.remove(contact);
+ readRepository.addUserContact(userId, userContact);
+ }
+ }
+}
diff --git a/patterns/cqrs-es/src/test/java/com/baeldung/patterns/cqrs/ApplicationUnitTest.java b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/cqrs/ApplicationUnitTest.java
new file mode 100644
index 0000000000..7f68a64274
--- /dev/null
+++ b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/cqrs/ApplicationUnitTest.java
@@ -0,0 +1,74 @@
+package com.baeldung.patterns.cqrs;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baeldung.patterns.cqrs.aggregates.UserAggregate;
+import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
+import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
+import com.baeldung.patterns.cqrs.projections.UserProjection;
+import com.baeldung.patterns.cqrs.projectors.UserProjector;
+import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
+import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
+import com.baeldung.patterns.cqrs.repository.UserReadRepository;
+import com.baeldung.patterns.cqrs.repository.UserWriteRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.domain.User;
+
+public class ApplicationUnitTest {
+
+ private UserWriteRepository writeRepository;
+ private UserReadRepository readRepository;
+ private UserProjector projector;
+ private UserAggregate userAggregate;
+ private UserProjection userProjection;
+
+ @Before
+ public void setUp() {
+ writeRepository = new UserWriteRepository();
+ readRepository = new UserReadRepository();
+ projector = new UserProjector(readRepository);
+ userAggregate = new UserAggregate(writeRepository);
+ userProjection = new UserProjection(readRepository);
+ }
+
+ @Test
+ public void givenCQRSApplication_whenCommandRun_thenQueryShouldReturnResult() throws Exception {
+ String userId = UUID.randomUUID()
+ .toString();
+ User user = null;
+ CreateUserCommand createUserCommand = new CreateUserCommand(userId, "Tom", "Sawyer");
+ user = userAggregate.handleCreateUserCommand(createUserCommand);
+ projector.project(user);
+
+ UpdateUserCommand updateUserCommand = new UpdateUserCommand(user.getUserid(), Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"))
+ .collect(Collectors.toSet()));
+ user = userAggregate.handleUpdateUserCommand(updateUserCommand);
+ projector.project(user);
+
+ updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()));
+ user = userAggregate.handleUpdateUserCommand(updateUserCommand);
+ projector.project(user);
+
+ ContactByTypeQuery contactByTypeQuery = new ContactByTypeQuery(userId, "EMAIL");
+ assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
+ .collect(Collectors.toSet()), userProjection.handle(contactByTypeQuery));
+ AddressByRegionQuery addressByRegionQuery = new AddressByRegionQuery(userId, "NY");
+ assertEquals(Stream.of(new Address("New York", "NY", "10001"))
+ .collect(Collectors.toSet()), userProjection.handle(addressByRegionQuery));
+
+ }
+
+}
diff --git a/patterns/cqrs-es/src/test/java/com/baeldung/patterns/crud/ApplicationUnitTest.java b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/crud/ApplicationUnitTest.java
new file mode 100644
index 0000000000..3fabfe405d
--- /dev/null
+++ b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/crud/ApplicationUnitTest.java
@@ -0,0 +1,48 @@
+package com.baeldung.patterns.crud;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baeldung.patterns.crud.repository.UserRepository;
+import com.baeldung.patterns.crud.service.UserService;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+
+public class ApplicationUnitTest {
+
+ private UserRepository repository;
+
+ @Before
+ public void setUp() {
+ repository = new UserRepository();
+ }
+
+ @Test
+ public void givenCRUDApplication_whenDataCreated_thenDataCanBeFetched() throws Exception {
+ UserService service = new UserService(repository);
+ String userId = UUID.randomUUID()
+ .toString();
+
+ service.createUser(userId, "Tom", "Sawyer");
+ service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()));
+ service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()));
+
+ assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
+ .collect(Collectors.toSet()), service.getContactByType(userId, "EMAIL"));
+ assertEquals(Stream.of(new Address("New York", "NY", "10001"))
+ .collect(Collectors.toSet()), service.getAddressByRegion(userId, "NY"));
+ }
+
+}
diff --git a/patterns/cqrs-es/src/test/java/com/baeldung/patterns/es/ApplicationUnitTest.java b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/es/ApplicationUnitTest.java
new file mode 100644
index 0000000000..61e7b4c05a
--- /dev/null
+++ b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/es/ApplicationUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.patterns.es;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.es.repository.EventStore;
+import com.baeldung.patterns.es.service.UserService;
+
+public class ApplicationUnitTest {
+
+ private EventStore repository;
+ private UserService service;
+
+ @Before
+ public void setUp() {
+ repository = new EventStore();
+ service = new UserService(repository);
+ }
+
+ @Test
+ public void givenCRUDApplication_whenDataCreated_thenDataCanBeFetched() throws Exception {
+ String userId = UUID.randomUUID()
+ .toString();
+
+ service.createUser(userId, "Tom", "Sawyer");
+ service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()));
+ service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()));
+
+ assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
+ .collect(Collectors.toSet()), service.getContactByType(userId, "EMAIL"));
+ assertEquals(Stream.of(new Address("New York", "NY", "10001"))
+ .collect(Collectors.toSet()), service.getAddressByRegion(userId, "NY"));
+
+ }
+
+}
diff --git a/patterns/cqrs-es/src/test/java/com/baeldung/patterns/escqrs/ApplicationUnitTest.java b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/escqrs/ApplicationUnitTest.java
new file mode 100644
index 0000000000..e0460b2f12
--- /dev/null
+++ b/patterns/cqrs-es/src/test/java/com/baeldung/patterns/escqrs/ApplicationUnitTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.patterns.escqrs;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
+import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
+import com.baeldung.patterns.cqrs.projections.UserProjection;
+import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
+import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
+import com.baeldung.patterns.cqrs.repository.UserReadRepository;
+import com.baeldung.patterns.domain.Address;
+import com.baeldung.patterns.domain.Contact;
+import com.baeldung.patterns.es.events.Event;
+import com.baeldung.patterns.es.repository.EventStore;
+import com.baeldung.patterns.escqrs.aggregates.UserAggregate;
+import com.baeldung.patterns.escqrs.projectors.UserProjector;
+
+public class ApplicationUnitTest {
+
+ private EventStore writeRepository;
+ private UserReadRepository readRepository;
+ private UserProjector projector;
+ private UserAggregate userAggregate;
+ private UserProjection userProjection;
+
+ @Before
+ public void setUp() {
+ writeRepository = new EventStore();
+ readRepository = new UserReadRepository();
+ projector = new UserProjector(readRepository);
+ userAggregate = new UserAggregate(writeRepository);
+ userProjection = new UserProjection(readRepository);
+ }
+
+ @Test
+ public void givenCQRSApplication_whenCommandRun_thenQueryShouldReturnResult() throws Exception {
+ String userId = UUID.randomUUID()
+ .toString();
+ List events = null;
+ CreateUserCommand createUserCommand = new CreateUserCommand(userId, "Kumar", "Chandrakant");
+ events = userAggregate.handleCreateUserCommand(createUserCommand);
+
+ projector.project(userId, events);
+
+ UpdateUserCommand updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"))
+ .collect(Collectors.toSet()));
+ events = userAggregate.handleUpdateUserCommand(updateUserCommand);
+ projector.project(userId, events);
+
+ updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
+ .collect(Collectors.toSet()),
+ Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
+ .collect(Collectors.toSet()));
+ events = userAggregate.handleUpdateUserCommand(updateUserCommand);
+ projector.project(userId, events);
+
+ ContactByTypeQuery contactByTypeQuery = new ContactByTypeQuery(userId, "EMAIL");
+ assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
+ .collect(Collectors.toSet()), userProjection.handle(contactByTypeQuery));
+ AddressByRegionQuery addressByRegionQuery = new AddressByRegionQuery(userId, "NY");
+ assertEquals(Stream.of(new Address("New York", "NY", "10001"))
+ .collect(Collectors.toSet()), userProjection.handle(addressByRegionQuery));
+
+ }
+
+}
diff --git a/patterns/pom.xml b/patterns/pom.xml
index 4c17055231..e1753aba56 100644
--- a/patterns/pom.xml
+++ b/patterns/pom.xml
@@ -5,13 +5,11 @@
patterns
patterns
pom
-
com.baeldung
parent-modules
1.0.0-SNAPSHOT
-
design-patterns-architectural
design-patterns-behavioral
@@ -21,11 +19,11 @@
design-patterns-functional
design-patterns-structural
dip
+ cqrs-es
front-controller
intercepting-filter
solid
-
@@ -36,7 +34,6 @@
-
@@ -53,9 +50,7 @@
-
9.4.0.v20161208
-
-
+
\ No newline at end of file
diff --git a/persistence-modules/hibernate-libraries/create-database.sh b/persistence-modules/hibernate-libraries/create-database.sh
new file mode 100755
index 0000000000..da55dc917d
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/create-database.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+docker run \
+ -p 53306:3306 \
+ --name=mysql57-hibernate-types \
+ -e MYSQL_ALLOW_EMPTY_PASSWORD=true \
+ -v "${PWD}/docker/docker-entrypoint-initdb.d":/docker-entrypoint-initdb.d \
+ -d mysql:5.7
diff --git a/persistence-modules/hibernate-libraries/docker-compose.yml b/persistence-modules/hibernate-libraries/docker-compose.yml
new file mode 100644
index 0000000000..3ea9fef2e7
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/docker-compose.yml
@@ -0,0 +1,19 @@
+version: '3.2'
+
+services:
+ mysql:
+ image: mysql:5.7
+ container_name: mysql57
+ restart: unless-stopped
+ ports:
+ - 53306:3306
+ environment:
+ - MYSQL_ALLOW_EMPTY_PASSWORD=true
+ volumes :
+ - ./docker/etc/mysql/conf.d:/etc/mysql/conf.d
+ - ./docker/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "50m"
+ max-file: "1"
diff --git a/persistence-modules/hibernate-libraries/docker/docker-entrypoint-initdb.d/init-db.sql b/persistence-modules/hibernate-libraries/docker/docker-entrypoint-initdb.d/init-db.sql
new file mode 100644
index 0000000000..1df234f5a1
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/docker/docker-entrypoint-initdb.d/init-db.sql
@@ -0,0 +1,3 @@
+CREATE DATABASE hibernate_types;
+use hibernate_types;
+GRANT ALL PRIVILEGES ON hibernate_types.* TO 'mysql'@'%' IDENTIFIED BY 'admin';
diff --git a/persistence-modules/hibernate-libraries/pom.xml b/persistence-modules/hibernate-libraries/pom.xml
new file mode 100644
index 0000000000..ea2dda7e88
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/pom.xml
@@ -0,0 +1,188 @@
+
+
+ 4.0.0
+ hibernate-libraries
+ 0.0.1-SNAPSHOT
+ hibernate-libraries
+ Introduction into hibernate types library
+
+
+ com.baeldung
+ persistence-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ com.vladmihalcea
+ hibernate-types-52
+ ${hibernate-types.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${spring-boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ ${spring-boot.version}
+ runtime
+ true
+
+
+ org.hibernate
+ hibernate-core
+ ${hibernate.version}
+ provided
+
+
+ org.hibernate
+ hibernate-ehcache
+ ${hibernate.version}
+ test
+
+
+ org.hibernate
+ hibernate-testing
+ ${hibernate.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj-core.version}
+ test
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.version}
+ runtime
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+ provided
+ true
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ provided
+ true
+
+
+ javax.xml.bind
+ jaxb-api
+ ${jaxb.version}
+
+
+ org.javassist
+ javassist
+ ${javassist.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+ provided
+ true
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+ provided
+ true
+
+
+ net.ttddyy
+ datasource-proxy
+ ${datasource-proxy.version}
+ test
+
+
+ com.integralblue
+ log4jdbc-spring-boot-starter
+ ${log4jdbc.version}
+
+
+
+
+ hibernate-types
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+ integration
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ none
+
+
+ **/*IntegrationTest.java
+
+
+
+
+
+
+
+
+
+
+
+ 3.15.0
+ 1.6
+ 29.0-jre
+ 2.9.7
+ 5.4.14.Final
+ 2.10.3
+ 1.8
+ 3.27.0-GA
+ 2.3.1
+ 2.0.0
+ 1.2.3
+ 3.0.2
+ 2.22.2
+ 3.8.1
+ 3.8.1
+ 8.0.19
+ 1.7.30
+ 2.1.3.RELEASE
+
+
+
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Album.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Album.java
new file mode 100644
index 0000000000..f18818c3a9
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Album.java
@@ -0,0 +1,34 @@
+package com.baeldung.hibernate.types;
+
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.util.List;
+
+@Entity(name = "Album")
+@Table(name = "album")
+public class Album extends BaseEntity {
+ @Type(type = "json")
+ @Column(columnDefinition = "json")
+ private CoverArt coverArt;
+
+ @OneToMany(fetch = FetchType.EAGER)
+ private List songs;
+
+ public CoverArt getCoverArt() {
+ return coverArt;
+ }
+
+ public void setCoverArt(CoverArt coverArt) {
+ this.coverArt = coverArt;
+ }
+
+
+ public List getSongs() {
+ return songs;
+ }
+
+ public void setSong(List songs) {
+ this.songs = songs;
+ }
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/AlbumRepository.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/AlbumRepository.java
new file mode 100644
index 0000000000..d89542de46
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/AlbumRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.hibernate.types;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AlbumRepository extends CrudRepository {
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Artist.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Artist.java
new file mode 100644
index 0000000000..8f3ccb44c5
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Artist.java
@@ -0,0 +1,72 @@
+package com.baeldung.hibernate.types;
+
+import java.io.Serializable;
+
+public class Artist implements Serializable {
+
+ private String name;
+ private String country;
+ private String genre;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public String getGenre() {
+ return genre;
+ }
+
+ public void setGenre(String genre) {
+ this.genre = genre;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((country == null) ? 0 : country.hashCode());
+ result = prime * result + ((genre == null) ? 0 : genre.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Artist other = (Artist) obj;
+ if (country == null) {
+ if (other.country != null)
+ return false;
+ } else if (!country.equals(other.country))
+ return false;
+ if (genre == null) {
+ if (other.genre != null)
+ return false;
+ } else if (!genre.equals(other.genre))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ }
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/BaseEntity.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/BaseEntity.java
new file mode 100644
index 0000000000..3e0fbc7595
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/BaseEntity.java
@@ -0,0 +1,37 @@
+package com.baeldung.hibernate.types;
+
+import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
+import com.vladmihalcea.hibernate.type.json.JsonStringType;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.TypeDefs;
+
+import javax.persistence.*;
+
+@TypeDefs({
+ @TypeDef(name = "json", typeClass = JsonStringType.class),
+ @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
+})
+@MappedSuperclass
+public class BaseEntity {
+ @Id
+ @GeneratedValue(strategy=GenerationType.AUTO)
+ @Column(name = "id", unique = true, nullable = false)
+ long id;
+ String name;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/CoverArt.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/CoverArt.java
new file mode 100644
index 0000000000..bd71edc53c
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/CoverArt.java
@@ -0,0 +1,71 @@
+package com.baeldung.hibernate.types;
+
+import java.io.Serializable;
+
+public class CoverArt implements Serializable {
+
+ private String frontCoverArtUrl;
+ private String backCoverArtUrl;
+ private String upcCode;
+
+ public String getFrontCoverArtUrl() {
+ return frontCoverArtUrl;
+ }
+
+ public void setFrontCoverArtUrl(String frontCoverArtUrl) {
+ this.frontCoverArtUrl = frontCoverArtUrl;
+ }
+
+ public String getBackCoverArtUrl() {
+ return backCoverArtUrl;
+ }
+
+ public void setBackCoverArtUrl(String backCoverArtUrl) {
+ this.backCoverArtUrl = backCoverArtUrl;
+ }
+
+ public String getUpcCode() {
+ return upcCode;
+ }
+
+ public void setUpcCode(String upcCode) {
+ this.upcCode = upcCode;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((backCoverArtUrl == null) ? 0 : backCoverArtUrl.hashCode());
+ result = prime * result + ((frontCoverArtUrl == null) ? 0 : frontCoverArtUrl.hashCode());
+ result = prime * result + ((upcCode == null) ? 0 : upcCode.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CoverArt other = (CoverArt) obj;
+ if (backCoverArtUrl == null) {
+ if (other.backCoverArtUrl != null)
+ return false;
+ } else if (!backCoverArtUrl.equals(other.backCoverArtUrl))
+ return false;
+ if (frontCoverArtUrl == null) {
+ if (other.frontCoverArtUrl != null)
+ return false;
+ } else if (!frontCoverArtUrl.equals(other.frontCoverArtUrl))
+ return false;
+ if (upcCode == null) {
+ if (other.upcCode != null)
+ return false;
+ } else if (!upcCode.equals(other.upcCode))
+ return false;
+ return true;
+ }
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/HibernateTypesApplication.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/HibernateTypesApplication.java
new file mode 100644
index 0000000000..ac379f9ee2
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/HibernateTypesApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.hibernate.types;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class HibernateTypesApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(HibernateTypesApplication.class, args);
+ }
+
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Song.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Song.java
new file mode 100644
index 0000000000..2d22296024
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/Song.java
@@ -0,0 +1,57 @@
+package com.baeldung.hibernate.types;
+
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+
+import java.time.YearMonth;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.vladmihalcea.hibernate.type.basic.YearMonthIntegerType;
+
+@Entity(name = "Song")
+@Table(name = "song")
+@TypeDef(
+ typeClass = YearMonthIntegerType.class,
+ defaultForType = YearMonth.class
+)
+public class Song extends BaseEntity {
+
+ private Long length = 0L;
+
+ @Type(type = "json")
+ @Column(columnDefinition = "json")
+ private Artist artist;
+
+ @Column(
+ name = "recorded_on",
+ columnDefinition = "mediumint"
+ )
+ private YearMonth recordedOn = YearMonth.now();
+
+ public Long getLength() {
+ return length;
+ }
+
+ public void setLength(Long length) {
+ this.length = length;
+ }
+
+ public Artist getArtist() {
+ return artist;
+ }
+
+ public void setArtist(Artist artist) {
+ this.artist = artist;
+ }
+
+ public YearMonth getRecordedOn() {
+ return recordedOn;
+ }
+
+ public void setRecordedOn(YearMonth recordedOn) {
+ this.recordedOn = recordedOn;
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/SongRepository.java b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/SongRepository.java
new file mode 100644
index 0000000000..e79e88108b
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/java/com/baeldung/hibernate/types/SongRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.hibernate.types;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface SongRepository extends CrudRepository {
+}
diff --git a/persistence-modules/hibernate-libraries/src/main/resources/application.properties b/persistence-modules/hibernate-libraries/src/main/resources/application.properties
new file mode 100644
index 0000000000..9588139eb0
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+log4jdbc.dump.sql.addsemicolon=true
+log4jdbc.dump.sql.maxlinelength=0
+log4jdbc.trim.sql.extrablanklines=false
+logging.level.jdbc.audit=fatal
+logging.level.jdbc.connection=fatal
+logging.level.jdbc.resultset=fatal
+logging.level.jdbc.resultsettable=info
+logging.level.jdbc.sqlonly=fatal
+logging.level.jdbc.sqltiming=info
+spring.datasource.url=jdbc:mysql://localhost:53306/hibernate_types?serverTimezone=UTC&useSSL=false
+spring.datasource.username=root
+spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
+spring.jpa.hibernate.ddl-auto=create
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
diff --git a/persistence-modules/hibernate-libraries/src/main/resources/hibernate-types.properties b/persistence-modules/hibernate-libraries/src/main/resources/hibernate-types.properties
new file mode 100644
index 0000000000..226b50fafd
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/main/resources/hibernate-types.properties
@@ -0,0 +1 @@
+hibernate.types.print.banner=false
diff --git a/persistence-modules/hibernate-libraries/src/test/java/com/baeldung/hibernate/types/HibernateTypesLiveTest.java b/persistence-modules/hibernate-libraries/src/test/java/com/baeldung/hibernate/types/HibernateTypesLiveTest.java
new file mode 100644
index 0000000000..4b551386ad
--- /dev/null
+++ b/persistence-modules/hibernate-libraries/src/test/java/com/baeldung/hibernate/types/HibernateTypesLiveTest.java
@@ -0,0 +1,181 @@
+package com.baeldung.hibernate.types;
+
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.Duration;
+import java.time.YearMonth;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest
+public class HibernateTypesLiveTest {
+
+ @Autowired
+ AlbumRepository albumRepository;
+
+ @Autowired
+ SongRepository songRepository;
+
+ private void deleteAll() {
+ albumRepository.deleteAll();
+ songRepository.deleteAll();
+ }
+
+ @BeforeEach
+ public void setUp() {
+ deleteAll();
+ }
+
+ @BeforeEach
+ public void tearDown() {
+ setUp();
+ }
+
+ @Test
+ void whenSavingHibernateTypes_thenTheCorrectJsonIsStoredInTheDatabase() {
+ Album emptyAlbum = new Album();
+ emptyAlbum = albumRepository.save(emptyAlbum);
+
+ Song emptySong = new Song();
+ emptySong = songRepository.save(emptySong);
+
+ Artist superstarArtist = new Artist();
+ superstarArtist.setCountry("England");
+ superstarArtist.setGenre("Pop");
+ superstarArtist.setName("Superstar");
+
+ Song aHappySong = new Song();
+ aHappySong.setArtist(superstarArtist);
+ aHappySong.setName("A Happy Song");
+ aHappySong.setLength(Duration.ofMinutes(4).getSeconds());
+ aHappySong = songRepository.save(aHappySong);
+
+ Song aSadSong = new Song();
+ aSadSong.setArtist(superstarArtist);
+ aSadSong.setName("A Sad Song");
+ aSadSong.setLength(Duration.ofMinutes(2).getSeconds());
+ aSadSong = songRepository.save(aSadSong);
+
+ Song anotherHappySong = new Song();
+ anotherHappySong.setArtist(superstarArtist);
+ anotherHappySong.setName("Another Happy Song");
+ anotherHappySong.setLength(Duration.ofMinutes(3).getSeconds());
+ anotherHappySong = songRepository.save(anotherHappySong);
+
+ Artist newcomer = new Artist();
+ newcomer.setCountry("Jamaica");
+ newcomer.setGenre("Reggae");
+ newcomer.setName("Newcomer");
+
+ Song aNewSong = new Song();
+ aNewSong.setArtist(newcomer);
+ aNewSong.setName("A New Song");
+ aNewSong.setLength(Duration.ofMinutes(5).getSeconds());
+ aNewSong = songRepository.save(aNewSong);
+
+ CoverArt superstarAlbumCoverArt = new CoverArt();
+ superstarAlbumCoverArt.setUpcCode(UUID.randomUUID().toString());
+ superstarAlbumCoverArt.setFrontCoverArtUrl("http://fakeurl-0");
+ superstarAlbumCoverArt.setBackCoverArtUrl("http://fakeurl-1");
+
+ Album superstarAlbum = new Album();
+ superstarAlbum.setCoverArt(superstarAlbumCoverArt);
+ superstarAlbum.setName("The Superstar Album");
+ superstarAlbum.setSong(Lists.newArrayList(aHappySong, aSadSong, anotherHappySong));
+ superstarAlbum = albumRepository.save(superstarAlbum);
+
+ CoverArt newcomerAlbumCoverArt = new CoverArt();
+ newcomerAlbumCoverArt.setUpcCode(UUID.randomUUID().toString());
+ newcomerAlbumCoverArt.setFrontCoverArtUrl("http://fakeurl-2");
+ newcomerAlbumCoverArt.setBackCoverArtUrl("http://fakeurl-3");
+
+ Album newcomerAlbum = new Album();
+ newcomerAlbum.setCoverArt(newcomerAlbumCoverArt);
+ newcomerAlbum.setName("The Newcomer Album");
+ newcomerAlbum.setSong(Lists.newArrayList(aNewSong));
+ albumRepository.save(newcomerAlbum);
+
+ Iterable selectAlbumsQueryResult = albumRepository.findAll();
+ assertThat(selectAlbumsQueryResult).hasSize(3);
+
+ Iterable selectSongsQueryResult = songRepository.findAll();
+ assertThat(selectSongsQueryResult).hasSize(5);
+
+ Album selectAlbumQueryResult;
+
+ selectAlbumQueryResult = albumRepository.findById(emptyAlbum.getId()).get();
+ assertThat(selectAlbumQueryResult.getName()).isNull();
+ assertThat(selectAlbumQueryResult.getCoverArt()).isNull();
+ assertThat(selectAlbumQueryResult.getSongs()).isNullOrEmpty();
+
+ selectAlbumQueryResult = albumRepository.findById(superstarAlbum.getId()).get();
+ assertThat(selectAlbumQueryResult.getName()).isEqualTo("The Superstar Album");
+ assertThat(selectAlbumQueryResult.getCoverArt().getFrontCoverArtUrl()).isEqualTo("http://fakeurl-0");
+ assertThat(selectAlbumQueryResult.getCoverArt().getBackCoverArtUrl()).isEqualTo("http://fakeurl-1");
+ assertThat(selectAlbumQueryResult.getSongs()).hasSize(3);
+ assertThat(selectAlbumQueryResult.getSongs()).usingFieldByFieldElementComparator().containsExactlyInAnyOrder(aHappySong, aSadSong, anotherHappySong);
+
+ selectAlbumQueryResult = albumRepository.findById(newcomerAlbum.getId()).get();
+ assertThat(selectAlbumQueryResult.getName()).isEqualTo("The Newcomer Album");
+ assertThat(selectAlbumQueryResult.getCoverArt().getFrontCoverArtUrl()).isEqualTo("http://fakeurl-2");
+ assertThat(selectAlbumQueryResult.getCoverArt().getBackCoverArtUrl()).isEqualTo("http://fakeurl-3");
+ assertThat(selectAlbumQueryResult.getSongs()).hasSize(1);
+ assertThat(selectAlbumQueryResult.getSongs()).usingFieldByFieldElementComparator().containsExactlyInAnyOrder(aNewSong);
+
+ Song selectSongQueryResult;
+
+ selectSongQueryResult = songRepository.findById(emptySong.getId()).get();
+ assertThat(selectSongQueryResult.getName()).isNull();
+ assertThat(selectSongQueryResult.getLength()).isZero();
+ assertThat(selectSongQueryResult.getArtist()).isNull();
+
+ selectSongQueryResult = songRepository.findById(aHappySong.getId()).get();
+ assertThat(selectSongQueryResult.getName()).isEqualTo("A Happy Song");
+ assertThat(selectSongQueryResult.getLength()).isEqualTo(Duration.ofMinutes(4).getSeconds());
+ assertThat(selectSongQueryResult.getArtist().getName()).isEqualTo("Superstar");
+ assertThat(selectSongQueryResult.getArtist().getGenre()).isEqualTo("Pop");
+ assertThat(selectSongQueryResult.getArtist().getCountry()).isEqualTo("England");
+
+ selectSongQueryResult = songRepository.findById(aSadSong.getId()).get();
+ assertThat(selectSongQueryResult.getName()).isEqualTo("A Sad Song");
+ assertThat(selectSongQueryResult.getLength()).isEqualTo(Duration.ofMinutes(2).getSeconds());
+ assertThat(selectSongQueryResult.getArtist().getName()).isEqualTo("Superstar");
+ assertThat(selectSongQueryResult.getArtist().getGenre()).isEqualTo("Pop");
+ assertThat(selectSongQueryResult.getArtist().getCountry()).isEqualTo("England");
+
+ selectSongQueryResult = songRepository.findById(anotherHappySong.getId()).get();
+ assertThat(selectSongQueryResult.getName()).isEqualTo("Another Happy Song");
+ assertThat(selectSongQueryResult.getLength()).isEqualTo(Duration.ofMinutes(3).getSeconds());
+ assertThat(selectSongQueryResult.getArtist().getName()).isEqualTo("Superstar");
+ assertThat(selectSongQueryResult.getArtist().getGenre()).isEqualTo("Pop");
+ assertThat(selectSongQueryResult.getArtist().getCountry()).isEqualTo("England");
+
+ selectSongQueryResult = songRepository.findById(aNewSong.getId()).get();
+ assertThat(selectSongQueryResult.getName()).isEqualTo("A New Song");
+ assertThat(selectSongQueryResult.getLength()).isEqualTo(Duration.ofMinutes(5).getSeconds());
+ assertThat(selectSongQueryResult.getArtist().getName()).isEqualTo("Newcomer");
+ assertThat(selectSongQueryResult.getArtist().getGenre()).isEqualTo("Reggae");
+ assertThat(selectSongQueryResult.getArtist().getCountry()).isEqualTo("Jamaica");
+ }
+
+ @Test
+ void whenSavingAHibernateTypeYearMonth_thenTheCorrectValueIsStoredInTheDatabase() {
+ Song mySong = new Song();
+ YearMonth now = YearMonth.of(2019, 12);
+ mySong.setArtist(new Artist());
+ mySong.setName("My Song");
+ mySong.setLength(Duration.ofMinutes(1).getSeconds());
+ mySong.setRecordedOn(now);
+ mySong = songRepository.save(mySong);
+
+ Song selectSongQueryResult;
+ selectSongQueryResult = songRepository.findById(mySong.getId()).get();
+ assertThat(selectSongQueryResult.getRecordedOn().getYear()).isEqualTo(2019);
+ assertThat(selectSongQueryResult.getRecordedOn().getMonthValue()).isEqualTo(12);
+ }
+}
diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml
index 78da896861..a03ba1ec5d 100644
--- a/persistence-modules/pom.xml
+++ b/persistence-modules/pom.xml
@@ -24,6 +24,7 @@
hibernate-mapping
hibernate-ogm
hibernate-annotations
+ hibernate-libraries
hibernate-jpa
hibernate-queries
hibernate-enterprise
diff --git a/persistence-modules/spring-boot-persistence-2/README.md b/persistence-modules/spring-boot-persistence-2/README.md
index 3ea93db4fe..392218d2bf 100644
--- a/persistence-modules/spring-boot-persistence-2/README.md
+++ b/persistence-modules/spring-boot-persistence-2/README.md
@@ -1,4 +1,8 @@
### Relevant Articles:
- [Using JDBI with Spring Boot](https://www.baeldung.com/spring-boot-jdbi)
+- [Configuring a Tomcat Connection Pool in Spring Boot](https://www.baeldung.com/spring-boot-tomcat-connection-pool)
+- [Integrating Spring Boot with HSQLDB](https://www.baeldung.com/spring-boot-hsqldb)
+- [List of In-Memory Databases](https://www.baeldung.com/java-in-memory-databases)
- [Oracle Connection Pooling With Spring](https://www.baeldung.com/spring-oracle-connection-pooling)
+- More articles: [[<-- prev]](../spring-boot-persistence)
diff --git a/persistence-modules/spring-boot-persistence-2/pom.xml b/persistence-modules/spring-boot-persistence-2/pom.xml
index 9f456fa8af..f36d8fc43f 100644
--- a/persistence-modules/spring-boot-persistence-2/pom.xml
+++ b/persistence-modules/spring-boot-persistence-2/pom.xml
@@ -104,6 +104,38 @@
tomcat-jdbc
+
+ mysql
+ mysql-connector-java
+
+
+ org.xerial
+ sqlite-jdbc
+
+
+ org.apache.derby
+ derby
+
+
+ org.hsqldb
+ hsqldb
+
+
+
+ com.oracle.database.jdbc
+ ojdbc8
+ ${oracle-database.version}
+
+
+ com.oracle.database.ha
+ ons
+ ${oracle-database.version}
+
+
+ com.oracle.database.jdbc
+ ucp
+ ${oracle-database.version}
+
@@ -112,23 +144,6 @@
org.springframework.boot
spring-boot-maven-plugin
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- com/baeldung/spring/oracle/pooling/configuration/OracleConfiguration.java
- com/baeldung/spring/oracle/pooling/configuration/OracleUCPConfiguration.java
-
-
- com/baeldung/spring/oracle/pooling/SpringOraclePoolingApplicationOracleLiveTest.java
- com/baeldung/spring/oracle/pooling/SpringOraclePoolingApplicationOracleUCPLiveTest.java
-
-
-
@@ -136,6 +151,7 @@
3.9.1
2.1.8.RELEASE
0.9.5.2
+ 19.6.0.0
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/Application.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/Application.java
similarity index 96%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/Application.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/Application.java
index c5a5c291c6..bde8346482 100644
--- a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/Application.java
+++ b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/Application.java
@@ -1,12 +1,12 @@
-package com.baeldung.springboothsqldb.application;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class Application {
-
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
-}
+package com.baeldung.springboothsqldb.application;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java
similarity index 92%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java
index 8229a1d1c0..045612a86f 100644
--- a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java
+++ b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/controllers/CustomerController.java
@@ -1,33 +1,32 @@
-package com.baeldung.springboothsqldb.application.controllers;
-
-import com.baeldung.springboothsqldb.application.entities.Customer;
-import com.baeldung.springboothsqldb.application.repositories.CustomerRepository;
-import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class CustomerController {
-
- private final CustomerRepository customerRepository;
-
- @Autowired
- public CustomerController(CustomerRepository customerRepository) {
- this.customerRepository = customerRepository;
- }
-
- @PostMapping("/customers")
- public Customer addCustomer(@RequestBody Customer customer) {
- customerRepository.save(customer);
- return customer;
- }
-
- @GetMapping("/customers")
- public List getCustomers() {
- return (List) customerRepository.findAll();
- }
-}
+package com.baeldung.springboothsqldb.application.controllers;
+
+import com.baeldung.springboothsqldb.application.entities.Customer;
+import com.baeldung.springboothsqldb.application.repositories.CustomerRepository;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class CustomerController {
+
+ private final CustomerRepository customerRepository;
+
+ @Autowired
+ public CustomerController(CustomerRepository customerRepository) {
+ this.customerRepository = customerRepository;
+ }
+
+ @PostMapping("/customers")
+ public Customer addCustomer(@RequestBody Customer customer) {
+ customerRepository.save(customer);
+ return customer;
+ }
+
+ @GetMapping("/customers")
+ public List getCustomers() {
+ return (List) customerRepository.findAll();
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java
similarity index 95%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java
index 636a80e272..ee2735abb8 100644
--- a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java
+++ b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/entities/Customer.java
@@ -1,55 +1,55 @@
-package com.baeldung.springboothsqldb.application.entities;
-
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Table;
-
-@Entity
-@Table(name = "customers")
-public class Customer {
-
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private long id;
-
- private String name;
- private String email;
-
- public Customer() {}
-
- public Customer(String name, String email) {
- this.name = name;
- this.email = email;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- public long getId() {
- return id;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getEmail() {
- return email;
- }
-
- @Override
- public String toString() {
- return "Customer{" + "id=" + id + ", name=" + name + ", email=" + email + '}';
- }
-}
+package com.baeldung.springboothsqldb.application.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "customers")
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long id;
+
+ private String name;
+ private String email;
+
+ public Customer() {}
+
+ public Customer(String name, String email) {
+ this.name = name;
+ this.email = email;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ @Override
+ public String toString() {
+ return "Customer{" + "id=" + id + ", name=" + name + ", email=" + email + '}';
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java
similarity index 97%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java
index a81aa62c43..cc8a045e83 100644
--- a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java
+++ b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/springboothsqldb/application/repositories/CustomerRepository.java
@@ -1,8 +1,8 @@
-package com.baeldung.springboothsqldb.application.repositories;
-
-import com.baeldung.springboothsqldb.application.entities.Customer;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.stereotype.Repository;
-
-@Repository
-public interface CustomerRepository extends CrudRepository {}
+package com.baeldung.springboothsqldb.application.repositories;
+
+import com.baeldung.springboothsqldb.application.entities.Customer;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CustomerRepository extends CrudRepository {}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/SpringBootConsoleApplication.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/SpringBootConsoleApplication.java
similarity index 100%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/SpringBootConsoleApplication.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/SpringBootConsoleApplication.java
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/entities/Customer.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/entities/Customer.java
similarity index 100%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/entities/Customer.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/entities/Customer.java
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/repositories/CustomerRepository.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/repositories/CustomerRepository.java
similarity index 100%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/repositories/CustomerRepository.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/repositories/CustomerRepository.java
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/runners/CommandLineCrudRunner.java b/persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/runners/CommandLineCrudRunner.java
similarity index 100%
rename from persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/tomcatconnectionpool/application/runners/CommandLineCrudRunner.java
rename to persistence-modules/spring-boot-persistence-2/src/main/java/com/baeldung/tomcatconnectionpool/application/runners/CommandLineCrudRunner.java
diff --git a/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-derby.properties b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-derby.properties
new file mode 100644
index 0000000000..5b5ff05236
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-derby.properties
@@ -0,0 +1,8 @@
+jdbc.driverClassName=org.apache.derby.jdbc.EmbeddedDriver
+jdbc.url=jdbc:derby:memory:myD;create=true
+jdbc.user=sa
+jdbc.pass=
+
+hibernate.dialect=org.hibernate.dialect.DerbyDialect
+hibernate.show_sql=true
+hibernate.hbm2ddl.auto=create-drop
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-generic-entity.properties b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-generic-entity.properties
new file mode 100644
index 0000000000..b19304cb1f
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-generic-entity.properties
@@ -0,0 +1,8 @@
+jdbc.driverClassName=org.h2.Driver
+jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
+jdbc.user=sa
+jdbc.pass=sa
+
+hibernate.dialect=org.hibernate.dialect.H2Dialect
+hibernate.show_sql=true
+hibernate.hbm2ddl.auto=create-drop
diff --git a/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-hsqldb.properties b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-hsqldb.properties
new file mode 100644
index 0000000000..d045a8b7e5
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-hsqldb.properties
@@ -0,0 +1,8 @@
+jdbc.driverClassName=org.hsqldb.jdbc.JDBCDriver
+jdbc.url=jdbc:hsqldb:mem:myDb
+jdbc.user=sa
+jdbc.pass=
+
+hibernate.dialect=org.hibernate.dialect.HSQLDialect
+hibernate.show_sql=true
+hibernate.hbm2ddl.auto=create-drop
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-sqlite.properties b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-sqlite.properties
new file mode 100644
index 0000000000..ee16081603
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-2/src/main/resources/persistence-sqlite.properties
@@ -0,0 +1,7 @@
+jdbc.driverClassName=org.sqlite.JDBC
+jdbc.url=jdbc:sqlite:memory:myDb?cache=shared
+jdbc.user=sa
+jdbc.pass=sa
+hibernate.dialect=com.baeldung.dialect.SQLiteDialect
+hibernate.hbm2ddl.auto=create-drop
+hibernate.show_sql=true
diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java b/persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java
similarity index 97%
rename from persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java
rename to persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java
index 33891e3d43..be16f8f563 100644
--- a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java
+++ b/persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/springboothsqldb/application/tests/CustomerControllerUnitTest.java
@@ -1,61 +1,61 @@
-package com.baeldung.springboothsqldb.application.tests;
-
-import com.baeldung.springboothsqldb.application.entities.Customer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectWriter;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import java.nio.charset.Charset;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.http.MediaType;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
-import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
-
-@RunWith(SpringRunner.class)
-@SpringBootTest
-@AutoConfigureMockMvc
-public class CustomerControllerUnitTest {
-
- private static MediaType MEDIA_TYPE_JSON;
-
- @Autowired
- private MockMvc mockMvc;
-
- @Before
- public void setUpJsonMediaType() {
- MEDIA_TYPE_JSON = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
-
- }
-
- @Test
- public void whenPostHttpRequesttoCustomers_thenStatusOK() throws Exception {
- Customer customer = new Customer("John", "john@domain.com");
- ObjectMapper mapper = new ObjectMapper();
- mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
- ObjectWriter objectWriter = mapper.writer().withDefaultPrettyPrinter();
- String requestJson = objectWriter.writeValueAsString(customer);
-
- this.mockMvc
- .perform(MockMvcRequestBuilders.post("/customers")
- .contentType(MEDIA_TYPE_JSON)
- .content(requestJson)
- )
-
- .andExpect(MockMvcResultMatchers.status().isOk());
- }
-
- @Test
- public void whenGetHttpRequesttoCustomers_thenStatusOK() throws Exception {
- this.mockMvc
- .perform(MockMvcRequestBuilders.get("/customers"))
-
- .andExpect(MockMvcResultMatchers.content().contentType(MEDIA_TYPE_JSON))
- .andExpect(MockMvcResultMatchers.status().isOk());
- }
-}
+package com.baeldung.springboothsqldb.application.tests;
+
+import com.baeldung.springboothsqldb.application.entities.Customer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import java.nio.charset.Charset;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureMockMvc
+public class CustomerControllerUnitTest {
+
+ private static MediaType MEDIA_TYPE_JSON;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUpJsonMediaType() {
+ MEDIA_TYPE_JSON = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
+
+ }
+
+ @Test
+ public void whenPostHttpRequesttoCustomers_thenStatusOK() throws Exception {
+ Customer customer = new Customer("John", "john@domain.com");
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
+ ObjectWriter objectWriter = mapper.writer().withDefaultPrettyPrinter();
+ String requestJson = objectWriter.writeValueAsString(customer);
+
+ this.mockMvc
+ .perform(MockMvcRequestBuilders.post("/customers")
+ .contentType(MEDIA_TYPE_JSON)
+ .content(requestJson)
+ )
+
+ .andExpect(MockMvcResultMatchers.status().isOk());
+ }
+
+ @Test
+ public void whenGetHttpRequesttoCustomers_thenStatusOK() throws Exception {
+ this.mockMvc
+ .perform(MockMvcRequestBuilders.get("/customers"))
+
+ .andExpect(MockMvcResultMatchers.content().contentType(MEDIA_TYPE_JSON))
+ .andExpect(MockMvcResultMatchers.status().isOk());
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java b/persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java
similarity index 84%
rename from persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java
rename to persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java
index 5400d76fbe..4422c27150 100644
--- a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java
+++ b/persistence-modules/spring-boot-persistence-2/src/test/java/com/baeldung/tomcatconnectionpool/test/application/SpringBootTomcatConnectionPoolIntegrationTest.java
@@ -4,6 +4,7 @@ import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.tomcatconnectionpool.application.SpringBootConsoleApplication;
@@ -13,6 +14,7 @@ import org.springframework.boot.test.context.SpringBootTest;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {SpringBootConsoleApplication.class})
+@TestPropertySource(properties = "spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource")
public class SpringBootTomcatConnectionPoolIntegrationTest {
@Autowired
diff --git a/persistence-modules/spring-boot-persistence-2/src/test/resources/schema.sql b/persistence-modules/spring-boot-persistence-2/src/test/resources/schema.sql
index a0d0eaf62e..8d7db6c9f3 100644
--- a/persistence-modules/spring-boot-persistence-2/src/test/resources/schema.sql
+++ b/persistence-modules/spring-boot-persistence-2/src/test/resources/schema.sql
@@ -1,3 +1,6 @@
+drop table if exists car_maker;
+drop table if exists car_model;
+
--
-- Car makers table
--
diff --git a/persistence-modules/spring-boot-persistence-h2/pom.xml b/persistence-modules/spring-boot-persistence-h2/pom.xml
index 777bc6cb2d..7070b5e674 100644
--- a/persistence-modules/spring-boot-persistence-h2/pom.xml
+++ b/persistence-modules/spring-boot-persistence-h2/pom.xml
@@ -35,23 +35,21 @@
${lombok.version}
compile
-
- org.hibernate
- hibernate-core
- ${hibernate.version}
-
com.vladmihalcea
db-util
${db-util.version}
+
+ net.bytebuddy
+ byte-buddy
+ ${byte-buddy.version}
+
com.baeldung.h2db.demo.server.SpringBootApp
- 2.0.4.RELEASE
- 5.3.11.Final
1.0.4
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
index ed1ffc63c3..0466eaac79 100644
--- a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
@@ -8,5 +8,4 @@ spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
#spring.jpa.properties.hibernate.check_nullability=true
spring.h2.console.enabled=true
-spring.h2.console.path=/h2-console
-debug=true
\ No newline at end of file
+spring.h2.console.path=/h2-console
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence/README.MD b/persistence-modules/spring-boot-persistence/README.MD
index 96eb326cbe..5b9fbf7b79 100644
--- a/persistence-modules/spring-boot-persistence/README.MD
+++ b/persistence-modules/spring-boot-persistence/README.MD
@@ -1,12 +1,10 @@
### Relevant Articles:
-- [Spring Boot with Multiple SQL Import Files](http://www.baeldung.com/spring-boot-sql-import-files)
-- [Configuring Separate Spring DataSource for Tests](http://www.baeldung.com/spring-testing-separate-data-source)
-- [Quick Guide on Loading Initial Data with Spring Boot](http://www.baeldung.com/spring-boot-data-sql-and-schema-sql)
-- [Configuring a Tomcat Connection Pool in Spring Boot](https://www.baeldung.com/spring-boot-tomcat-connection-pool)
-- [Hibernate Field Naming with Spring Boot](https://www.baeldung.com/hibernate-field-naming-spring-boot)
-- [Integrating Spring Boot with HSQLDB](https://www.baeldung.com/spring-boot-hsqldb)
+- [Spring Boot with Multiple SQL Import Files](https://www.baeldung.com/spring-boot-sql-import-files)
+- [Configuring Separate Spring DataSource for Tests](https://www.baeldung.com/spring-testing-separate-data-source)
+- [Quick Guide on Loading Initial Data with Spring Boot](https://www.baeldung.com/spring-boot-data-sql-and-schema-sql)
- [Configuring a DataSource Programmatically in Spring Boot](https://www.baeldung.com/spring-boot-configure-data-source-programmatic)
- [Resolving “Failed to Configure a DataSource” Error](https://www.baeldung.com/spring-boot-failed-to-configure-data-source)
+- [Hibernate Field Naming with Spring Boot](https://www.baeldung.com/hibernate-field-naming-spring-boot)
- [Spring Boot with Hibernate](https://www.baeldung.com/spring-boot-hibernate)
-- [List of In-Memory Databases](http://www.baeldung.com/java-in-memory-databases)
\ No newline at end of file
+- More articles: [[more -->]](../spring-boot-persistence-2)
\ No newline at end of file
diff --git a/persistence-modules/spring-jpa/README.md b/persistence-modules/spring-jpa/README.md
index 599a667a13..5db88cd5ee 100644
--- a/persistence-modules/spring-jpa/README.md
+++ b/persistence-modules/spring-jpa/README.md
@@ -13,7 +13,6 @@
- [Transactions with Spring and JPA](https://www.baeldung.com/transaction-configuration-with-jpa-and-spring)
- [Use Criteria Queries in a Spring Data Application](https://www.baeldung.com/spring-data-criteria-queries)
- [Many-To-Many Relationship in JPA](https://www.baeldung.com/jpa-many-to-many)
-- [Spring Persistence (Hibernate and JPA) with a JNDI datasource](https://www.baeldung.com/spring-persistence-hibernate-and-jpa-with-a-jndi-datasource/)
### Eclipse Config
diff --git a/rabbitmq/src/main/java/com/baeldung/consumer/Receiver.java b/rabbitmq/src/main/java/com/baeldung/consumer/Receiver.java
index d0612406e9..b2779a6b29 100644
--- a/rabbitmq/src/main/java/com/baeldung/consumer/Receiver.java
+++ b/rabbitmq/src/main/java/com/baeldung/consumer/Receiver.java
@@ -17,7 +17,7 @@ public class Receiver {
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
- Consumer consumer = new DefaultConsumer(channel) {
+ DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope, AMQP.BasicProperties properties,
diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index 5acaa30328..6caa93158a 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -49,6 +49,7 @@
spring-boot-parent
spring-boot-performance
spring-boot-properties
+ spring-boot-properties-2
spring-boot-property-exp
spring-boot-runtime
spring-boot-security
diff --git a/spring-boot-modules/spring-boot-ci-cd/README.md b/spring-boot-modules/spring-boot-ci-cd/README.md
index d207cd0fa0..ee8f106b53 100644
--- a/spring-boot-modules/spring-boot-ci-cd/README.md
+++ b/spring-boot-modules/spring-boot-ci-cd/README.md
@@ -1 +1,7 @@
-# Spring Boot CI/CD
\ No newline at end of file
+# Spring Boot CI/CD
+
+This module contains articles about CI/CD with Spring Boot
+
+## Relevant Articles
+
+- [CI/CD for a Spring Boot Project](https://www.baeldung.com/spring-boot-ci-cd)
diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml
index c29c1a738b..8e917df2b7 100644
--- a/spring-boot-modules/spring-boot-keycloak/pom.xml
+++ b/spring-boot-modules/spring-boot-keycloak/pom.xml
@@ -11,9 +11,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
@@ -41,6 +41,10 @@
org.springframework.boot
spring-boot-starter-data-jpa
+
+ net.bytebuddy
+ byte-buddy
+
org.springframework.boot
@@ -76,7 +80,7 @@
- 3.3.0.Final
+ 10.0.1
diff --git a/spring-boot-modules/spring-boot-libraries/pom.xml b/spring-boot-modules/spring-boot-libraries/pom.xml
index 090967d8a8..189eb4cf1a 100644
--- a/spring-boot-modules/spring-boot-libraries/pom.xml
+++ b/spring-boot-modules/spring-boot-libraries/pom.xml
@@ -87,7 +87,35 @@
javase
${zxing.version}
-
+
+
+ com.github.vladimir-bukhtoyarov
+ bucket4j-core
+ ${bucket4j.version}
+
+
+ com.giffing.bucket4j.spring.boot.starter
+ bucket4j-spring-boot-starter
+ ${bucket4j-spring-boot-starter.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ javax.cache
+ cache-api
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ ${caffeine.version}
+
+
+ com.github.ben-manes.caffeine
+ jcache
+ ${caffeine.version}
+
@@ -200,6 +228,9 @@
2.1
2.6.0
3.3.0
+ 4.10.0
+ 0.2.0
+ 2.8.2
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java
new file mode 100644
index 0000000000..f16d347f85
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java
@@ -0,0 +1,21 @@
+package com.baeldung.ratelimiting.bootstarterapp;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cache.annotation.EnableCaching;
+
+@SpringBootApplication(scanBasePackages = "com.baeldung.ratelimiting", exclude = {
+ DataSourceAutoConfiguration.class,
+ SecurityAutoConfiguration.class,
+})
+@EnableCaching
+public class Bucket4jRateLimitApp {
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(Bucket4jRateLimitApp.class)
+ .properties("spring.config.location=classpath:ratelimiting/application-bucket4j-starter.yml")
+ .run(args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java
new file mode 100644
index 0000000000..bb179b9b38
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java
@@ -0,0 +1,35 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.baeldung.ratelimiting.bucket4japp.interceptor.RateLimitInterceptor;
+
+@SpringBootApplication(scanBasePackages = "com.baeldung.ratelimiting", exclude = {
+ DataSourceAutoConfiguration.class,
+ SecurityAutoConfiguration.class
+})
+public class Bucket4jRateLimitApp implements WebMvcConfigurer {
+
+ @Autowired
+ @Lazy
+ private RateLimitInterceptor interceptor;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(interceptor)
+ .addPathPatterns("/api/v1/area/**");
+ }
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(Bucket4jRateLimitApp.class)
+ .properties("spring.config.location=classpath:ratelimiting/application-bucket4j.yml")
+ .run(args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java
new file mode 100644
index 0000000000..8a18d6c2b5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java
@@ -0,0 +1,57 @@
+package com.baeldung.ratelimiting.bucket4japp.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlanService;
+
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.ConsumptionProbe;
+
+@Component
+public class RateLimitInterceptor implements HandlerInterceptor {
+
+ private static final String HEADER_API_KEY = "X-api-key";
+ private static final String HEADER_LIMIT_REMAINING = "X-Rate-Limit-Remaining";
+ private static final String HEADER_RETRY_AFTER = "X-Rate-Limit-Retry-After-Seconds";
+
+ @Autowired
+ private PricingPlanService pricingPlanService;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+
+ String apiKey = request.getHeader(HEADER_API_KEY);
+
+ if (apiKey == null || apiKey.isEmpty()) {
+ response.sendError(HttpStatus.BAD_REQUEST.value(), "Missing Header: " + HEADER_API_KEY);
+ return false;
+ }
+
+ Bucket tokenBucket = pricingPlanService.resolveBucket(apiKey);
+
+ ConsumptionProbe probe = tokenBucket.tryConsumeAndReturnRemaining(1);
+
+ if (probe.isConsumed()) {
+
+ response.addHeader(HEADER_LIMIT_REMAINING, String.valueOf(probe.getRemainingTokens()));
+ return true;
+
+ } else {
+
+ long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
+
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.addHeader(HEADER_RETRY_AFTER, String.valueOf(waitForRefill));
+ response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), "You have exhausted your API Request Quota"); // 429
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java
new file mode 100644
index 0000000000..27c30ba3a0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java
@@ -0,0 +1,42 @@
+package com.baeldung.ratelimiting.bucket4japp.service;
+
+import java.time.Duration;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Refill;
+
+public enum PricingPlan {
+
+ FREE(20),
+
+ BASIC(40),
+
+ PROFESSIONAL(100);
+
+ private int bucketCapacity;
+
+ private PricingPlan(int bucketCapacity) {
+ this.bucketCapacity = bucketCapacity;
+ }
+
+ Bandwidth getLimit() {
+ return Bandwidth.classic(bucketCapacity, Refill.intervally(bucketCapacity, Duration.ofHours(1)));
+ }
+
+ public int bucketCapacity() {
+ return bucketCapacity;
+ }
+
+ static PricingPlan resolvePlanFromApiKey(String apiKey) {
+ if (apiKey == null || apiKey.isEmpty()) {
+ return FREE;
+
+ } else if (apiKey.startsWith("PX001-")) {
+ return PROFESSIONAL;
+
+ } else if (apiKey.startsWith("BX001-")) {
+ return BASIC;
+ }
+ return FREE;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java
new file mode 100644
index 0000000000..7d8a718601
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java
@@ -0,0 +1,31 @@
+package com.baeldung.ratelimiting.bucket4japp.service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.stereotype.Service;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.Bucket4j;
+
+@Service
+public class PricingPlanService {
+
+ private final Map cache = new ConcurrentHashMap<>();
+
+ public Bucket resolveBucket(String apiKey) {
+ return cache.computeIfAbsent(apiKey, this::newBucket);
+ }
+
+ private Bucket newBucket(String apiKey) {
+ PricingPlan pricingPlan = PricingPlan.resolvePlanFromApiKey(apiKey);
+ return bucket(pricingPlan.getLimit());
+ }
+
+ private Bucket bucket(Bandwidth limit) {
+ return Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java
new file mode 100644
index 0000000000..f3fb63ebdd
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java
@@ -0,0 +1,29 @@
+package com.baeldung.ratelimiting.controller;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.ratelimiting.dto.AreaV1;
+import com.baeldung.ratelimiting.dto.RectangleDimensionsV1;
+import com.baeldung.ratelimiting.dto.TriangleDimensionsV1;
+
+@RestController
+@RequestMapping(value = "/api/v1/area", consumes = MediaType.APPLICATION_JSON_VALUE)
+class AreaCalculationController {
+
+ @PostMapping(value = "/rectangle", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity rectangle(@RequestBody RectangleDimensionsV1 dimensions) {
+
+ return ResponseEntity.ok(new AreaV1("rectangle", dimensions.getLength() * dimensions.getWidth()));
+ }
+
+ @PostMapping(value = "/triangle", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity triangle(@RequestBody TriangleDimensionsV1 dimensions) {
+
+ return ResponseEntity.ok(new AreaV1("triangle", 0.5d * dimensions.getHeight() * dimensions.getBase()));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java
new file mode 100644
index 0000000000..78097f55b2
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java
@@ -0,0 +1,20 @@
+package com.baeldung.ratelimiting.dto;
+
+public class AreaV1 {
+
+ private String shape;
+ private Double area;
+
+ public AreaV1(String shape, Double area) {
+ this.area = area;
+ this.shape = shape;
+ }
+
+ public Double getArea() {
+ return area;
+ }
+
+ public String getShape() {
+ return shape;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java
new file mode 100644
index 0000000000..e3c17e1ba7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java
@@ -0,0 +1,15 @@
+package com.baeldung.ratelimiting.dto;
+
+public class RectangleDimensionsV1 {
+
+ private double length;
+ private double width;
+
+ public double getLength() {
+ return length;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java
new file mode 100644
index 0000000000..44c954bded
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java
@@ -0,0 +1,15 @@
+package com.baeldung.ratelimiting.dto;
+
+public class TriangleDimensionsV1 {
+
+ private double base;
+ private double height;
+
+ public double getBase() {
+ return base;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml
new file mode 100644
index 0000000000..ecc9f22e0a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml
@@ -0,0 +1,40 @@
+server:
+ port: 9001
+
+spring:
+ application:
+ name: bucket4j-starter-api-rate-limit-app
+ mvc:
+ throw-exception-if-no-handler-found: true
+ resources:
+ add-mappings: false
+ cache:
+ cache-names:
+ - rate-limit-buckets
+ caffeine:
+ spec: maximumSize=100000,expireAfterAccess=3600s
+
+bucket4j:
+ enabled: true
+ filters:
+ - cache-name: rate-limit-buckets
+ url: /api/v1/area.*
+ http-response-body: "{ \"status\": 429, \"error\": \"Too Many Requests\", \"message\": \"You have exhausted your API Request Quota\" }"
+ rate-limits:
+ - expression: "getHeader('X-api-key')"
+ execute-condition: "getHeader('X-api-key').startsWith('PX001-')"
+ bandwidths:
+ - capacity: 100
+ time: 1
+ unit: hours
+ - expression: "getHeader('X-api-key')"
+ execute-condition: "getHeader('X-api-key').startsWith('BX001-')"
+ bandwidths:
+ - capacity: 40
+ time: 1
+ unit: hours
+ - expression: "getHeader('X-api-key')"
+ bandwidths:
+ - capacity: 20
+ time: 1
+ unit: hours
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml
new file mode 100644
index 0000000000..ae19622d9b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml
@@ -0,0 +1,10 @@
+server:
+ port: 9000
+
+spring:
+ application:
+ name: bucket4j-api-rate-limit-app
+ mvc:
+ throw-exception-if-no-handler-found: true
+ resources:
+ add-mappings: false
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java
new file mode 100644
index 0000000000..d93e61988b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.ratelimiting.bootstarterapp;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Bucket4jRateLimitApp.class)
+@TestPropertySource(properties = "spring.config.location=classpath:ratelimiting/application-bucket4j-starter.yml")
+@AutoConfigureMockMvc
+public class Bucket4jBootStarterRateLimitIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void givenTriangleAreaCalculator_whenRequestsWithinRateLimit_thenAccepted() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/triangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"height\": 8, \"base\": 10 }")
+ .header("X-api-key", "FX001-UBSZ5YRYQ");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request)
+ .andExpect(status().isOk())
+ .andExpect(header().exists("X-Rate-Limit-Remaining"))
+ .andExpect(jsonPath("$.shape", equalTo("triangle")))
+ .andExpect(jsonPath("$.area", equalTo(40d)));
+ }
+ }
+
+ @Test
+ public void givenTriangleAreaCalculator_whenRequestRateLimitTriggered_thenRejected() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/triangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"height\": 8, \"base\": 10 }")
+ .header("X-api-key", "FX001-ZBSY6YSLP");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request); // exhaust limit
+ }
+
+ mockMvc.perform(request)
+ .andExpect(status().isTooManyRequests())
+ .andExpect(jsonPath("$.message", equalTo("You have exhausted your API Request Quota")))
+ .andExpect(header().exists("X-Rate-Limit-Retry-After-Seconds"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java
new file mode 100644
index 0000000000..20f57a7021
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Bucket4jRateLimitApp.class)
+@AutoConfigureMockMvc
+public class Bucket4jRateLimitIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void givenRectangleAreaCalculator_whenRequestsWithinRateLimit_thenAccepted() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/rectangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"length\": 12, \"width\": 10 }")
+ .header("X-api-key", "FX001-UBSZ5YRYQ");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request)
+ .andExpect(status().isOk())
+ .andExpect(header().exists("X-Rate-Limit-Remaining"))
+ .andExpect(jsonPath("$.shape", equalTo("rectangle")))
+ .andExpect(jsonPath("$.area", equalTo(120d)));
+ }
+ }
+
+ @Test
+ public void givenReactangleAreaCalculator_whenRequestRateLimitTriggered_thenRejected() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/rectangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"length\": 12, \"width\": 10 }")
+ .header("X-api-key", "FX001-ZBSY6YSLP");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request); // exhaust limit
+ }
+
+ mockMvc.perform(request)
+ .andExpect(status().isTooManyRequests())
+ .andExpect(status().reason("You have exhausted your API Request Quota"))
+ .andExpect(header().exists("X-Rate-Limit-Retry-After-Seconds"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java
new file mode 100644
index 0000000000..fbf63ba403
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java
@@ -0,0 +1,82 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.Bucket4j;
+import io.github.bucket4j.Refill;
+
+public class Bucket4jUsageUnitTest {
+
+ @Test
+ public void givenBucketLimit_whenExceedLimit_thenConsumeReturnsFalse() {
+ Refill refill = Refill.intervally(10, Duration.ofMinutes(1));
+ Bandwidth limit = Bandwidth.classic(10, refill);
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+
+ for (int i = 1; i <= 10; i++) {
+ assertTrue(bucket.tryConsume(1));
+ }
+ assertFalse(bucket.tryConsume(1));
+ }
+
+ @Test
+ public void givenMultipletLimits_whenExceedSmallerLimit_thenConsumeReturnsFalse() {
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(Bandwidth.classic(10, Refill.intervally(10, Duration.ofMinutes(1))))
+ .addLimit(Bandwidth.classic(5, Refill.intervally(5, Duration.ofSeconds(20))))
+ .build();
+
+ for (int i = 1; i <= 5; i++) {
+ assertTrue(bucket.tryConsume(1));
+ }
+ assertFalse(bucket.tryConsume(1));
+ }
+
+ @Test
+ public void givenBucketLimit_whenThrottleRequests_thenConsumeReturnsTrue() throws InterruptedException {
+ Refill refill = Refill.intervally(1, Duration.ofSeconds(2));
+ Bandwidth limit = Bandwidth.classic(1, refill);
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+
+ assertTrue(bucket.tryConsume(1));
+
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+ CountDownLatch latch = new CountDownLatch(1);
+
+ executor.schedule(new AssertTryConsume(bucket, latch), 2, TimeUnit.SECONDS);
+
+ latch.await();
+ }
+
+ static class AssertTryConsume implements Runnable {
+
+ private Bucket bucket;
+ private CountDownLatch latch;
+
+ AssertTryConsume(Bucket bucket, CountDownLatch latch) {
+ this.bucket = bucket;
+ this.latch = latch;
+ }
+
+ @Override
+ public void run() {
+ assertTrue(bucket.tryConsume(1));
+ latch.countDown();
+ }
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java
new file mode 100644
index 0000000000..325b898779
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlanService;
+
+import io.github.bucket4j.Bucket;
+
+public class PricingPlanServiceUnitTest {
+
+ private PricingPlanService service = new PricingPlanService();
+
+ @Test
+ public void givenAPIKey_whenFreePlan_thenReturnFreePlanBucket() {
+ Bucket bucket = service.resolveBucket("FX001-UBSZ5YRYQ");
+
+ assertEquals(PricingPlan.FREE.bucketCapacity(), bucket.getAvailableTokens());
+ }
+
+ @Test
+ public void givenAPIKey_whenBasiclan_thenReturnBasicPlanBucket() {
+ Bucket bucket = service.resolveBucket("BX001-MBSZ5YRYP");
+
+ assertEquals(PricingPlan.BASIC.bucketCapacity(), bucket.getAvailableTokens());
+ }
+
+ @Test
+ public void givenAPIKey_whenProfessionalPlan_thenReturnProfessionalPlanBucket() {
+ Bucket bucket = service.resolveBucket("PX001-NBSZ5YRYY");
+
+ assertEquals(PricingPlan.PROFESSIONAL.bucketCapacity(), bucket.getAvailableTokens());
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-birt/pom.xml b/spring-boot-modules/spring-boot-mvc-birt/pom.xml
index f65b851f30..0e8e231a84 100644
--- a/spring-boot-modules/spring-boot-mvc-birt/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-birt/pom.xml
@@ -75,7 +75,6 @@
- 2.1.1.RELEASE
com.baeldung.birt.engine.ReportEngineApplication
1.8
1.8
diff --git a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
index baba410b39..331a85ec5b 100644
--- a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
+++ b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
@@ -20,7 +20,7 @@
org.springframework.boot
spring-boot-starter-data-jpa
- ${spring-boot.version}
+ ${spring-boot-starter-data-jpa.version}
@@ -38,7 +38,7 @@
1.8
- 2.1.5.RELEASE
+ 2.2.5.RELEASE
4.11
diff --git a/spring-boot-modules/spring-boot-properties-2/README.md b/spring-boot-modules/spring-boot-properties-2/README.md
index 01e2970e89..cd0fd5e5ee 100644
--- a/spring-boot-modules/spring-boot-properties-2/README.md
+++ b/spring-boot-modules/spring-boot-properties-2/README.md
@@ -3,3 +3,8 @@
This module contains articles about Properties in Spring Boot.
### Relevant Articles:
+- [Load Spring Boot Properties From a JSON File](https://www.baeldung.com/spring-boot-json-properties)
+- [A Quick Guide to Spring @Value](https://www.baeldung.com/spring-value-annotation)
+- [Using Spring @Value with Defaults](https://www.baeldung.com/spring-value-defaults)
+- [How to Inject a Property Value Into a Class Not Managed by Spring?](https://www.baeldung.com/inject-properties-value-non-spring-class)
+- More articles: [[<-- prev]](../spring-boot-properties)
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties-2/pom.xml b/spring-boot-modules/spring-boot-properties-2/pom.xml
index c3c3e57251..bd2a35b19d 100644
--- a/spring-boot-modules/spring-boot-properties-2/pom.xml
+++ b/spring-boot-modules/spring-boot-properties-2/pom.xml
@@ -20,6 +20,10 @@
org.springframework.boot
spring-boot-starter
+
+ org.springframework.boot
+ spring-boot-starter-web
+
diff --git a/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/ConfigPropertiesDemoApplication.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/ConfigPropertiesDemoApplication.java
new file mode 100644
index 0000000000..a1e2584b2c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/ConfigPropertiesDemoApplication.java
@@ -0,0 +1,16 @@
+package com.baeldung.properties.json;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan(basePackageClasses = {JsonProperties.class, CustomJsonProperties.class})
+public class ConfigPropertiesDemoApplication {
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(ConfigPropertiesDemoApplication.class).initializers(new JsonPropertyContextInitializer())
+ .run();
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/CustomJsonProperties.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/CustomJsonProperties.java
similarity index 97%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/CustomJsonProperties.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/CustomJsonProperties.java
index 084138ec6f..555711c49b 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/CustomJsonProperties.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/CustomJsonProperties.java
@@ -1,4 +1,4 @@
-package com.baeldung.properties;
+package com.baeldung.properties.json;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonProperties.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonProperties.java
similarity index 92%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonProperties.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonProperties.java
index 31b3be14b4..6ada770e3b 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonProperties.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonProperties.java
@@ -1,12 +1,13 @@
-package com.baeldung.properties;
-
-import java.util.LinkedHashMap;
-import java.util.List;
+package com.baeldung.properties.json;
+import com.baeldung.properties.json.factory.JsonPropertySourceFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
+import java.util.LinkedHashMap;
+import java.util.List;
+
@Component
@PropertySource(value = "classpath:configprops.json", factory = JsonPropertySourceFactory.class)
@ConfigurationProperties
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonPropertyContextInitializer.java
similarity index 98%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonPropertyContextInitializer.java
index 0aee149123..e3b713f29b 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertyContextInitializer.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/JsonPropertyContextInitializer.java
@@ -1,4 +1,11 @@
-package com.baeldung.properties;
+package com.baeldung.properties.json;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Collection;
@@ -10,14 +17,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.MapPropertySource;
-import org.springframework.core.env.PropertySource;
-import org.springframework.core.io.Resource;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
public class JsonPropertyContextInitializer implements ApplicationContextInitializer {
private final static String CUSTOM_PREFIX = "custom.";
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/factory/JsonPropertySourceFactory.java
similarity index 93%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/factory/JsonPropertySourceFactory.java
index c14d3faea5..dccaae4ad2 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/JsonPropertySourceFactory.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/json/factory/JsonPropertySourceFactory.java
@@ -1,14 +1,13 @@
-package com.baeldung.properties;
-
-import java.io.IOException;
-import java.util.Map;
+package com.baeldung.properties.json.factory;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.Map;
public class JsonPropertySourceFactory implements PropertySourceFactory {
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ClassNotManagedBySpring.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ClassNotManagedBySpring.java
similarity index 95%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ClassNotManagedBySpring.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ClassNotManagedBySpring.java
index 0329769d3c..59ee73a07b 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ClassNotManagedBySpring.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ClassNotManagedBySpring.java
@@ -1,4 +1,4 @@
-package com.baeldung.value;
+package com.baeldung.properties.value;
public class ClassNotManagedBySpring {
diff --git a/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/CollectionProvider.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/CollectionProvider.java
new file mode 100644
index 0000000000..6327e688e8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/CollectionProvider.java
@@ -0,0 +1,27 @@
+package com.baeldung.properties.value;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+@Component
+@PropertySource("classpath:values.properties")
+public class CollectionProvider {
+
+ private final List values = new ArrayList<>();
+
+ public Collection getValues() {
+ return Collections.unmodifiableCollection(values);
+ }
+
+ @Autowired
+ public void setValues(@Value("#{'${listOfValues}'.split(',')}") List values) {
+ this.values.addAll(values);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/InitializerBean.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/InitializerBean.java
similarity index 94%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/InitializerBean.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/InitializerBean.java
index 8c8634c767..b06cfa12ea 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/InitializerBean.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/InitializerBean.java
@@ -1,4 +1,4 @@
-package com.baeldung.value;
+package com.baeldung.properties.value;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
diff --git a/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/PriorityProvider.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/PriorityProvider.java
new file mode 100644
index 0000000000..892118eb06
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/PriorityProvider.java
@@ -0,0 +1,22 @@
+package com.baeldung.properties.value;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+@Component
+@PropertySource("classpath:values.properties")
+public class PriorityProvider {
+
+ private final String priority;
+
+ @Autowired
+ public PriorityProvider(@Value("${priority:normal}") String priority) {
+ this.priority = priority;
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/SomeBean.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/SomeBean.java
similarity index 88%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/SomeBean.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/SomeBean.java
index 39d5245049..b3346a96dd 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/SomeBean.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/SomeBean.java
@@ -1,4 +1,4 @@
-package com.baeldung.value;
+package com.baeldung.properties.value;
public class SomeBean {
private int someValue;
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ValuesApp.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ValuesApp.java
similarity index 98%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ValuesApp.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ValuesApp.java
index 80893c1adf..67547199a6 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/value/ValuesApp.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/ValuesApp.java
@@ -1,10 +1,4 @@
-package com.baeldung.value;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.PostConstruct;
+package com.baeldung.properties.value;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
@@ -13,6 +7,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
+import javax.annotation.PostConstruct;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
@Configuration
@PropertySource(name = "myProperties", value = "values.properties")
public class ValuesApp {
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/valuewithdefaults/ValuesWithDefaultsApp.java b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/defaults/ValuesWithDefaultsApp.java
similarity index 85%
rename from spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/valuewithdefaults/ValuesWithDefaultsApp.java
rename to spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/defaults/ValuesWithDefaultsApp.java
index 589f891e6b..72fa0e03c0 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/valuewithdefaults/ValuesWithDefaultsApp.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/java/com/baeldung/properties/value/defaults/ValuesWithDefaultsApp.java
@@ -1,10 +1,6 @@
-package com.baeldung.valuewithdefaults;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
+package com.baeldung.properties.value.defaults;
+import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -12,8 +8,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.Assert;
-import com.google.common.collect.Lists;
-import com.google.common.primitives.Ints;
+import javax.annotation.PostConstruct;
+import java.util.Arrays;
+import java.util.List;
/**
* Demonstrates setting defaults for @Value annotation. Note that there are no properties
@@ -62,14 +59,13 @@ public class ValuesWithDefaultsApp {
Assert.isTrue(intWithDefaultValue == 42);
// arrays
- List stringListValues = Lists.newArrayList("one", "two", "three");
+ List stringListValues = Arrays.asList("one", "two", "three");
Assert.isTrue(Arrays.asList(stringArrayWithDefaults).containsAll(stringListValues));
- List intListValues = Lists.newArrayList(1, 2, 3);
- Assert.isTrue(Ints.asList(intArrayWithDefaults).containsAll(intListValues));
+ List intListValues = Arrays.asList(1, 2, 3);
+ Assert.isTrue(Arrays.asList(ArrayUtils.toObject(intArrayWithDefaults)).containsAll(intListValues));
// SpEL
Assert.isTrue(spelWithDefaultValue.equals("my default system property value"));
-
}
}
diff --git a/spring-boot-modules/spring-boot-properties/src/main/resources/configprops.json b/spring-boot-modules/spring-boot-properties-2/src/main/resources/configprops.json
similarity index 100%
rename from spring-boot-modules/spring-boot-properties/src/main/resources/configprops.json
rename to spring-boot-modules/spring-boot-properties-2/src/main/resources/configprops.json
diff --git a/spring-boot-modules/spring-boot-properties-2/src/main/resources/values.properties b/spring-boot-modules/spring-boot-properties-2/src/main/resources/values.properties
new file mode 100644
index 0000000000..9c85893d5f
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/main/resources/values.properties
@@ -0,0 +1,4 @@
+value.from.file=Value got from the file
+priority=high
+listOfValues=A,B,C
+valuesMap={key1:'1', key2 : '2', key3 : '3'}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties/src/main/resources/valueswithdefaults.properties b/spring-boot-modules/spring-boot-properties-2/src/main/resources/valueswithdefaults.properties
similarity index 100%
rename from spring-boot-modules/spring-boot-properties/src/main/resources/valueswithdefaults.properties
rename to spring-boot-modules/spring-boot-properties-2/src/main/resources/valueswithdefaults.properties
diff --git a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/json/JsonPropertiesIntegrationTest.java
similarity index 98%
rename from spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java
rename to spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/json/JsonPropertiesIntegrationTest.java
index 2f0e5ae408..6b00489b5c 100644
--- a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/properties/JsonPropertiesIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/json/JsonPropertiesIntegrationTest.java
@@ -1,6 +1,4 @@
-package com.baeldung.properties;
-
-import java.util.Arrays;
+package com.baeldung.properties.json;
import org.hamcrest.Matchers;
import org.junit.Assert;
@@ -10,6 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
+import java.util.Arrays;
+
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = ConfigPropertiesDemoApplication.class, initializers = JsonPropertyContextInitializer.class)
public class JsonPropertiesIntegrationTest {
diff --git a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/value/ClassNotManagedBySpringIntegrationTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/ClassNotManagedBySpringIntegrationTest.java
similarity index 96%
rename from spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/value/ClassNotManagedBySpringIntegrationTest.java
rename to spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/ClassNotManagedBySpringIntegrationTest.java
index 689801bece..271242d4e8 100644
--- a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/value/ClassNotManagedBySpringIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/ClassNotManagedBySpringIntegrationTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.value;
+package com.baeldung.properties.value;
import org.junit.Before;
import org.junit.Test;
diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/CollectionProviderIntegrationTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/CollectionProviderIntegrationTest.java
new file mode 100644
index 0000000000..b045786a29
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/CollectionProviderIntegrationTest.java
@@ -0,0 +1,22 @@
+package com.baeldung.properties.value;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = CollectionProvider.class)
+public class CollectionProviderIntegrationTest {
+
+ @Autowired
+ private CollectionProvider collectionProvider;
+
+ @Test
+ public void givenPropertyFileWhenSetterInjectionUsedThenValueInjected() {
+ assertThat(collectionProvider.getValues()).contains("A", "B", "C");
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/PriorityProviderIntegrationTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/PriorityProviderIntegrationTest.java
new file mode 100644
index 0000000000..d7d1e7d78b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/value/PriorityProviderIntegrationTest.java
@@ -0,0 +1,22 @@
+package com.baeldung.properties.value;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = PriorityProvider.class)
+public class PriorityProviderIntegrationTest {
+
+ @Autowired
+ private PriorityProvider priorityProvider;
+
+ @Test
+ public void givenPropertyFileWhenConstructorInjectionUsedThenValueInjected() {
+ assertThat(priorityProvider.getPriority()).isEqualTo("high");
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties/README.md b/spring-boot-modules/spring-boot-properties/README.md
index f861a01d10..b6685c7587 100644
--- a/spring-boot-modules/spring-boot-properties/README.md
+++ b/spring-boot-modules/spring-boot-properties/README.md
@@ -5,12 +5,9 @@ This module contains articles about Properties in Spring Boot.
### Relevant Articles:
- [Reloading Properties Files in Spring](https://www.baeldung.com/spring-reloading-properties)
- [Guide to @ConfigurationProperties in Spring Boot](https://www.baeldung.com/configuration-properties-in-spring-boot)
-- [Load Spring Boot Properties From a JSON File](https://www.baeldung.com/spring-boot-json-properties)
- [Guide to @EnableConfigurationProperties](https://www.baeldung.com/spring-enable-config-properties)
- [Properties with Spring and Spring Boot](https://www.baeldung.com/properties-with-spring) - checkout the `com.baeldung.properties` package for all scenarios of properties injection and usage
-- [A Quick Guide to Spring @Value](https://www.baeldung.com/spring-value-annotation)
- [Spring YAML Configuration](https://www.baeldung.com/spring-yaml)
-- [Using Spring @Value with Defaults](https://www.baeldung.com/spring-value-defaults)
-- [How to Inject a Property Value Into a Class Not Managed by Spring?](https://www.baeldung.com/inject-properties-value-non-spring-class)
- [Add Build Properties to a Spring Boot Application](https://www.baeldung.com/spring-boot-build-properties)
- [IntelliJ – Cannot Resolve Spring Boot Configuration Properties Error](https://www.baeldung.com/intellij-resolve-spring-boot-configuration-properties)
+- More articles: [[more -->]](../spring-boot-properties-2)
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java b/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java
index e280bbd79f..1e5e88921a 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java
+++ b/spring-boot-modules/spring-boot-properties/src/main/java/com/baeldung/properties/ConfigPropertiesDemoApplication.java
@@ -1,5 +1,7 @@
package com.baeldung.properties;
+import com.baeldung.buildproperties.Application;
+import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ComponentScan;
@@ -7,11 +9,10 @@ import org.springframework.context.annotation.ComponentScan;
import com.baeldung.configurationproperties.ConfigProperties;
@SpringBootApplication
-@ComponentScan(basePackageClasses = { ConfigProperties.class, JsonProperties.class, CustomJsonProperties.class})
+@ComponentScan(basePackageClasses = {ConfigProperties.class, AdditionalProperties.class})
public class ConfigPropertiesDemoApplication {
public static void main(String[] args) {
- new SpringApplicationBuilder(ConfigPropertiesDemoApplication.class).initializers(new JsonPropertyContextInitializer())
- .run();
+ SpringApplication.run(ConfigPropertiesDemoApplication.class, args);
}
}
diff --git a/spring-boot-modules/spring-boot-properties/src/main/resources/values.properties b/spring-boot-modules/spring-boot-properties/src/main/resources/values.properties
deleted file mode 100644
index c6db5873fb..0000000000
--- a/spring-boot-modules/spring-boot-properties/src/main/resources/values.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-value.from.file=Value got from the file
-priority=Properties file
-listOfValues=A,B,C
-valuesMap={key1:'1', key2 : '2', key3 : '3'}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-springdoc/pom.xml b/spring-boot-modules/spring-boot-springdoc/pom.xml
index 1c1f27b5a5..4bede8c796 100644
--- a/spring-boot-modules/spring-boot-springdoc/pom.xml
+++ b/spring-boot-modules/spring-boot-springdoc/pom.xml
@@ -66,6 +66,22 @@
spring-restdocs-restassured
test
+
+
+
+ org.springdoc
+ springdoc-openapi-kotlin
+ ${springdoc.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jre8
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+
@@ -97,6 +113,41 @@
+
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+ ${kotlin.version}
+
+
+ spring
+
+ ${java.version}
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-allopen
+ ${kotlin.version}
+
+
+
@@ -116,6 +167,7 @@
5.2.10.Final
1.2.32
1.5.6
+ 1.2.71
${project.build.directory}/generated-snippets
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/BookController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/BookController.java
index 05f8c5a946..326a97149b 100644
--- a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/BookController.java
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/controller/BookController.java
@@ -25,6 +25,13 @@ import com.baeldung.springdoc.exception.BookNotFoundException;
import com.baeldung.springdoc.model.Book;
import com.baeldung.springdoc.repository.BookRepository;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+
@RestController
@RequestMapping("/api/book")
public class BookController {
@@ -32,8 +39,15 @@ public class BookController {
@Autowired
private BookRepository repository;
+ // @formatter:off
+ @Operation(summary = "Get a book by its id")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Found the book",
+ content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Book.class)) }),
+ @ApiResponse(responseCode = "400", description = "Invalid id supplied", content = @Content),
+ @ApiResponse(responseCode = "404", description = "Book not found", content = @Content) }) // @formatter:on
@GetMapping("/{id}")
- public Book findById(@PathVariable long id) {
+ public Book findById(@Parameter(description = "id of book to be searched") @PathVariable long id) {
return repository.findById(id)
.orElseThrow(() -> new BookNotFoundException());
}
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/Foo.kt b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/Foo.kt
new file mode 100644
index 0000000000..3bc3c8fe61
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/Foo.kt
@@ -0,0 +1,17 @@
+package com.baeldung.springdoc.kotlin
+
+import javax.persistence.Entity
+import javax.persistence.GeneratedValue
+import javax.persistence.Id
+import javax.validation.constraints.NotBlank
+import javax.validation.constraints.Size
+
+@Entity
+data class Foo(
+ @Id
+ val id: Long = 0,
+
+ @NotBlank
+ @Size(min = 0, max = 50)
+ val name: String = ""
+)
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/FooController.kt b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/FooController.kt
new file mode 100644
index 0000000000..d3ecd6a6ba
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/springdoc/kotlin/FooController.kt
@@ -0,0 +1,30 @@
+package com.baeldung.springdoc.kotlin
+
+import io.swagger.v3.oas.annotations.Operation
+import io.swagger.v3.oas.annotations.media.ArraySchema
+import io.swagger.v3.oas.annotations.media.Content
+import io.swagger.v3.oas.annotations.media.Schema
+import io.swagger.v3.oas.annotations.responses.ApiResponse
+import io.swagger.v3.oas.annotations.responses.ApiResponses
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/")
+class FooController() {
+ val fooList: List = listOf(Foo(1, "one"), Foo(2, "two"))
+
+ @Operation(summary = "Get all foos")
+ @ApiResponses(value = [
+ ApiResponse(responseCode = "200", description = "Found Foos", content = [
+ (Content(mediaType = "application/json", array = (
+ ArraySchema(schema = Schema(implementation = Foo::class)))))]),
+ ApiResponse(responseCode = "400", description = "Bad request", content = [Content()]),
+ ApiResponse(responseCode = "404", description = "Did not find any Foos", content = [Content()])]
+ )
+ @GetMapping("/foo")
+ fun getAllFoos(): List = fooList
+}
+
+
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/resources/application.properties b/spring-boot-modules/spring-boot-springdoc/src/main/resources/application.properties
index 4191d8ba25..0eecfbb1c4 100644
--- a/spring-boot-modules/spring-boot-springdoc/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/resources/application.properties
@@ -1,5 +1,6 @@
# custom path for swagger-ui
springdoc.swagger-ui.path=/swagger-ui-custom.html
+springdoc.swagger-ui.operationsSorter=method
# custom path for api docs
springdoc.api-docs.path=/api-docs
@@ -7,5 +8,7 @@ springdoc.api-docs.path=/api-docs
# H2 Related Configurations
spring.datasource.url=jdbc:h2:mem:springdoc
+## for com.baeldung.restdocopenapi ##
springdoc.version=@springdoc.version@
-spring.jpa.hibernate.ddl-auto=none
\ No newline at end of file
+spring.jpa.hibernate.ddl-auto=none
+######################################
\ No newline at end of file
diff --git a/spring-data-rest-querydsl/pom.xml b/spring-data-rest-querydsl/pom.xml
index c0ad43fe0b..5e47f4979e 100644
--- a/spring-data-rest-querydsl/pom.xml
+++ b/spring-data-rest-querydsl/pom.xml
@@ -9,9 +9,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
diff --git a/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/AddressRepository.java b/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/AddressRepository.java
index 2e88820c98..476a11ea6b 100644
--- a/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/AddressRepository.java
+++ b/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/AddressRepository.java
@@ -5,13 +5,13 @@ import com.baeldung.entity.QAddress;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.core.types.dsl.StringPath;
import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.SingleValueBinding;
public interface AddressRepository
- extends JpaRepository, QueryDslPredicateExecutor, QuerydslBinderCustomizer {
+ extends JpaRepository, QuerydslPredicateExecutor, QuerydslBinderCustomizer {
@Override
default void customize(final QuerydslBindings bindings, final QAddress root) {
bindings.bind(String.class).first((SingleValueBinding) StringExpression::eq);
diff --git a/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/UserRepository.java b/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/UserRepository.java
index 98ff2ac5e3..b5f32b3624 100644
--- a/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/UserRepository.java
+++ b/spring-data-rest-querydsl/src/main/java/com/baeldung/controller/repository/UserRepository.java
@@ -5,13 +5,13 @@ import com.baeldung.entity.User;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.core.types.dsl.StringPath;
import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.querydsl.QueryDslPredicateExecutor;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.SingleValueBinding;
public interface UserRepository
- extends JpaRepository, QueryDslPredicateExecutor, QuerydslBinderCustomizer {
+ extends JpaRepository, QuerydslPredicateExecutor, QuerydslBinderCustomizer {
@Override
default void customize(final QuerydslBindings bindings, final QUser root) {
bindings.bind(String.class).first((SingleValueBinding) StringExpression::eq);
diff --git a/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/IntegrationTest.java b/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/IntegrationTest.java
index 2d3dbc4c74..ad19c441b6 100644
--- a/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/IntegrationTest.java
+++ b/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/IntegrationTest.java
@@ -13,8 +13,6 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-import java.nio.charset.Charset;
-
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -24,7 +22,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class IntegrationTest {
final MediaType contentType =
- new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
+ new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype());
@Autowired private WebApplicationContext webApplicationContext;
diff --git a/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/QuerydslIntegrationTest.java b/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/QuerydslIntegrationTest.java
index 11e5ffca05..768d28347b 100644
--- a/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/QuerydslIntegrationTest.java
+++ b/spring-data-rest-querydsl/src/test/java/com/baeldung/springdatarestquerydsl/QuerydslIntegrationTest.java
@@ -13,8 +13,6 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-import java.nio.charset.Charset;
-
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -26,7 +24,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class QuerydslIntegrationTest {
final MediaType contentType =
- new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
+ new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype());
@Autowired
private WebApplicationContext webApplicationContext;
diff --git a/spring-jenkins-pipeline/pom.xml b/spring-jenkins-pipeline/pom.xml
index aa6008162c..38d4ed15de 100644
--- a/spring-jenkins-pipeline/pom.xml
+++ b/spring-jenkins-pipeline/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
diff --git a/spring-jenkins-pipeline/src/test/java/com/baeldung/SomeIntegrationTest.java b/spring-jenkins-pipeline/src/test/java/com/baeldung/SomeIntegrationTest.java
index 477a7d2adb..9033d10c5d 100644
--- a/spring-jenkins-pipeline/src/test/java/com/baeldung/SomeIntegrationTest.java
+++ b/spring-jenkins-pipeline/src/test/java/com/baeldung/SomeIntegrationTest.java
@@ -13,7 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertNotEquals;
@RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(classes = {SpringJenkinsPipelineApplication.class, TestMongoConfig.class })
+@SpringBootTest(classes = {SpringJenkinsPipelineApplication.class})
public class SomeIntegrationTest {
@Autowired
private StudentRepository studentRepository;
diff --git a/spring-jenkins-pipeline/src/test/java/com/baeldung/TestMongoConfig.java b/spring-jenkins-pipeline/src/test/java/com/baeldung/TestMongoConfig.java
deleted file mode 100644
index a85491cf7e..0000000000
--- a/spring-jenkins-pipeline/src/test/java/com/baeldung/TestMongoConfig.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.baeldung;
-
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
-public class TestMongoConfig {
-
-}
\ No newline at end of file
diff --git a/spring-jinq/pom.xml b/spring-jinq/pom.xml
index 29fc3605d7..073808823c 100644
--- a/spring-jinq/pom.xml
+++ b/spring-jinq/pom.xml
@@ -9,9 +9,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
@@ -31,6 +31,17 @@
org.hibernate
hibernate-entitymanager
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ net.bytebuddy
+ byte-buddy-dep
+ ${bytebuddy.version}
+
@@ -61,7 +72,8 @@
- 1.8.22
+ 1.8.29
+ 1.10.10
diff --git a/spring-jinq/src/main/resources/application.properties b/spring-jinq/src/main/resources/application.properties
index dc73bed0c5..c9440b3b45 100644
--- a/spring-jinq/src/main/resources/application.properties
+++ b/spring-jinq/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-spring.datasource.url=jdbc:h2:~/jinq
+spring.datasource.url=jdbc:h2:~/jinq;;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
diff --git a/spring-mvc-webflow/pom.xml b/spring-mvc-webflow/pom.xml
index 5a6856385c..22ae3c913d 100644
--- a/spring-mvc-webflow/pom.xml
+++ b/spring-mvc-webflow/pom.xml
@@ -72,6 +72,24 @@
+
+ org.apache.tomee.maven
+ tomee-maven-plugin
+ 8.0.1
+
+ 8080
+ spring-mvc-webflow
+ true
+ plume
+
+
+ .class
+
+
+ -Xmx2048m -XX:PermSize=256m -Dtomee.serialization.class.blacklist=- -Dtomee.serialization.class.whitelist=*
+ true
+
+
org.apache.maven.plugins
maven-war-plugin
diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 7ce33dd3e3..60a662781f 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -15,6 +15,7 @@
spring-security-acl
+ spring-security-auth0
spring-security-angular/server
spring-security-cache-control
spring-security-core
diff --git a/spring-security-modules/spring-security-auth0/pom.xml b/spring-security-modules/spring-security-auth0/pom.xml
new file mode 100644
index 0000000000..0bd879a40b
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+ spring-security-auth0
+ 1.0-SNAPSHOT
+ spring-security-auth0
+ war
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-core
+
+
+ org.springframework.security
+ spring-security-oauth2-resource-server
+
+
+ com.auth0
+ mvc-auth-commons
+ ${mvc-auth-commons.version}
+
+
+ org.json
+ json
+ ${json.version}
+
+
+
+
+ spring-security-auth0
+
+
+ src/main/resources
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
+ 20190722
+ 1.2.0
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java
new file mode 100644
index 0000000000..42f8d946b5
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung.auth0;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java
new file mode 100644
index 0000000000..69cf8b3071
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java
@@ -0,0 +1,114 @@
+package com.baeldung.auth0;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+import com.auth0.AuthenticationController;
+import com.baeldung.auth0.controller.LogoutController;
+import com.auth0.jwk.JwkProvider;
+import com.auth0.jwk.JwkProviderBuilder;
+
+@Configuration
+@EnableWebSecurity
+public class AuthConfig extends WebSecurityConfigurerAdapter {
+
+ @Value(value = "${com.auth0.domain}")
+ private String domain;
+
+ @Value(value = "${com.auth0.clientId}")
+ private String clientId;
+
+ @Value(value = "${com.auth0.clientSecret}")
+ private String clientSecret;
+
+ @Value(value = "${com.auth0.managementApi.clientId}")
+ private String managementApiClientId;
+
+ @Value(value = "${com.auth0.managementApi.clientSecret}")
+ private String managementApiClientSecret;
+
+ @Value(value = "${com.auth0.managementApi.grantType}")
+ private String grantType;
+
+ @Bean
+ public LogoutSuccessHandler logoutSuccessHandler() {
+ return new LogoutController();
+ }
+
+ @Bean
+ public AuthenticationController authenticationController() throws UnsupportedEncodingException {
+ JwkProvider jwkProvider = new JwkProviderBuilder(domain).build();
+ return AuthenticationController.newBuilder(domain, clientId, clientSecret)
+ .withJwkProvider(jwkProvider)
+ .build();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf().disable();
+ http
+ .authorizeRequests()
+ .antMatchers("/callback", "/login", "/").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .and()
+ .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public String getManagementApiClientId() {
+ return managementApiClientId;
+ }
+
+ public String getManagementApiClientSecret() {
+ return managementApiClientSecret;
+ }
+
+ public String getGrantType() {
+ return grantType;
+ }
+
+ public String getUserInfoUrl() {
+ return "https://" + getDomain() + "/userinfo";
+ }
+
+ public String getUsersUrl() {
+ return "https://" + getDomain() + "/api/v2/users";
+ }
+
+ public String getUsersByEmailUrl() {
+ return "https://" + getDomain() + "/api/v2/users-by-email?email=";
+ }
+
+ public String getLogoutUrl() {
+ return "https://" + getDomain() +"/v2/logout";
+ }
+
+ public String getContextPath(HttpServletRequest request) {
+ String path = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
+ return path;
+ }
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java
new file mode 100644
index 0000000000..48d09db155
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java
@@ -0,0 +1,77 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.client.RestTemplate;
+
+import com.auth0.AuthenticationController;
+import com.auth0.IdentityVerificationException;
+import com.auth0.Tokens;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.baeldung.auth0.AuthConfig;
+
+@Controller
+public class AuthController {
+
+ @Autowired
+ private AuthenticationController authenticationController;
+
+ @Autowired
+ private AuthConfig config;
+
+ private static final String AUTH0_TOKEN_URL = "https://dev-example.auth0.com/oauth/token";
+
+ @GetMapping(value = "/login")
+ protected void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String redirectUri = config.getContextPath(request) + "/callback";
+ String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri)
+ .withScope("openid email")
+ .build();
+ response.sendRedirect(authorizeUrl);
+ }
+
+ @GetMapping(value="/callback")
+ public void callback(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException {
+ Tokens tokens = authenticationController.handle(request, response);
+
+ DecodedJWT jwt = JWT.decode(tokens.getIdToken());
+ TestingAuthenticationToken authToken2 = new TestingAuthenticationToken(jwt.getSubject(), jwt.getToken());
+ authToken2.setAuthenticated(true);
+
+ SecurityContextHolder.getContext().setAuthentication(authToken2);
+ response.sendRedirect(config.getContextPath(request) + "/");
+ }
+
+ public String getManagementApiToken() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+
+ JSONObject requestBody = new JSONObject();
+ requestBody.put("client_id", config.getManagementApiClientId());
+ requestBody.put("client_secret", config.getManagementApiClientSecret());
+ requestBody.put("audience", "https://dev-example.auth0.com/api/v2/");
+ requestBody.put("grant_type", config.getGrantType());
+
+ HttpEntity request = new HttpEntity(requestBody.toString(), headers);
+
+ RestTemplate restTemplate = new RestTemplate();
+ HashMap result = restTemplate.postForObject(AUTH0_TOKEN_URL, request, HashMap.class);
+
+ return result.get("access_token");
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java
new file mode 100644
index 0000000000..8a4e650846
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java
@@ -0,0 +1,37 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
+
+@Controller
+public class HomeController {
+
+ @GetMapping(value = "/")
+ @ResponseBody
+ public String home(HttpServletRequest request, HttpServletResponse response, final Authentication authentication) throws IOException {
+
+ if (authentication!= null && authentication instanceof TestingAuthenticationToken) {
+ TestingAuthenticationToken token = (TestingAuthenticationToken) authentication;
+
+ DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
+ String email = jwt.getClaims().get("email").asString();
+
+ return "Welcome, " + email + "!";
+ } else {
+ response.sendRedirect("http://localhost:8080/login");
+ return null;
+ }
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java
new file mode 100755
index 0000000000..d508fe2c44
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java
@@ -0,0 +1,35 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.stereotype.Controller;
+
+import com.baeldung.auth0.AuthConfig;
+
+@Controller
+public class LogoutController implements LogoutSuccessHandler {
+
+ @Autowired
+ private AuthConfig config;
+
+ @Override
+ public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authentication) {
+ if (req.getSession() != null) {
+ req.getSession().invalidate();
+ }
+ String returnTo = config.getContextPath(req);
+ String logoutUrl = config.getLogoutUrl() + "?client_id=" + config.getClientId() + "&returnTo=" +returnTo;
+ try {
+ res.sendRedirect(logoutUrl);
+ } catch(IOException e){
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java
new file mode 100644
index 0000000000..86601a06d3
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java
@@ -0,0 +1,57 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.auth0.IdentityVerificationException;
+import com.baeldung.auth0.AuthConfig;
+import com.baeldung.auth0.service.ApiService;
+
+@Controller
+public class UserController {
+
+ @Autowired
+ private ApiService apiService;
+
+ @Autowired
+ private AuthConfig config;
+
+ @GetMapping(value="/users")
+ @ResponseBody
+ public ResponseEntity users(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException {
+ ResponseEntity result = apiService.getCall(config.getUsersUrl());
+ return result;
+ }
+
+ @GetMapping(value = "/userByEmail")
+ @ResponseBody
+ public ResponseEntity userByEmail(HttpServletResponse response, @RequestParam String email) {
+ ResponseEntity result = apiService.getCall(config.getUsersByEmailUrl()+email);
+ return result;
+ }
+
+ @GetMapping(value = "/createUser")
+ @ResponseBody
+ public ResponseEntity createUser(HttpServletResponse response) {
+ JSONObject request = new JSONObject();
+ request.put("email", "norman.lewis@email.com");
+ request.put("given_name", "Norman");
+ request.put("family_name", "Lewis");
+ request.put("connection", "Username-Password-Authentication");
+ request.put("password", "Pa33w0rd");
+
+ ResponseEntity result = apiService.postCall(config.getUsersUrl(), request.toString());
+ return result;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java
new file mode 100644
index 0000000000..0d8263ae19
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java
@@ -0,0 +1,44 @@
+package com.baeldung.auth0.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import com.baeldung.auth0.controller.AuthController;
+
+@Service
+public class ApiService {
+
+ @Autowired
+ private AuthController controller;
+
+ public ResponseEntity getCall(String url) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer "+controller.getManagementApiToken());
+
+ HttpEntity entity = new HttpEntity(headers);
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity result = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
+
+ return result;
+ }
+
+ public ResponseEntity postCall(String url, String requestBody) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer "+controller.getManagementApiToken());
+
+ HttpEntity request = new HttpEntity(requestBody, headers);
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity result = restTemplate.postForEntity(url, request, String.class);
+
+ return result;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/resources/application.properties b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties
new file mode 100644
index 0000000000..45492c5c00
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+com.auth0.domain: dev-example.auth0.com
+com.auth0.clientId: exampleClientId
+com.auth0.clientSecret: exampleClientSecret
+
+com.auth0.managementApi.clientId: exampleManagementApiClientId
+com.auth0.managementApi.clientSecret: exampleManagementApiClientSecret
+com.auth0.managementApi.grantType: client_credentials
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-mvc-boot-1/pom.xml b/spring-security-modules/spring-security-mvc-boot-1/pom.xml
index 7ad18376ec..b00b7bab32 100644
--- a/spring-security-modules/spring-security-mvc-boot-1/pom.xml
+++ b/spring-security-modules/spring-security-mvc-boot-1/pom.xml
@@ -106,6 +106,10 @@
${ehcache-core.version}
jar
+
+ net.bytebuddy
+ byte-buddy
+
diff --git a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterApplication.java b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterApplication.java
index d3e0652ae9..148f9c17b1 100644
--- a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterApplication.java
+++ b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterApplication.java
@@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
-@ComponentScan(basePackages = {"com.baeldung.voter"})
+@ComponentScan(basePackages = {"com.baeldung.roles.voter"})
public class VoterApplication {
public static void main(String[] args) {
diff --git a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterMvcConfig.java b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterMvcConfig.java
index f11a4ae06c..402065129f 100644
--- a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterMvcConfig.java
+++ b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/VoterMvcConfig.java
@@ -12,6 +12,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class VoterMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
- registry.addViewController("/").setViewName("private");
+ registry.addViewController("/private").setViewName("private");
}
}
diff --git a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/WebSecurityConfig.java b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/WebSecurityConfig.java
index 8a0f438b49..1a6d1b8235 100644
--- a/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/WebSecurityConfig.java
+++ b/spring-security-modules/spring-security-mvc-boot-1/src/main/java/com/baeldung/roles/voter/WebSecurityConfig.java
@@ -34,7 +34,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter: off
http
// needed so our login could work
- .csrf().disable().authorizeRequests().anyRequest().authenticated().accessDecisionManager(accessDecisionManager()).antMatchers("/").hasAnyRole("ROLE_ADMIN", "ROLE_USER").and().formLogin().permitAll().and().logout().permitAll()
+ .csrf().disable().authorizeRequests().anyRequest().authenticated().accessDecisionManager(accessDecisionManager()).and().formLogin().permitAll().and().logout().permitAll()
.deleteCookies("JSESSIONID").logoutSuccessUrl("/login");
// @formatter: on
}
diff --git a/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java b/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
index 02bf8a9ee0..c1e3cf7458 100644
--- a/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
+++ b/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
@@ -27,7 +27,7 @@ public class CurrenciesControllerIntegrationTest {
.header("Accept-Language", "es-ES")
.param("amount", "10032.5"))
.andExpect(status().isOk())
- .andExpect(content().string(containsString("10.032,50 €")));
+ .andExpect(content().string(containsString("10.032,50")));
}
@Test
@@ -42,10 +42,10 @@ public class CurrenciesControllerIntegrationTest {
@Test
public void whenCallCurrencyWithRomanianLocaleWithArrays_ThenReturnLocaleCurrencies() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/currency")
- .header("Accept-Language", "ro-RO")
+ .header("Accept-Language", "en-GB")
.param("amountList", "10", "20", "30"))
.andExpect(status().isOk())
- .andExpect(content().string(containsString("10,00 RON, 20,00 RON, 30,00 RON")));
+ .andExpect(content().string(containsString("£10.00, £20.00, £30.00")));
}
@Test
diff --git a/testing-modules/testing-libraries/src/test/java/com/baeldung/mutation/PalindromeUnitTest.java b/testing-modules/testing-libraries/src/test/java/com/baeldung/mutation/PalindromeUnitTest.java
index cb4830a6fb..207077158e 100644
--- a/testing-modules/testing-libraries/src/test/java/com/baeldung/mutation/PalindromeUnitTest.java
+++ b/testing-modules/testing-libraries/src/test/java/com/baeldung/mutation/PalindromeUnitTest.java
@@ -11,13 +11,13 @@ public class PalindromeUnitTest {
@Test
public void whenEmptyString_thanAccept() {
Palindrome palindromeTester = new Palindrome();
- assertTrue(palindromeTester.isPalindrome("noon"));
+ assertTrue(palindromeTester.isPalindrome(""));
}
@Test
- public void whenPalindrom_thanAccept() {
- Palindrome palindromeTester = new Palindrome();
- assertTrue(palindromeTester.isPalindrome("noon"));
+ public void whenPalindrom_thanAccept() {
+ Palindrome palindromeTester = new Palindrome();
+ assertTrue(palindromeTester.isPalindrome("noon"));
}
@Test
diff --git a/testing-modules/testng/pom.xml b/testing-modules/testng/pom.xml
index 601b152144..c4a1284b0e 100644
--- a/testing-modules/testng/pom.xml
+++ b/testing-modules/testng/pom.xml
@@ -42,7 +42,7 @@
- 6.10
+ 7.1.0
\ No newline at end of file