diff --git a/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java
new file mode 100644
index 0000000000..fbea87be30
--- /dev/null
+++ b/algorithms-miscellaneous-2/src/main/java/com/baeldung/algorithms/relativelyprime/RelativelyPrime.java
@@ -0,0 +1,45 @@
+package com.baeldung.algorithms.relativelyprime;
+
+import java.math.BigInteger;
+
+class RelativelyPrime {
+
+ static boolean iterativeRelativelyPrime(int a, int b) {
+ return iterativeGCD(a, b) == 1;
+ }
+
+ static boolean recursiveRelativelyPrime(int a, int b) {
+ return recursiveGCD(a, b) == 1;
+ }
+
+ static boolean bigIntegerRelativelyPrime(int a, int b) {
+ return BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).equals(BigInteger.ONE);
+ }
+
+ private static int iterativeGCD(int a, int b) {
+ int tmp;
+ while (b != 0) {
+ if (a < b) {
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+ tmp = b;
+ b = a % b;
+ a = tmp;
+ }
+ return a;
+ }
+
+ private static int recursiveGCD(int a, int b) {
+ if (b == 0) {
+ return a;
+ }
+ if (a < b) {
+ return recursiveGCD(b, a);
+ }
+ return recursiveGCD(b, a % b);
+ }
+
+
+}
diff --git a/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java b/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java
new file mode 100644
index 0000000000..84bb2620af
--- /dev/null
+++ b/algorithms-miscellaneous-2/src/test/java/com/baeldung/algorithms/relativelyprime/RelativelyPrimeUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.algorithms.relativelyprime;
+
+import org.junit.Test;
+
+import static com.baeldung.algorithms.relativelyprime.RelativelyPrime.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RelativelyPrimeUnitTest {
+
+ @Test
+ public void givenNonRelativelyPrimeNumbers_whenCheckingIteratively_shouldReturnFalse() {
+
+ boolean result = iterativeRelativelyPrime(45, 35);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void givenRelativelyPrimeNumbers_whenCheckingIteratively_shouldReturnTrue() {
+
+ boolean result = iterativeRelativelyPrime(500, 501);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void givenNonRelativelyPrimeNumbers_whenCheckingRecursively_shouldReturnFalse() {
+
+ boolean result = recursiveRelativelyPrime(45, 35);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void givenRelativelyPrimeNumbers_whenCheckingRecursively_shouldReturnTrue() {
+
+ boolean result = recursiveRelativelyPrime(500, 501);
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void givenNonRelativelyPrimeNumbers_whenCheckingUsingBigIntegers_shouldReturnFalse() {
+
+ boolean result = bigIntegerRelativelyPrime(45, 35);
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void givenRelativelyPrimeNumbers_whenCheckingBigIntegers_shouldReturnTrue() {
+
+ boolean result = bigIntegerRelativelyPrime(500, 501);
+ assertThat(result).isTrue();
+ }
+}
diff --git a/core-groovy/README.md b/core-groovy/README.md
index bac91bf028..606a317747 100644
--- a/core-groovy/README.md
+++ b/core-groovy/README.md
@@ -9,3 +9,5 @@
- [A Quick Guide to Iterating a Map in Groovy](https://www.baeldung.com/groovy-map-iterating)
- [An Introduction to Traits in Groovy](https://www.baeldung.com/groovy-traits)
- [Lists in Groovy](https://www.baeldung.com/groovy-lists)
+- [Converting a String to a Date in Groovy](https://www.baeldung.com/groovy-string-to-date)
+- [Guide to I/O in Groovy](https://www.baeldung.com/groovy-io)
diff --git a/core-java-11/README.md b/core-java-11/README.md
index 5e2c07178b..b09649f4f1 100644
--- a/core-java-11/README.md
+++ b/core-java-11/README.md
@@ -5,3 +5,4 @@
- [Java 11 String API Additions](https://www.baeldung.com/java-11-string-api)
- [Java 11 Nest Based Access Control](https://www.baeldung.com/java-nest-based-access-control)
- [Exploring the New HTTP Client in Java 9 and 11](https://www.baeldung.com/java-9-http-client)
+- [An Introduction to Epsilon GC: A No-Op Experimental Garbage Collector](https://www.baeldung.com/jvm-epsilon-gc-garbage-collector)
diff --git a/core-java-9/README.md b/core-java-9/README.md
index fb7f69dc42..a423f0cb14 100644
--- a/core-java-9/README.md
+++ b/core-java-9/README.md
@@ -27,3 +27,6 @@
- [Immutable Set in Java](https://www.baeldung.com/java-immutable-set)
- [Multi-Release Jar Files](https://www.baeldung.com/java-multi-release-jar)
- [Ahead of Time Compilation (AoT)](https://www.baeldung.com/ahead-of-time-compilation)
+- [Java 9 Process API Improvements](https://www.baeldung.com/java-9-process-api)
+- [Guide to java.lang.Process API](https://www.baeldung.com/java-process-api)
+
diff --git a/core-java-collections/README.md b/core-java-collections/README.md
index 80d4385c45..1e504ded65 100644
--- a/core-java-collections/README.md
+++ b/core-java-collections/README.md
@@ -33,3 +33,4 @@
- [Differences Between HashMap and Hashtable](https://www.baeldung.com/hashmap-hashtable-differences)
- [Java ArrayList vs Vector](https://www.baeldung.com/java-arraylist-vs-vector)
- [Defining a Char Stack in Java](https://www.baeldung.com/java-char-stack)
+- [Time Comparison of Arrays.sort(Object[]) and Arrays.sort(int[])](https://www.baeldung.com/arrays-sortobject-vs-sortint)
diff --git a/core-java-concurrency-basic/README.md b/core-java-concurrency-basic/README.md
index ad3de4a758..7d106095e7 100644
--- a/core-java-concurrency-basic/README.md
+++ b/core-java-concurrency-basic/README.md
@@ -16,3 +16,4 @@
- [Life Cycle of a Thread in Java](http://www.baeldung.com/java-thread-lifecycle)
- [Runnable vs. Callable in Java](http://www.baeldung.com/java-runnable-callable)
- [What is Thread-Safety and How to Achieve it](https://www.baeldung.com/java-thread-safety)
+- [How to Start a Thread in Java](https://www.baeldung.com/java-start-thread)
diff --git a/core-java-os/README.md b/core-java-os/README.md
index 90e1d29a58..5f5d373d9b 100644
--- a/core-java-os/README.md
+++ b/core-java-os/README.md
@@ -6,3 +6,5 @@ This module uses Java 9, so make sure to have the JDK 9 installed to run it.
### Relevant Articles:
- [Java 9 Process API Improvements](http://www.baeldung.com/java-9-process-api)
- [Guide to java.lang.Process API](https://www.baeldung.com/java-process-api)
+- [Guide to java.lang.ProcessBuilder API](https://www.baeldung.com/java-lang-processbuilder-api)
+
diff --git a/core-java/README.md b/core-java/README.md
index e1325ab29f..cbc9251b0b 100644
--- a/core-java/README.md
+++ b/core-java/README.md
@@ -50,3 +50,4 @@
- [Finding Leap Years in Java](https://www.baeldung.com/java-leap-year)
- [Java Bitwise Operators](https://www.baeldung.com/java-bitwise-operators)
- [Guide to Creating and Running a Jar File in Java](https://www.baeldung.com/java-create-jar)
+- [Making a JSON POST Request With HttpURLConnection](https://www.baeldung.com/httpurlconnection-post)
diff --git a/core-kotlin-2/README.md b/core-kotlin-2/README.md
index da2f7548d9..4ac1c2c7bb 100644
--- a/core-kotlin-2/README.md
+++ b/core-kotlin-2/README.md
@@ -3,4 +3,4 @@
- [Void Type in Kotlin](https://www.baeldung.com/kotlin-void-type)
- [How to use Kotlin Range Expressions](https://www.baeldung.com/kotlin-ranges)
- [Split a List into Parts in Kotlin](https://www.baeldung.com/kotlin-split-list-into-parts)
-
+- [String Comparison in Kotlin](https://www.baeldung.com/kotlin-string-comparison)
diff --git a/core-kotlin-2/pom.xml b/core-kotlin-2/pom.xml
index 81df3cee81..e329611593 100644
--- a/core-kotlin-2/pom.xml
+++ b/core-kotlin-2/pom.xml
@@ -13,4 +13,66 @@
../parent-kotlin
-
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+ 1.8
+
+
+
+
+
+
+ 1.2.71
+ 1.1.1
+ 5.2.0
+ 3.10.0
+
+
+
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt
new file mode 100644
index 0000000000..3151674d61
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/CharRange.kt
@@ -0,0 +1,13 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (ch in 'a'..'f') {
+ print(ch)
+ }
+ println()
+
+ for (ch in 'f' downTo 'a') {
+ print(ch)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt
new file mode 100644
index 0000000000..ef7adf06b5
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Color.kt
@@ -0,0 +1,21 @@
+package com.baeldung.range
+
+enum class Color(val rgb: Int) {
+ BLUE(0x0000FF),
+ GREEN(0x008000),
+ RED(0xFF0000),
+ MAGENTA(0xFF00FF),
+ YELLOW(0xFFFF00);
+}
+
+fun main(args: Array) {
+
+ println(Color.values().toList());
+ val red = Color.RED
+ val yellow = Color.YELLOW
+ val range = red..yellow
+
+ println(range.contains(Color.MAGENTA))
+ println(range.contains(Color.BLUE))
+ println(range.contains(Color.GREEN))
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt
new file mode 100644
index 0000000000..0e611b14cf
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Filter.kt
@@ -0,0 +1,18 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+ val r = 1..10
+
+ //Apply filter
+ val f = r.filter { it -> it % 2 == 0 }
+ println(f)
+
+ //Map
+ val m = r.map { it -> it * it }
+ println(m)
+
+ //Reduce
+ val rdc = r.reduce { a, b -> a + b }
+ println(rdc)
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt
new file mode 100644
index 0000000000..b82f5a8b9b
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/FirstLast.kt
@@ -0,0 +1,8 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ println((1..9).first)
+ println((1..9 step 2).step)
+ println((3..9).reversed().last)
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt
new file mode 100644
index 0000000000..19dcab89b2
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/OtherRangeFunctions.kt
@@ -0,0 +1,14 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ val r = 1..20
+ println(r.min())
+ println(r.max())
+ println(r.sum())
+ println(r.average())
+ println(r.count())
+
+ val repeated = listOf(1, 1, 2, 4, 4, 6, 10)
+ println(repeated.distinct())
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt
new file mode 100644
index 0000000000..c313181599
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Range.kt
@@ -0,0 +1,28 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (i in 1..9) {
+ print(i)
+ }
+ println()
+
+ for (i in 9 downTo 1) {
+ print(i)
+ }
+ println()
+
+ for (i in 1.rangeTo(9)) {
+ print(i)
+ }
+ println()
+
+ for (i in 9.downTo(1)) {
+ print(i)
+ }
+ println()
+
+ for (i in 1 until 9) {
+ print(i)
+ }
+}
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt
new file mode 100644
index 0000000000..875cf62200
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/ReverseRange.kt
@@ -0,0 +1,14 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ (1..9).reversed().forEach {
+ print(it)
+ }
+
+ println()
+
+ (1..9).reversed().step(3).forEach {
+ print(it)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt
new file mode 100644
index 0000000000..b9c5d48588
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/Step.kt
@@ -0,0 +1,15 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for(i in 1..9 step 2){
+ print(i)
+ }
+
+ println()
+
+ for (i in 9 downTo 1 step 2){
+ print(i)
+ }
+
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt b/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt
new file mode 100644
index 0000000000..2c116a286f
--- /dev/null
+++ b/core-kotlin-2/src/main/kotlin/com/baeldung/range/UntilRange.kt
@@ -0,0 +1,8 @@
+package com.baeldung.range
+
+fun main(args: Array) {
+
+ for (i in 1 until 9) {
+ print(i)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt
new file mode 100644
index 0000000000..0e23f508b6
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/CharRangeTest.kt
@@ -0,0 +1,17 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class CharRangeTest {
+
+ @Test
+ fun testCharRange() {
+ assertEquals(listOf('a', 'b', 'c'), ('a'..'c').toList())
+ }
+
+ @Test
+ fun testCharDownRange() {
+ assertEquals(listOf('c', 'b', 'a'), ('c'.downTo('a')).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt
new file mode 100644
index 0000000000..4ac3270fcc
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ColorTest.kt
@@ -0,0 +1,20 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class ColorTest {
+
+ @Test
+ fun testEnumRange() {
+
+ println(Color.values().toList());
+ val red = Color.RED
+ val yellow = Color.YELLOW
+ val range = red..yellow
+
+ assertTrue { range.contains(Color.MAGENTA) }
+ assertFalse { range.contains(Color.BLUE) }
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt
new file mode 100644
index 0000000000..d0e2df8860
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FilterTest.kt
@@ -0,0 +1,24 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class FilterTest {
+
+ val r = 1..10
+
+ @Test
+ fun filterTest() {
+ assertEquals(listOf(2, 4, 6, 8, 10), r.filter { it -> it % 2 == 0 }.toList())
+ }
+
+ @Test
+ fun mapTest() {
+ assertEquals(listOf(1, 4, 9, 16, 25, 36, 49, 64, 81, 100), r.map { it -> it * it }.toList())
+ }
+
+ @Test
+ fun reduceTest() {
+ assertEquals(55, r.reduce { a, b -> a + b })
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt
new file mode 100644
index 0000000000..ca797e9c9b
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/FirstLastTest.kt
@@ -0,0 +1,22 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class FirstLastTest {
+
+ @Test
+ fun testFirst() {
+ assertEquals(1, (1..9).first)
+ }
+
+ @Test
+ fun testLast() {
+ assertEquals(9, (1..9).last)
+ }
+
+ @Test
+ fun testStep() {
+ assertEquals(2, (1..9 step 2).step)
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt
new file mode 100644
index 0000000000..d2d36bbfae
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/OtherRangeFunctionsTest.kt
@@ -0,0 +1,40 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class OtherRangeFunctionsTest {
+
+ val r = 1..20
+ val repeated = listOf(1, 1, 2, 4, 4, 6, 10)
+
+ @Test
+ fun testMin() {
+ assertEquals(1, r.min())
+ }
+
+ @Test
+ fun testMax() {
+ assertEquals(20, r.max())
+ }
+
+ @Test
+ fun testSum() {
+ assertEquals(210, r.sum())
+ }
+
+ @Test
+ fun testAverage() {
+ assertEquals(10.5, r.average())
+ }
+
+ @Test
+ fun testCount() {
+ assertEquals(20, r.count())
+ }
+
+ @Test
+ fun testDistinct() {
+ assertEquals(listOf(1, 2, 4, 6, 10), repeated.distinct())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt
new file mode 100644
index 0000000000..48fa483924
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/RangeTest.kt
@@ -0,0 +1,22 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class RangeTest {
+
+ @Test
+ fun testRange() {
+ assertEquals(listOf(1,2,3), (1.rangeTo(3).toList()))
+ }
+
+ @Test
+ fun testDownTo(){
+ assertEquals(listOf(3,2,1), (3.downTo(1).toList()))
+ }
+
+ @Test
+ fun testUntil(){
+ assertEquals(listOf(1,2), (1.until(3).toList()))
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt
new file mode 100644
index 0000000000..7e1c7badb7
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/ReverseRangeTest.kt
@@ -0,0 +1,12 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class ReverseRangeTest {
+
+ @Test
+ fun reversedTest() {
+ assertEquals(listOf(9, 6, 3), (1..9).reversed().step(3).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt
new file mode 100644
index 0000000000..4570ceeb0a
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/StepTest.kt
@@ -0,0 +1,17 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class StepTest {
+
+ @Test
+ fun testStep() {
+ assertEquals(listOf(1, 3, 5, 7, 9), (1..9 step 2).toList())
+ }
+
+ @Test
+ fun testStepDown() {
+ assertEquals(listOf(9, 7, 5, 3, 1), (9 downTo 1 step 2).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt b/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt
new file mode 100644
index 0000000000..f941c7f1e6
--- /dev/null
+++ b/core-kotlin-2/src/test/kotlin/com/baeldung/range/UntilRangeTest.kt
@@ -0,0 +1,12 @@
+package com.baeldung.range
+
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class UntilRangeTest {
+
+ @Test
+ fun testUntil() {
+ assertEquals(listOf(1, 2, 3, 4), (1 until 5).toList())
+ }
+}
\ No newline at end of file
diff --git a/core-kotlin/README.md b/core-kotlin/README.md
index 95c57336b9..73a78eccff 100644
--- a/core-kotlin/README.md
+++ b/core-kotlin/README.md
@@ -55,3 +55,4 @@
- [Building DSLs in Kotlin](https://www.baeldung.com/kotlin-dsl)
- [Static Methods Behavior in Kotlin](https://www.baeldung.com/kotlin-static-methods)
- [Inline Functions in Kotlin](https://www.baeldung.com/kotlin-inline-functions)
+- [Delegation Pattern in Kotlin](https://www.baeldung.com/kotlin-delegation-pattern)
diff --git a/gson/README.md b/gson/README.md
index 665ccb552b..fec0506488 100644
--- a/gson/README.md
+++ b/gson/README.md
@@ -12,4 +12,5 @@
- [Working with Primitive Values in Gson](https://www.baeldung.com/java-gson-primitives)
- [Convert String to JsonObject with Gson](https://www.baeldung.com/gson-string-to-jsonobject)
- [Mapping Multiple JSON Fields to One Java Field](https://www.baeldung.com/json-multiple-fields-single-java-field)
+- [Serializing and Deserializing a List with Gson](https://www.baeldung.com/gson-list)
diff --git a/jackson-2/README.md b/jackson-2/README.md
index 7c14bcfd19..ec147f5fd9 100644
--- a/jackson-2/README.md
+++ b/jackson-2/README.md
@@ -6,4 +6,4 @@
The "REST With Spring" Classes: http://bit.ly/restwithspring
### Relevant Articles:
-- [Mapping Multiple JSON Fields to One Java Field](https://www.baeldung.com/json-multiple-fields-single-java-field)
\ No newline at end of file
+- [Mapping Multiple JSON Fields to a Single Java Field](https://www.baeldung.com/json-multiple-fields-single-java-field)
diff --git a/java-collections-maps-2/pom.xml b/java-collections-maps-2/pom.xml
new file mode 100644
index 0000000000..b025c5e32d
--- /dev/null
+++ b/java-collections-maps-2/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+ java-collections-maps-2
+ 0.1.0-SNAPSHOT
+ java-collections-maps-2
+ jar
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../parent-java
+
+
+
+
+ org.eclipse.collections
+ eclipse-collections
+ ${eclipse-collections.version}
+
+
+ net.sf.trove4j
+ trove4j
+ 3.0.2
+
+
+ it.unimi.dsi
+ fastutil
+ 8.1.0
+
+
+ colt
+ colt
+ 1.2.0
+
+
+
+
+ 8.2.0
+
+
+
\ No newline at end of file
diff --git a/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java b/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java
new file mode 100644
index 0000000000..d835950c68
--- /dev/null
+++ b/java-collections-maps-2/src/main/java/com/baeldung/map/PrimitiveMaps.java
@@ -0,0 +1,69 @@
+package com.baeldung.map;
+
+import cern.colt.map.AbstractIntDoubleMap;
+import cern.colt.map.OpenIntDoubleHashMap;
+import gnu.trove.map.TDoubleIntMap;
+import gnu.trove.map.hash.TDoubleIntHashMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanSortedMap;
+import it.unimi.dsi.fastutil.ints.Int2BooleanSortedMaps;
+import org.eclipse.collections.api.map.primitive.ImmutableIntIntMap;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.api.map.primitive.MutableObjectDoubleMap;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.ObjectDoubleMaps;
+
+public class PrimitiveMaps {
+
+ public static void main(String[] args) {
+
+ eclipseCollectionsMap();
+ troveMap();
+ coltMap();
+ fastutilMap();
+ }
+
+ private static void fastutilMap() {
+ Int2BooleanMap int2BooleanMap = new Int2BooleanOpenHashMap();
+ int2BooleanMap.put(1, true);
+ int2BooleanMap.put(7, false);
+ int2BooleanMap.put(4, true);
+
+ boolean value = int2BooleanMap.get(1);
+
+ Int2BooleanSortedMap int2BooleanSorted = Int2BooleanSortedMaps.EMPTY_MAP;
+ }
+
+ private static void coltMap() {
+ AbstractIntDoubleMap map = new OpenIntDoubleHashMap();
+ map.put(1, 4.5);
+ double value = map.get(1);
+ }
+
+ private static void eclipseCollectionsMap() {
+ MutableIntIntMap mutableIntIntMap = IntIntMaps.mutable.empty();
+ mutableIntIntMap.addToValue(1, 1);
+
+ ImmutableIntIntMap immutableIntIntMap = IntIntMaps.immutable.empty();
+
+ MutableObjectDoubleMap dObject = ObjectDoubleMaps.mutable.empty();
+ dObject.addToValue("price", 150.5);
+ dObject.addToValue("quality", 4.4);
+ dObject.addToValue("stability", 0.8);
+ }
+
+ private static void troveMap() {
+ double[] doubles = new double[] {1.2, 4.5, 0.3};
+ int[] ints = new int[] {1, 4, 0};
+
+ TDoubleIntMap doubleIntMap = new TDoubleIntHashMap(doubles, ints);
+
+ doubleIntMap.put(1.2, 22);
+ doubleIntMap.put(4.5, 16);
+
+ doubleIntMap.adjustValue(1.2, 1);
+ doubleIntMap.adjustValue(4.5, 4);
+ doubleIntMap.adjustValue(0.3, 7);
+ }
+}
diff --git a/java-streams/README.md b/java-streams/README.md
index b931c0d7d9..e294e5aee1 100644
--- a/java-streams/README.md
+++ b/java-streams/README.md
@@ -17,3 +17,4 @@
- [Java Stream Filter with Lambda Expression](https://www.baeldung.com/java-stream-filter-lambda)
- [Counting Matches on a Stream Filter](https://www.baeldung.com/java-stream-filter-count)
- [Java 8 Streams peek() API](https://www.baeldung.com/java-streams-peek-api)
+- [Working With Maps Using Streams](https://www.baeldung.com/java-maps-streams)
diff --git a/lombok/README.md b/lombok/README.md
index e3d08d4e26..4ff7ca7921 100644
--- a/lombok/README.md
+++ b/lombok/README.md
@@ -6,4 +6,5 @@
- [Lombok Builder with Default Value](https://www.baeldung.com/lombok-builder-default-value)
- [Lombok Builder with Custom Setter](https://www.baeldung.com/lombok-builder-custom-setter)
- [Setting up Lombok with Eclipse and Intellij](https://www.baeldung.com/lombok-ide)
+- [Using the @Singular Annotation with Lombok Builders](https://www.baeldung.com/lombok-builder-singular)
diff --git a/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java
new file mode 100644
index 0000000000..d7d95feb66
--- /dev/null
+++ b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Person.java
@@ -0,0 +1,25 @@
+package com.baeldung.lombok.builder.singular;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Getter
+@Builder
+public class Person {
+
+ private final String givenName;
+ private final String additionalName;
+ private final String familyName;
+
+ private final List tags;
+ @Singular private final List interests;
+ @Singular private final Set skills;
+ @Singular private final Map awards;
+}
diff --git a/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java
new file mode 100644
index 0000000000..8cf38e5f1a
--- /dev/null
+++ b/lombok/src/main/java/com/baeldung/lombok/builder/singular/Sea.java
@@ -0,0 +1,14 @@
+package com.baeldung.lombok.builder.singular;
+
+import java.util.List;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+@Getter
+@Builder
+public class Sea {
+
+ @Singular private final List grasses;
+ @Singular("oneFish") private final List fish;
+}
diff --git a/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java b/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java
new file mode 100644
index 0000000000..41d5b19df7
--- /dev/null
+++ b/lombok/src/test/java/com/baeldung/lombok/builder/singular/BuilderWithSingularSupportForCollectionsUnitTest.java
@@ -0,0 +1,192 @@
+package com.baeldung.lombok.builder.singular;
+
+import org.junit.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+public class BuilderWithSingularSupportForCollectionsUnitTest {
+
+ @Test
+ public void canAddMultipleElementsAsNewCollection() throws Exception {
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(Arrays.asList("fictional", "incidental"))
+ .build();
+
+ assertThat(person.getTags(), containsInAnyOrder("fictional", "incidental"));
+ }
+
+ @Test
+ public void canUpdateCollectionAfterBuildIfMutableCollectionPassedToBuilder() throws Exception {
+
+ List tags = new ArrayList();
+ tags.add("fictional");
+ tags.add("incidental");
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(tags)
+ .build();
+ person.getTags()
+ .clear();
+ person.getTags()
+ .add("non-fictional");
+ person.getTags()
+ .add("important");
+
+ assertThat(person.getTags(), containsInAnyOrder("non-fictional", "important"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void cannotUpdateCollectionAfterBuildIfImmutableCollectionPassedToBuilder() throws Exception {
+ List tags = Arrays.asList("fictional", "incidental");
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .tags(tags)
+ .build();
+ person.getTags()
+ .clear();
+ }
+
+ @Test
+ public void canAssignToSingularAnnotatedCollectionOneByOne() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("history")
+ .interest("sport")
+ .build();
+
+ assertThat(person.getInterests(), containsInAnyOrder("sport", "history"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void singularAnnotatedBuilderCreatesImmutableCollection() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("history")
+ .interest("sport")
+ .build();
+ person.getInterests()
+ .clear();
+ }
+
+ @Test
+ public void unpopulatedListsCreatedAsNullIfNotSingularButEmptyArrayIfSingular() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .build();
+ assertThat(person.getInterests(), hasSize(0));
+ assertThat(person.getSkills(), hasSize(0));
+ assertThat(person.getAwards()
+ .keySet(), hasSize(0));
+ assertThat(person.getTags(), is(nullValue()));
+ }
+
+ @Test
+ public void singularSupportsSetsToo() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .skill("singing")
+ .skill("dancing")
+ .build();
+ assertThat(person.getSkills(), contains("singing", "dancing"));
+ }
+
+ @Test
+ public void singularSetsAreLenientWithDuplicates() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .interest("singing")
+ .interest("singing")
+ .skill("singing")
+ .skill("singing")
+ .build();
+ assertThat(person.getInterests(), contains("singing", "singing"));
+ assertThat(person.getSkills(), contains("singing"));
+ }
+
+ @Test
+ public void singularSupportsMapsToo() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .award("Singer of the Year", LocalDate.now()
+ .minusYears(5))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(2))
+ .build();
+ assertThat(person.getAwards()
+ .keySet(), contains("Singer of the Year", "Best Dancer"));
+ assertThat(person.getAwards()
+ .get("Best Dancer"),
+ is(LocalDate.now()
+ .minusYears(2)));
+ }
+
+ @Test
+ public void singularIsLenientWithMapKeys() throws Exception {
+
+ Person person = Person.builder()
+ .givenName("Aaron")
+ .additionalName("A")
+ .familyName("Aardvark")
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(5))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(4))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(3))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(2))
+ .award("Best Dancer", LocalDate.now()
+ .minusYears(1))
+ .build();
+ assertThat(person.getAwards()
+ .keySet(), hasSize(1));
+ assertThat(person.getAwards()
+ .get("Best Dancer"),
+ is(LocalDate.now()
+ .minusYears(1)));
+ }
+
+ @Test
+ public void wordsWithNonStandardPlurals() throws Exception {
+ Sea sea = Sea.builder()
+ .grass("Dulse")
+ .grass("Kelp")
+ .oneFish("Cod")
+ .oneFish("Mackerel")
+ .build();
+ assertThat(sea.getGrasses(), contains("Dulse", "Kelp"));
+ assertThat(sea.getFish(), contains("Cod", "Mackerel"));
+ }
+
+}
diff --git a/maven/README.md b/maven/README.md
index 1352a2a10f..ca648ec203 100644
--- a/maven/README.md
+++ b/maven/README.md
@@ -15,3 +15,4 @@
- [Use the Latest Version of a Dependency in Maven](https://www.baeldung.com/maven-dependency-latest-version)
- [Multi-Module Project with Maven](https://www.baeldung.com/maven-multi-module)
- [Maven Enforcer Plugin](https://www.baeldung.com/maven-enforcer-plugin)
+- [Eclipse Error: web.xml is missing and failOnMissingWebXml is set to true](https://www.baeldung.com/eclipse-error-web-xml-missing)
diff --git a/patterns/README.md b/patterns/README.md
index 9a15cdff02..1c9a59ea21 100644
--- a/patterns/README.md
+++ b/patterns/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [A Guide to the Front Controller Pattern in Java](http://www.baeldung.com/java-front-controller-pattern)
- [Introduction to Intercepting Filter Pattern in Java](http://www.baeldung.com/intercepting-filter-pattern-in-java)
+- [Introduction to the Null Object Pattern](https://www.baeldung.com/java-null-object-pattern)
diff --git a/persistence-modules/core-java-persistence/README.md b/persistence-modules/core-java-persistence/README.md
index ca0ce81eef..26bd4bf00f 100644
--- a/persistence-modules/core-java-persistence/README.md
+++ b/persistence-modules/core-java-persistence/README.md
@@ -7,3 +7,4 @@
- [Batch Processing in JDBC](http://www.baeldung.com/jdbc-batch-processing)
- [Introduction to the JDBC RowSet Interface in Java](http://www.baeldung.com/java-jdbc-rowset)
- [A Simple Guide to Connection Pooling in Java](https://www.baeldung.com/java-connection-pooling)
+- [Guide to the JDBC ResultSet Interface](https://www.baeldung.com/jdbc-resultset)
diff --git a/persistence-modules/java-jpa/README.md b/persistence-modules/java-jpa/README.md
index 2c26581bab..2424999fb3 100644
--- a/persistence-modules/java-jpa/README.md
+++ b/persistence-modules/java-jpa/README.md
@@ -6,3 +6,4 @@
- [JPA Entity Graph](https://www.baeldung.com/jpa-entity-graph)
- [JPA 2.2 Support for Java 8 Date/Time Types](https://www.baeldung.com/jpa-java-time)
- [Converting Between LocalDate and SQL Date](https://www.baeldung.com/java-convert-localdate-sql-date)
+- [Combining JPA And/Or Criteria Predicates](https://www.baeldung.com/jpa-and-or-criteria-predicates)
diff --git a/persistence-modules/spring-boot-persistence-mongodb/README.md b/persistence-modules/spring-boot-persistence-mongodb/README.md
index f093d4baf0..40f9f40749 100644
--- a/persistence-modules/spring-boot-persistence-mongodb/README.md
+++ b/persistence-modules/spring-boot-persistence-mongodb/README.md
@@ -1,3 +1,4 @@
# Relevant Articles
- [Auto-Generated Field for MongoDB using Spring Boot](https://www.baeldung.com/spring-boot-mongodb-auto-generated-field)
+- [Spring Boot Integration Testing with Embedded MongoDB](http://www.baeldung.com/spring-boot-embedded-mongodb)
diff --git a/persistence-modules/spring-boot-persistence-mongodb/pom.xml b/persistence-modules/spring-boot-persistence-mongodb/pom.xml
index 86b93c7826..585e54bf57 100644
--- a/persistence-modules/spring-boot-persistence-mongodb/pom.xml
+++ b/persistence-modules/spring-boot-persistence-mongodb/pom.xml
@@ -25,6 +25,11 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ test
+
org.junit.jupiter
@@ -102,4 +107,4 @@
-
\ No newline at end of file
+
diff --git a/spring-boot/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
similarity index 100%
rename from spring-boot/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
rename to persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
diff --git a/spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
similarity index 90%
rename from spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
rename to persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
index 39127f62e9..954bae3684 100644
--- a/spring-boot/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
+++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/MongoDbSpringIntegrationTest.java
@@ -2,7 +2,6 @@ package com.baeldung.mongodb;
import static org.assertj.core.api.Assertions.assertThat;
-import org.baeldung.boot.Application;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -12,10 +11,11 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
+import com.baeldung.SpringBootPersistenceApplication;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
-@ContextConfiguration(classes = Application.class)
+@ContextConfiguration(classes = SpringBootPersistenceApplication.class)
@DataMongoTest
@ExtendWith(SpringExtension.class)
public class MongoDbSpringIntegrationTest {
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java
new file mode 100644
index 0000000000..bf09caf6ff
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/Car.java
@@ -0,0 +1,48 @@
+package com.baeldung.exists;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author paullatzelsperger
+ * @since 2019-03-20
+ */
+@Entity
+public class Car {
+
+ @Id
+ @GeneratedValue
+ private int id;
+ private Integer power;
+ private String model;
+
+ Car() {
+
+ }
+
+ public Car(int power, String model) {
+ this.power = power;
+ this.model = model;
+ }
+
+ public Integer getPower() {
+ return power;
+ }
+
+ public void setPower(Integer power) {
+ this.power = power;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public int getId() {
+ return id;
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java
new file mode 100644
index 0000000000..a54f19f4cd
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/main/java/com/baeldung/exists/CarRepository.java
@@ -0,0 +1,24 @@
+package com.baeldung.exists;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author paullatzelsperger
+ * @since 2019-03-20
+ */
+@Repository
+public interface CarRepository extends JpaRepository {
+
+ boolean existsCarByPower(int power);
+
+ boolean existsCarByModel(String model);
+
+ @Query("select case when count(c)> 0 then true else false end from Car c where c.model = :model")
+ boolean existsCarExactCustomQuery(@Param("model") String model);
+
+ @Query("select case when count(c)> 0 then true else false end from Car c where lower(c.model) like lower(:model)")
+ boolean existsCarLikeCustomQuery(@Param("model") String model);
+}
diff --git a/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..1633df35d6
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence/src/test/java/com/baeldung/exists/CarRepositoryIntegrationTest.java
@@ -0,0 +1,86 @@
+package com.baeldung.exists;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.ignoreCase;
+
+import org.junit.After;
+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.context.SpringBootTest;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class CarRepositoryIntegrationTest {
+
+ @Autowired
+ private CarRepository repository;
+ private int searchId;
+
+ @Before
+ public void setup() {
+ List cars = repository.saveAll(Arrays.asList(new Car(200, "BMW"), new Car(300, "Audi")));
+ searchId = cars.get(0).getId();
+ }
+
+ @After
+ public void teardown() {
+ repository.deleteAll();
+ }
+
+ @Test
+ public void whenIdIsCorrect_thenExistsShouldReturnTrue() {
+ assertThat(repository.existsById(searchId)).isTrue();
+ }
+
+ @Test
+ public void givenExample_whenExists_thenIsTrue() {
+ ExampleMatcher modelMatcher = ExampleMatcher.matching()
+ .withIgnorePaths("id") // must explicitly ignore -> PK
+ .withMatcher("model", ignoreCase());
+ Car probe = new Car();
+ probe.setModel("bmw");
+
+ Example example = Example.of(probe, modelMatcher);
+
+ assertThat(repository.exists(example)).isTrue();
+ }
+
+ @Test
+ public void givenPower_whenExists_thenIsFalse() {
+ assertThat(repository.existsCarByPower(200)).isTrue();
+ assertThat(repository.existsCarByPower(800)).isFalse();
+ }
+
+ @Test
+ public void existsByDerivedQuery_byModel() {
+ assertThat(repository.existsCarByModel("Audi")).isTrue();
+ assertThat(repository.existsCarByModel("audi")).isFalse();
+ assertThat(repository.existsCarByModel("AUDI")).isFalse();
+ assertThat(repository.existsCarByModel("")).isFalse();
+ }
+
+ @Test
+ public void givenModelName_whenExistsExact_thenIsTrue() {
+ assertThat(repository.existsCarExactCustomQuery("BMW")).isTrue();
+ assertThat(repository.existsCarExactCustomQuery("Bmw")).isFalse();
+ assertThat(repository.existsCarExactCustomQuery("bmw")).isFalse();
+ assertThat(repository.existsCarExactCustomQuery("")).isFalse();
+ }
+
+ @Test
+ public void givenModelName_whenExistsLike_thenIsTrue() {
+ assertThat(repository.existsCarLikeCustomQuery("BMW")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("Bmw")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("bmw")).isTrue();
+ assertThat(repository.existsCarLikeCustomQuery("")).isFalse();
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa/README.md b/persistence-modules/spring-data-jpa/README.md
index 9512ad336d..48c3180262 100644
--- a/persistence-modules/spring-data-jpa/README.md
+++ b/persistence-modules/spring-data-jpa/README.md
@@ -22,6 +22,7 @@
- [Spring Data JPA Query by Example](https://www.baeldung.com/spring-data-query-by-example)
- [DB Integration Tests with Spring Boot and Testcontainers](https://www.baeldung.com/spring-boot-testcontainers-integration-test)
- [Spring Data JPA @Modifying Annotation](https://www.baeldung.com/spring-data-jpa-modifying-annotation)
+- [Spring Data JPA Batch Inserts](https://www.baeldung.com/spring-data-jpa-batch-inserts)
### Eclipse Config
After importing the project into Eclipse, you may see the following error:
diff --git a/persistence-modules/spring-data-jpa/pom.xml b/persistence-modules/spring-data-jpa/pom.xml
index 401f4877ac..c512994931 100644
--- a/persistence-modules/spring-data-jpa/pom.xml
+++ b/persistence-modules/spring-data-jpa/pom.xml
@@ -53,6 +53,15 @@
spring-security-test
test
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
com.google.guava
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java
new file mode 100644
index 0000000000..7623d4d166
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/CustomerController.java
@@ -0,0 +1,43 @@
+package com.baeldung.batchinserts;
+
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.batchinserts.model.Customer;
+import com.baeldung.batchinserts.repository.CustomerRepository;
+
+/**
+ * A simple controller to test the JPA CrudRepository operations
+ * controllers
+ *
+ * @author ysharma2512
+ *
+ */
+@RestController
+public class CustomerController {
+
+ @Autowired
+ CustomerRepository customerRepository;
+
+ public CustomerController(CustomerRepository customerRepository2) {
+ this.customerRepository = customerRepository2;
+ }
+
+ @PostMapping("/customers")
+ public ResponseEntity> insertCustomers() throws URISyntaxException {
+ Customer c1 = new Customer("James", "Gosling");
+ Customer c2 = new Customer("Doug", "Lea");
+ Customer c3 = new Customer("Martin", "Fowler");
+ Customer c4 = new Customer("Brian", "Goetz");
+ List customers = Arrays.asList(c1, c2, c3, c4);
+ customerRepository.saveAll(customers);
+ return ResponseEntity.ok(customers);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java
new file mode 100644
index 0000000000..4d82cf12a2
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/model/Customer.java
@@ -0,0 +1,56 @@
+package com.baeldung.batchinserts.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+/**
+ * Customer Entity class
+ * @author ysharma2512
+ *
+ */
+@Entity
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String firstName;
+ private String lastName;
+
+ public Customer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java
new file mode 100644
index 0000000000..ab0214bade
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/main/java/com/baeldung/batchinserts/repository/CustomerRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.batchinserts.repository;
+
+import org.springframework.data.repository.CrudRepository;
+
+import com.baeldung.batchinserts.model.Customer;
+
+/**
+ * JPA CrudRepository interface
+ *
+ * @author ysharma2512
+ *
+ */
+public interface CustomerRepository extends CrudRepository{
+
+}
diff --git a/persistence-modules/spring-data-jpa/src/main/resources/application.properties b/persistence-modules/spring-data-jpa/src/main/resources/application.properties
index 37fb9ca9c4..239f81db7b 100644
--- a/persistence-modules/spring-data-jpa/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa/src/main/resources/application.properties
@@ -14,4 +14,9 @@ hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFa
spring.datasource.data=import_entities.sql
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
+spring.main.allow-bean-definition-overriding=true
+
+spring.jpa.properties.hibernate.jdbc.batch_size=4
+spring.jpa.properties.hibernate.order_inserts=true
+spring.jpa.properties.hibernate.order_updates=true
+spring.jpa.properties.hibernate.generate_statistics=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java b/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
new file mode 100644
index 0000000000..f60e0d21bf
--- /dev/null
+++ b/persistence-modules/spring-data-jpa/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.batchinserts;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+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.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import com.baeldung.batchinserts.CustomerController;
+import com.baeldung.batchinserts.repository.CustomerRepository;
+import com.baeldung.config.PersistenceConfiguration;
+import com.baeldung.config.PersistenceProductConfiguration;
+import com.baeldung.config.PersistenceUserConfiguration;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureMockMvc
+@ContextConfiguration(classes = { PersistenceConfiguration.class, PersistenceProductConfiguration.class, PersistenceUserConfiguration.class })
+public class BatchInsertIntegrationTest {
+
+ @Autowired
+ private CustomerRepository customerRepository;
+ private MockMvc mockMvc;
+ @Before
+ public void setUp() throws Exception {
+ mockMvc = MockMvcBuilders.standaloneSetup( new CustomerController(customerRepository))
+ .build();
+ }
+
+ @Test
+ public void whenInsertingCustomers_thenCustomersAreCreated() throws Exception {
+ this.mockMvc.perform(post("/customers"))
+ .andExpect(status().isOk());
+ }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7af92fbd11..615fc7b46e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -577,12 +577,12 @@
spring-4
spring-5
+ spring-5-webflux
spring-5-mvc
spring-5-reactive
spring-5-reactive-client
spring-5-reactive-oauth
spring-5-reactive-security
- spring-5-reactive-netty
spring-5-security
spring-5-security-oauth
diff --git a/reactor-core/README.md b/reactor-core/README.md
index 6f90e59894..5f9d2d1bc2 100644
--- a/reactor-core/README.md
+++ b/reactor-core/README.md
@@ -2,3 +2,4 @@
- [Intro To Reactor Core](http://www.baeldung.com/reactor-core)
- [Combining Publishers in Project Reactor](http://www.baeldung.com/reactor-combine-streams)
+- [Programmatically Creating Sequences with Project Reactor](https://www.baeldung.com/flux-sequences-reactor)
diff --git a/spring-5-reactive-netty/.gitignore b/spring-5-reactive-netty/.gitignore
deleted file mode 100644
index 70ed41e73a..0000000000
--- a/spring-5-reactive-netty/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-# Folders #
-**/.idea
-**/target
-
-# Files #
-*.log
-
-# Packaged files #
-*.jar
-*.war
-*.ear
\ No newline at end of file
diff --git a/spring-5-reactive-netty/README.md b/spring-5-reactive-netty/README.md
deleted file mode 100644
index 09f7cc0e24..0000000000
--- a/spring-5-reactive-netty/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Spring 5 Reactive Project With Netty Server
-
-Includes configuration options for Netty server.
diff --git a/spring-5-reactive-netty/pom.xml b/spring-5-reactive-netty/pom.xml
deleted file mode 100644
index 48fc0b201f..0000000000
--- a/spring-5-reactive-netty/pom.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
- 4.0.0
- com.baeldung
- spring-5-reactive-netty
- 0.0.1-SNAPSHOT
- spring-5-reactive-netty
- jar
- Spring 5 sample project about reactive web with Netty server
-
-
- com.baeldung
- parent-boot-2
- 0.0.1-SNAPSHOT
- ../parent-boot-2
-
-
-
-
- org.springframework.boot
- spring-boot-starter-webflux
-
-
-
- org.projectlombok
- lombok
-
-
-
- org.springframework.boot
- spring-boot-devtools
- runtime
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
diff --git a/spring-5-reactive/README.md b/spring-5-reactive/README.md
index ef2f7d07eb..538a15c879 100644
--- a/spring-5-reactive/README.md
+++ b/spring-5-reactive/README.md
@@ -19,3 +19,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Logging a Reactive Sequence](https://www.baeldung.com/spring-reactive-sequence-logging)
- [Testing Reactive Streams Using StepVerifier and TestPublisher](https://www.baeldung.com/reactive-streams-step-verifier-test-publisher)
- [Debugging Reactive Streams in Spring 5](https://www.baeldung.com/spring-debugging-reactive-streams)
+- [Static Content in Spring WebFlux](https://www.baeldung.com/spring-webflux-static-content)
diff --git a/spring-5-webflux/.gitignore b/spring-5-webflux/.gitignore
new file mode 100644
index 0000000000..aa4871eeea
--- /dev/null
+++ b/spring-5-webflux/.gitignore
@@ -0,0 +1,2 @@
+# Files #
+*.log
\ No newline at end of file
diff --git a/spring-5-webflux/pom.xml b/spring-5-webflux/pom.xml
index d7fb7b7930..3306fd1729 100644
--- a/spring-5-webflux/pom.xml
+++ b/spring-5-webflux/pom.xml
@@ -1,23 +1,20 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
com.baeldung
spring-5-webflux
1.0-SNAPSHOT
-
spring-5-webflux
-
http://www.baeldung.com
-
- UTF-8
- 1.8
- 1.8
- 2.0.2.RELEASE
-
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
@@ -58,12 +55,8 @@
- maven-compiler-plugin
- 3.8.0
-
-
- maven-surefire-plugin
- 2.22.1
+ org.springframework.boot
+ spring-boot-maven-plugin
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/CustomNettyWebServerFactory.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java
similarity index 97%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/CustomNettyWebServerFactory.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java
index 8a1cdbba97..f9de3b4006 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/CustomNettyWebServerFactory.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/CustomNettyWebServerFactory.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingController.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java
similarity index 94%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingController.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java
index 9cb5b27ac5..990ea5cf83 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingController.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingController.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingService.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java
similarity index 84%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingService.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java
index 5440f526aa..a6243b2bd0 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/GreetingService.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/GreetingService.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactoryPortCustomizer.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java
similarity index 95%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactoryPortCustomizer.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java
index 152e1285aa..fdde130286 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactoryPortCustomizer.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactoryPortCustomizer.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactorySslCustomizer.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java
similarity index 96%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactorySslCustomizer.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java
index d0ad0dcac5..cf4e5ac8ea 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/NettyWebServerFactorySslCustomizer.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/NettyWebServerFactorySslCustomizer.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.embedded.netty.SslServerCustomizer;
diff --git a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/ServerConfigApplication.java b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java
similarity index 87%
rename from spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/ServerConfigApplication.java
rename to spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java
index 9d420cc7da..b4f6006be3 100644
--- a/spring-5-reactive-netty/src/main/java/com/baeldung/serverconfig/ServerConfigApplication.java
+++ b/spring-5-webflux/src/main/java/com/baeldung/spring/serverconfig/ServerConfigApplication.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/spring-5-reactive-netty/src/main/resources/logback.xml b/spring-5-webflux/src/main/resources/logback.xml
similarity index 100%
rename from spring-5-reactive-netty/src/main/resources/logback.xml
rename to spring-5-webflux/src/main/resources/logback.xml
diff --git a/spring-5-reactive-netty/src/main/resources/sample.jks b/spring-5-webflux/src/main/resources/sample.jks
similarity index 100%
rename from spring-5-reactive-netty/src/main/resources/sample.jks
rename to spring-5-webflux/src/main/resources/sample.jks
diff --git a/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java b/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
similarity index 97%
rename from spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java
rename to spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
index 5112c8ceb2..4c6708e423 100644
--- a/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerTests.java
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/responsestatus/ResponseStatusControllerLiveTest.java
@@ -9,7 +9,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-public class ResponseStatusControllerTests {
+public class ResponseStatusControllerLiveTest {
@Autowired
private WebTestClient testClient;
diff --git a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingControllerIntegrationTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java
similarity index 96%
rename from spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingControllerIntegrationTest.java
rename to spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java
index 3c2c08321a..ce156beb3f 100644
--- a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingControllerIntegrationTest.java
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingControllerIntegrationTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import static org.mockito.Mockito.when;
diff --git a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingLiveTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java
similarity index 94%
rename from spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingLiveTest.java
rename to spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java
index 7c4a37c890..2400272c6e 100644
--- a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingLiveTest.java
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingLiveTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
@@ -10,7 +10,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
-import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
@@ -18,6 +18,7 @@ import reactor.netty.http.client.HttpClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@DirtiesContext
public class GreetingLiveTest {
private static final String BASE_URL = "https://localhost:8443";
diff --git a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingSkipAutoConfigLiveTest.java b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java
similarity index 79%
rename from spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingSkipAutoConfigLiveTest.java
rename to spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java
index 646742b3d7..45918dfd70 100644
--- a/spring-5-reactive-netty/src/test/java/com/baeldung/serverconfig/GreetingSkipAutoConfigLiveTest.java
+++ b/spring-5-webflux/src/test/java/com/baeldung/spring/serverconfig/GreetingSkipAutoConfigLiveTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.serverconfig;
+package com.baeldung.spring.serverconfig;
import org.springframework.test.context.ActiveProfiles;
diff --git a/spring-5-reactive-netty/src/test/resources/logback-test.xml b/spring-5-webflux/src/test/resources/logback-test.xml
similarity index 100%
rename from spring-5-reactive-netty/src/test/resources/logback-test.xml
rename to spring-5-webflux/src/test/resources/logback-test.xml
diff --git a/spring-boot-angular-ecommerce/src/main/frontend/package.json b/spring-boot-angular-ecommerce/src/main/frontend/package.json
index 958e9f1023..17363ef961 100644
--- a/spring-boot-angular-ecommerce/src/main/frontend/package.json
+++ b/spring-boot-angular-ecommerce/src/main/frontend/package.json
@@ -7,7 +7,7 @@
"build": "ng build",
"postbuild": "npm run deploy",
"predeploy": "rimraf ../resources/static/ && mkdirp ../resources/static",
- "deploy": "copyfiles -f dist/** ../resources/static",
+ "deploy": "copyfiles -f dist/frontend/** ../resources/static",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
diff --git a/spring-boot-data/pom.xml b/spring-boot-data/pom.xml
index 9ef4cc69c8..8735a54e7b 100644
--- a/spring-boot-data/pom.xml
+++ b/spring-boot-data/pom.xml
@@ -119,4 +119,4 @@
2.2.4
-
\ No newline at end of file
+
diff --git a/spring-boot-ops/README.md b/spring-boot-ops/README.md
index 553b02ebf0..d5063f14e5 100644
--- a/spring-boot-ops/README.md
+++ b/spring-boot-ops/README.md
@@ -10,3 +10,5 @@
- [Spring Boot Console Application](http://www.baeldung.com/spring-boot-console-app)
- [Comparing Embedded Servlet Containers in Spring Boot](http://www.baeldung.com/spring-boot-servlet-containers)
- [Programmatically Restarting a Spring Boot Application](https://www.baeldung.com/java-restart-spring-boot-app)
+ - [Spring Properties File Outside jar](https://www.baeldung.com/spring-properties-file-outside-jar)
+
diff --git a/spring-boot-rest/README.md b/spring-boot-rest/README.md
index 8fbc9527b8..af372077f0 100644
--- a/spring-boot-rest/README.md
+++ b/spring-boot-rest/README.md
@@ -10,3 +10,4 @@ Module for the articles that are part of the Spring REST E-book:
8. [Http Message Converters with the Spring Framework](http://www.baeldung.com/spring-httpmessageconverter-rest)
9. [ETags for REST with Spring](http://www.baeldung.com/etags-for-rest-with-spring)
10. [Testing REST with multiple MIME types](http://www.baeldung.com/testing-rest-api-with-multiple-media-types)
+11. [Testing Web APIs with Postman Collections](https://www.baeldung.com/postman-testing-collections)
diff --git a/spring-boot/README.MD b/spring-boot/README.MD
index 223e0959fc..7d270c9c25 100644
--- a/spring-boot/README.MD
+++ b/spring-boot/README.MD
@@ -28,7 +28,6 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Spring Boot Exit Codes](http://www.baeldung.com/spring-boot-exit-codes)
- [Guide to the Favicon in Spring Boot](http://www.baeldung.com/spring-boot-favicon)
- [Spring Shutdown Callbacks](http://www.baeldung.com/spring-shutdown-callbacks)
-- [Spring Boot Integration Testing with Embedded MongoDB](http://www.baeldung.com/spring-boot-embedded-mongodb)
- [Container Configuration in Spring Boot 2](http://www.baeldung.com/embeddedservletcontainercustomizer-configurableembeddedservletcontainer-spring-boot)
- [Introduction to Chaos Monkey](https://www.baeldung.com/spring-boot-chaos-monkey)
- [Spring Component Scanning](https://www.baeldung.com/spring-component-scanning)
@@ -37,3 +36,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Injecting Git Information Into Spring](https://www.baeldung.com/spring-git-information)
- [Validation in Spring Boot](https://www.baeldung.com/spring-boot-bean-validation)
- [Entity To DTO Conversion for a Spring REST API](https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application)
+- [Guide to Creating and Running a Jar File in Java](https://www.baeldung.com/java-create-jar)
diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml
index 2e463e0189..ed2d8259df 100644
--- a/spring-boot/pom.xml
+++ b/spring-boot/pom.xml
@@ -15,16 +15,7 @@
-
-
- org.springframework.boot
- spring-boot-starter-data-mongodb
-
-
- de.flapdoodle.embed
- de.flapdoodle.embed.mongo
- test
-
+
org.junit.jupiter
diff --git a/spring-cloud/README.md b/spring-cloud/README.md
index fede3cc12d..5139cdca20 100644
--- a/spring-cloud/README.md
+++ b/spring-cloud/README.md
@@ -17,4 +17,5 @@
- [Dockerizing a Spring Boot Application](http://www.baeldung.com/dockerizing-spring-boot-application)
- [Instance Profile Credentials using Spring Cloud](http://www.baeldung.com/spring-cloud-instance-profiles)
- [Running Spring Boot Applications With Minikube](http://www.baeldung.com/spring-boot-minikube)
+- [Introduction to Spring Cloud OpenFeign](https://www.baeldung.com/spring-cloud-openfeign)
diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml
index baf86a4386..bf70e0284c 100644
--- a/spring-cloud/pom.xml
+++ b/spring-cloud/pom.xml
@@ -32,6 +32,7 @@
spring-cloud-zuul-eureka-integration
spring-cloud-contract
spring-cloud-kubernetes
+ spring-cloud-kubernetes-2
spring-cloud-archaius
spring-cloud-functions
spring-cloud-vault
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/.gitignore b/spring-cloud/spring-cloud-kubernetes-2/client-service/.gitignore
new file mode 100644
index 0000000000..2af7cefb0a
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/.gitignore
@@ -0,0 +1,24 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/Dockerfile b/spring-cloud/spring-cloud-kubernetes-2/client-service/Dockerfile
new file mode 100644
index 0000000000..bd8cf2918f
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/Dockerfile
@@ -0,0 +1,5 @@
+FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY target/client-service-1.0-SNAPSHOT.jar app.jar
+ENV JAVA_OPTS=""
+ENTRYPOINT exec java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999 -jar /app.jar
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/client-config.yaml b/spring-cloud/spring-cloud-kubernetes-2/client-service/client-config.yaml
new file mode 100644
index 0000000000..329c01582e
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/client-config.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: client-service
+data:
+ application.properties: |-
+ bean.message=Testing reload ! Message from backend is: %s
Services : %s
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/client-service-deployment.yaml b/spring-cloud/spring-cloud-kubernetes-2/client-service/client-service-deployment.yaml
new file mode 100644
index 0000000000..8867aecc33
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/client-service-deployment.yaml
@@ -0,0 +1,33 @@
+kind: Service
+apiVersion: v1
+metadata:
+ name: client-service
+spec:
+ selector:
+ app: client-service
+ ports:
+ - protocol: TCP
+ port: 8080
+ nodePort: 30083
+ type: NodePort
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: client-service
+spec:
+ selector:
+ matchLabels:
+ app: client-service
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: client-service
+ spec:
+ containers:
+ - name: client-service
+ image: client-service:latest
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/pom.xml b/spring-cloud/spring-cloud-kubernetes-2/client-service/pom.xml
new file mode 100644
index 0000000000..908a545242
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/pom.xml
@@ -0,0 +1,96 @@
+
+
+ 4.0.0
+ client-service
+ client-service
+ 1.0-SNAPSHOT
+
+
+ com.baeldung.spring.cloud
+ spring-cloud-kubernetes-2
+ 1.0-SNAPSHOT
+
+
+
+ 1.8
+ Finchley.SR2
+ 1.0.0.RELEASE
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-kubernetes-dependencies
+ ${spring.cloud.k8s.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-kubernetes-discovery
+
+
+ org.springframework.cloud
+ spring-cloud-starter-kubernetes-config
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-hystrix
+
+
+ org.springframework.cloud
+ spring-cloud-starter-kubernetes-ribbon
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-ribbon
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.baeldung.spring.cloud.kubernetes.client.Application
+ JAR
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/Application.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/Application.java
new file mode 100644
index 0000000000..24e562c79b
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/Application.java
@@ -0,0 +1,27 @@
+package com.baeldung.spring.cloud.kubernetes.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.netflix.ribbon.RibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@EnableCircuitBreaker
+@RibbonClient(name = "travel-agency-service", configuration = RibbonConfiguration.class)
+public class Application {
+
+ @LoadBalanced
+ @Bean
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientConfig.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientConfig.java
new file mode 100644
index 0000000000..4bc8fbe327
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientConfig.java
@@ -0,0 +1,19 @@
+package com.baeldung.spring.cloud.kubernetes.client;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "bean")
+public class ClientConfig {
+
+ private String message = "Message from backend is: %s
Services : %s";
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientController.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientController.java
new file mode 100755
index 0000000000..952bccc3a8
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/ClientController.java
@@ -0,0 +1,54 @@
+package com.baeldung.spring.cloud.kubernetes.client;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.UnknownHostException;
+import java.util.List;
+
+@RestController
+public class ClientController {
+
+ @Autowired
+ private DiscoveryClient discoveryClient;
+
+ @Autowired
+ private ClientConfig config;
+
+ @Autowired
+ private TravelAgencyService travelAgencyService;
+
+ @RequestMapping("/deals")
+ public String getDeals() {
+ return travelAgencyService.getDeals();
+ }
+
+ @GetMapping
+ public String load() {
+
+ RestTemplate restTemplate = new RestTemplate();
+ String resourceUrl = "http://travel-agency-service:8080";
+ ResponseEntity response = restTemplate.getForEntity(resourceUrl, String.class);
+
+ String serviceList = "";
+ if (discoveryClient != null) {
+ List services = this.discoveryClient.getServices();
+
+ for (String service : services) {
+
+ List instances = this.discoveryClient.getInstances(service);
+
+ serviceList += ("[" + service + " : " + ((!CollectionUtils.isEmpty(instances)) ? instances.size() : 0) + " instances ]");
+ }
+ }
+
+ return String.format(config.getMessage(), response.getBody(), serviceList);
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/RibbonConfiguration.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/RibbonConfiguration.java
new file mode 100755
index 0000000000..2b81c78be4
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/RibbonConfiguration.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 to the original authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.baeldung.spring.cloud.kubernetes.client;
+
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.AvailabilityFilteringRule;
+import com.netflix.loadbalancer.IPing;
+import com.netflix.loadbalancer.IRule;
+import com.netflix.loadbalancer.PingUrl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+
+public class RibbonConfiguration {
+
+ @Autowired
+ IClientConfig ribbonClientConfig;
+
+ /**
+ * PingUrl will ping a URL to check the status of each server.
+ * Say Hello has, as you’ll recall, a method mapped to the /path; that means that Ribbon will get an HTTP 200 response when it pings a running Backend Server
+ *
+ * @param config Client configuration
+ * @return The URL to be used for the Ping
+ */
+ @Bean
+ public IPing ribbonPing(IClientConfig config) {
+ return new PingUrl();
+ }
+
+ /**
+ * AvailabilityFilteringRule will use Ribbon’s built-in circuit breaker functionality to filter out any servers in an “open-circuit” state:
+ * if a ping fails to connect to a given server, or if it gets a read failure for the server, Ribbon will consider that server “dead” until it begins to respond normally.
+ *
+ * @param config Client configuration
+ * @return The Load Balancer rule
+ */
+ @Bean
+ public IRule ribbonRule(IClientConfig config) {
+ return new AvailabilityFilteringRule();
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/TravelAgencyService.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/TravelAgencyService.java
new file mode 100644
index 0000000000..5ce6d7b8a1
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/java/com/baeldung/spring/cloud/kubernetes/client/TravelAgencyService.java
@@ -0,0 +1,26 @@
+package com.baeldung.spring.cloud.kubernetes.client;
+
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+@Service
+public class TravelAgencyService {
+
+ private final RestTemplate restTemplate;
+
+ public TravelAgencyService(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ @HystrixCommand(fallbackMethod = "getFallbackName", commandProperties = {
+ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") })
+ public String getDeals() {
+ return this.restTemplate.getForObject("http://travel-agency-service:8080/deals", String.class);
+ }
+
+ private String getFallbackName() {
+ return "Fallback";
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/application.yaml b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/application.yaml
new file mode 100644
index 0000000000..f966fdd7a5
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/application.yaml
@@ -0,0 +1,16 @@
+spring:
+ application.name: client-service
+ cloud.kubernetes.reload.enabled: true
+server.port: 8080
+management:
+ endpoint:
+ restart:
+ enabled: true
+ health:
+ enabled: true
+ info:
+ enabled: true
+ribbon:
+ http:
+ client:
+ enabled: true
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/logback.xml b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/client-service/src/test/java/org/baeldung/SpringContextIntegrationTest.java b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/test/java/org/baeldung/SpringContextIntegrationTest.java
new file mode 100644
index 0000000000..a6a978a354
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/client-service/src/test/java/org/baeldung/SpringContextIntegrationTest.java
@@ -0,0 +1,17 @@
+package org.baeldung;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.spring.cloud.kubernetes.client.Application;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+public class SpringContextIntegrationTest {
+
+ @Test
+ public void contextLoads() {
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/deployment-all.sh b/spring-cloud/spring-cloud-kubernetes-2/deployment-all.sh
new file mode 100755
index 0000000000..9c088b7422
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/deployment-all.sh
@@ -0,0 +1,34 @@
+### build the repository
+mvn clean install
+
+### set docker env
+eval $(minikube docker-env)
+
+### build the docker images on minikube
+cd travel-agency-service
+docker build -t travel-agency-service .
+cd ../client-service
+docker build -t client-service .
+cd ..
+
+### secret and mongodb
+kubectl delete -f secret.yaml
+kubectl delete -f mongo-deployment.yaml
+
+kubectl create -f secret.yaml
+kubectl create -f mongo-deployment.yaml
+
+### travel-agency-service
+kubectl delete -f travel-agency-service/travel-agency-deployment.yaml
+kubectl create -f travel-agency-service/travel-agency-deployment.yaml
+
+
+### client-service
+kubectl delete configmap client-service
+kubectl delete -f client-service/client-service-deployment.yaml
+
+kubectl create -f client-service/client-config.yaml
+kubectl create -f client-service/client-service-deployment.yaml
+
+# Check that the pods are running
+kubectl get pods
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/mongo-deployment.yaml b/spring-cloud/spring-cloud-kubernetes-2/mongo-deployment.yaml
new file mode 100644
index 0000000000..3d40581578
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/mongo-deployment.yaml
@@ -0,0 +1,45 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: mongo
+ name: mongodb-service
+spec:
+ type: NodePort
+ ports:
+ - name: "http"
+ port: 27017
+ protocol: TCP
+ targetPort: 27017
+ selector:
+ service: mongo
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: mongo
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ service: mongo
+ name: mongodb-service
+ spec:
+ containers:
+ - args:
+ - mongod
+ - --smallfiles
+ image: mongo:latest
+ name: mongo
+ env:
+ - name: MONGO_INITDB_ROOT_USERNAME
+ valueFrom:
+ secretKeyRef:
+ name: db-secret
+ key: username
+ - name: MONGO_INITDB_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: db-secret
+ key: password
diff --git a/spring-cloud/spring-cloud-kubernetes-2/pom.xml b/spring-cloud/spring-cloud-kubernetes-2/pom.xml
new file mode 100644
index 0000000000..d501e8102f
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+ com.baeldung.spring.cloud
+ spring-cloud-kubernetes-2
+ 1.0-SNAPSHOT
+ spring-cloud-kubernetes-2
+ pom
+
+
+ 2.0.6.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+ client-service
+ travel-agency-service
+
+
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/secret.yaml b/spring-cloud/spring-cloud-kubernetes-2/secret.yaml
new file mode 100644
index 0000000000..a813c35be7
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/secret.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: db-secret
+data:
+ username: dXNlcg==
+ password: cDQ1NXcwcmQ=
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/Dockerfile b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/Dockerfile
new file mode 100755
index 0000000000..30b66d5eff
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/Dockerfile
@@ -0,0 +1,5 @@
+FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY target/travel-agency-service-1.0-SNAPSHOT.jar app.jar
+ENV JAVA_OPTS=""
+ENTRYPOINT exec java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999 -jar /app.jar
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/pom.xml b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/pom.xml
new file mode 100755
index 0000000000..a375264533
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/pom.xml
@@ -0,0 +1,76 @@
+
+
+ 4.0.0
+ travel-agency-service
+ 1.0-SNAPSHOT
+
+
+ com.baeldung.spring.cloud
+ spring-cloud-kubernetes-2
+ 1.0-SNAPSHOT
+
+
+
+ 1.8
+ Finchley.SR2
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-actuator
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.baeldung.spring.cloud.kubernetes.travelagency.Application
+ JAR
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/Application.java b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/Application.java
new file mode 100755
index 0000000000..bfc7356176
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/Application.java
@@ -0,0 +1,23 @@
+package com.baeldung.spring.cloud.kubernetes.travelagency;
+
+import com.baeldung.spring.cloud.kubernetes.travelagency.controller.TravelAgencyController;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application implements CommandLineRunner {
+
+ private static final Log log = LogFactory.getLog(TravelAgencyController.class);
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ public void run(String... args) throws Exception {
+ log.info("Travel Agency Started! ");
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/controller/TravelAgencyController.java b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/controller/TravelAgencyController.java
new file mode 100644
index 0000000000..42f001616a
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/controller/TravelAgencyController.java
@@ -0,0 +1,55 @@
+package com.baeldung.spring.cloud.kubernetes.travelagency.controller;
+
+import com.baeldung.spring.cloud.kubernetes.travelagency.model.TravelDeal;
+import com.baeldung.spring.cloud.kubernetes.travelagency.repository.TravelDealRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Random;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+
+@RestController
+public class TravelAgencyController {
+
+ @Autowired
+ private TravelDealRepository travelDealRepository;
+
+ private static final Log log = LogFactory.getLog(TravelAgencyController.class);
+
+ @RequestMapping(method = GET, path = "/deals")
+ public String deals() {
+ log.info("Client is requesting new deals!");
+
+ List travelDealList = travelDealRepository.findAll();
+ if (!travelDealList.isEmpty()) {
+ int randomDeal = new Random().nextInt(travelDealList.size());
+ return travelDealList.get(randomDeal)
+ .toString();
+ } else {
+ return "NO DEALS";
+ }
+ }
+
+ @RequestMapping(method = GET, path = "/")
+ @ResponseBody
+ public String get() throws UnknownHostException {
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("Host: ")
+ .append(InetAddress.getLocalHost()
+ .getHostName())
+ .append("
");
+ stringBuilder.append("IP: ")
+ .append(InetAddress.getLocalHost()
+ .getHostAddress())
+ .append("
");
+ stringBuilder.append("Type: ")
+ .append("Travel Agency")
+ .append("
");
+ return stringBuilder.toString();
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/model/TravelDeal.java b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/model/TravelDeal.java
new file mode 100644
index 0000000000..2cd2bf2d45
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/model/TravelDeal.java
@@ -0,0 +1,92 @@
+package com.baeldung.spring.cloud.kubernetes.travelagency.model;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+@Document(collection = "travel_deal")
+public class TravelDeal {
+
+ @Id
+ private BigInteger id;
+
+ private String destination;
+
+ private String description;
+
+ @Field("deal_price")
+ private double dealPrice;
+
+ @Field("old_price")
+ private double oldPrice;
+
+ @Field("departure_date")
+ private Date departureDate;
+
+ @Field("arrival_date")
+ private Date arrivalDate;
+
+ public BigInteger getId() {
+ return id;
+ }
+
+ public void setId(BigInteger id) {
+ this.id = id;
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public double getDealPrice() {
+ return dealPrice;
+ }
+
+ public void setDealPrice(double dealPrice) {
+ this.dealPrice = dealPrice;
+ }
+
+ public double getOldPrice() {
+ return oldPrice;
+ }
+
+ public void setOldPrice(double oldPrice) {
+ this.oldPrice = oldPrice;
+ }
+
+ public Date getDepartureDate() {
+ return departureDate;
+ }
+
+ public void setDepartureDate(Date departureDate) {
+ this.departureDate = departureDate;
+ }
+
+ public Date getArrivalDate() {
+ return arrivalDate;
+ }
+
+ public void setArrivalDate(Date arrivalDate) {
+ this.arrivalDate = arrivalDate;
+ }
+
+ @Override
+ public String toString() {
+ return "TravelDeal{" + "id=" + id + ", destination='" + destination + '\'' + ", description='" + description + '\'' + ", dealPrice=" + dealPrice + ", oldPrice=" + oldPrice + ", departureDate=" + departureDate + ", arrivalDate=" + arrivalDate + '}';
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/repository/TravelDealRepository.java b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/repository/TravelDealRepository.java
new file mode 100644
index 0000000000..20c1e3b67a
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/java/com/baeldung/spring/cloud/kubernetes/travelagency/repository/TravelDealRepository.java
@@ -0,0 +1,12 @@
+package com.baeldung.spring.cloud.kubernetes.travelagency.repository;
+
+import java.util.List;
+
+import com.baeldung.spring.cloud.kubernetes.travelagency.model.TravelDeal;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+public interface TravelDealRepository extends MongoRepository {
+
+ public List findByDestination(String destination);
+
+}
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/application.properties b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/application.properties
new file mode 100644
index 0000000000..ffe78f0917
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+spring.application.name=travel-agency-service
+server.port=8080
+spring.cloud.kubernetes.reload.enabled=true
+spring.cloud.kubernetes.secrets.name=db-secret
+spring.data.mongodb.host=mongodb-service
+spring.data.mongodb.port=27017
+spring.data.mongodb.database=admin
+spring.data.mongodb.username=${MONGO_USERNAME}
+spring.data.mongodb.password=${MONGO_PASSWORD}
+management.endpoint.health.enabled=true
+management.endpoint.info.enabled=true
+management.endpoint.restart.enabled=true
+com.baeldung.spring.cloud.kubernetes.services=debug
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback-spring.xml b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..49aeda8f9f
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback-spring.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ logstash:5000
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback.xml b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback.xml
new file mode 100644
index 0000000000..37492be5bf
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
+
+
+
+
+
+
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-config.yaml b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-config.yaml
new file mode 100644
index 0000000000..93a67e3777
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-config.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: travel-agency-service
+data:
+ application.properties: |-
+ bean.message=Testing reload ! Message from backend is: %s
Services : %s
+
diff --git a/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-deployment.yaml b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-deployment.yaml
new file mode 100644
index 0000000000..a41f13bf61
--- /dev/null
+++ b/spring-cloud/spring-cloud-kubernetes-2/travel-agency-service/travel-agency-deployment.yaml
@@ -0,0 +1,44 @@
+kind: Service
+apiVersion: v1
+metadata:
+ name: travel-agency-service
+spec:
+ selector:
+ app: travel-agency-service
+ ports:
+ - protocol: TCP
+ port: 8080
+ nodePort: 30081
+ type: NodePort
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: travel-agency-service
+spec:
+ selector:
+ matchLabels:
+ app: travel-agency-service
+ replicas: 2
+ template:
+ metadata:
+ labels:
+ app: travel-agency-service
+ spec:
+ containers:
+ - name: travel-agency-service
+ image: travel-agency-service:latest
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
+ env:
+ - name: MONGO_USERNAME
+ valueFrom:
+ secretKeyRef:
+ name: db-secret
+ key: username
+ - name: MONGO_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: db-secret
+ key: password
\ No newline at end of file
diff --git a/spring-core/README.md b/spring-core/README.md
index dcc15a4cb9..d542fa8ed1 100644
--- a/spring-core/README.md
+++ b/spring-core/README.md
@@ -23,3 +23,4 @@
- [Unsatisfied Dependency in Spring](https://www.baeldung.com/spring-unsatisfied-dependency)
- [What is a Spring Bean?](https://www.baeldung.com/spring-bean)
- [Spring PostConstruct and PreDestroy Annotations](https://www.baeldung.com/spring-postconstruct-predestroy)
+- [Guice vs Spring – Dependency Injection](https://www.baeldung.com/guice-spring-dependency-injection)
diff --git a/spring-rest-simple/src/main/java/org/baeldung/web/controller/FooController.java b/spring-rest-simple/src/main/java/org/baeldung/web/controller/FooController.java
index e8cb218258..c68d586667 100644
--- a/spring-rest-simple/src/main/java/org/baeldung/web/controller/FooController.java
+++ b/spring-rest-simple/src/main/java/org/baeldung/web/controller/FooController.java
@@ -2,12 +2,21 @@ package org.baeldung.web.controller;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import java.util.List;
+
import org.baeldung.web.dto.Foo;
import org.baeldung.web.dto.FooProtos;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import com.google.common.collect.Lists;
@Controller
public class FooController {
@@ -16,6 +25,12 @@ public class FooController {
super();
}
+ @RequestMapping(method = RequestMethod.GET, value = "/foos")
+ @ResponseBody
+ public List findListOfFoo() {
+ return Lists.newArrayList(new Foo(1, randomAlphabetic(4)));
+ }
+
// API - read
@RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
@@ -32,6 +47,27 @@ public class FooController {
public Foo updateFoo(@PathVariable("id") final String id, @RequestBody final Foo foo) {
return foo;
}
+
+ @RequestMapping(method = RequestMethod.PATCH, value = "/foos/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public Foo patchFoo(@PathVariable("id") final String id, @RequestBody final Foo foo) {
+ return foo;
+ }
+
+ @RequestMapping(method = RequestMethod.POST, value = "/foos")
+ @ResponseStatus(HttpStatus.CREATED)
+ @ResponseBody
+ public Foo postFoo(@RequestBody final Foo foo) {
+ return foo;
+ }
+
+ @RequestMapping(method = RequestMethod.HEAD, value = "/foos")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public Foo headFoo() {
+ return new Foo(1, randomAlphabetic(4));
+ }
@RequestMapping(method = RequestMethod.GET, value = "/foos/{id}", produces = { "application/x-protobuf" })
@ResponseBody
diff --git a/spring-rest-simple/src/test/java/org/baeldung/web/test/RestTemplateBasicLiveTest.java b/spring-rest-simple/src/test/java/org/baeldung/web/test/RestTemplateBasicLiveTest.java
new file mode 100644
index 0000000000..e213d0255f
--- /dev/null
+++ b/spring-rest-simple/src/test/java/org/baeldung/web/test/RestTemplateBasicLiveTest.java
@@ -0,0 +1,269 @@
+package org.baeldung.web.test;
+
+import static org.apache.commons.codec.binary.Base64.encodeBase64;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Set;
+
+import org.baeldung.web.dto.Foo;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RequestCallback;
+import org.springframework.web.client.RestTemplate;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.google.common.base.Charsets;
+
+public class RestTemplateBasicLiveTest {
+
+ private RestTemplate restTemplate;
+ private static final String fooResourceUrl = "http://localhost:8082/spring-rest/foos";
+
+ @Before
+ public void beforeTest() {
+ restTemplate = new RestTemplate();
+ // restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
+ }
+
+ // GET
+
+ @Test
+ public void givenResourceUrl_whenSendGetForRequestEntity_thenStatusOk() throws IOException {
+ final ResponseEntity response = restTemplate.getForEntity(fooResourceUrl + "/1", Foo.class);
+
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenResourceUrl_whenSendGetForRequestEntity_thenBodyCorrect() throws IOException {
+ final RestTemplate template = new RestTemplate();
+ final ResponseEntity response = template.getForEntity(fooResourceUrl + "/1", String.class);
+
+ final ObjectMapper mapper = new XmlMapper();
+ final JsonNode root = mapper.readTree(response.getBody());
+ final JsonNode name = root.path("name");
+ assertThat(name.asText(), notNullValue());
+ }
+
+ @Test
+ public void givenResourceUrl_whenRetrievingResource_thenCorrect() throws IOException {
+ final Foo foo = restTemplate.getForObject(fooResourceUrl + "/1", Foo.class);
+
+ assertThat(foo.getName(), notNullValue());
+ assertThat(foo.getId(), is(1L));
+ }
+
+ // HEAD, OPTIONS
+
+ @Test
+ public void givenFooService_whenCallHeadForHeaders_thenReceiveAllHeadersForThatResource() {
+ final HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);
+ assertTrue(httpHeaders.getContentType()
+ .includes(MediaType.APPLICATION_JSON));
+ }
+
+ // POST
+
+ @Test
+ public void givenFooService_whenPostForObject_thenCreatedObjectIsReturned() {
+ final HttpEntity request = new HttpEntity<>(new Foo("bar"));
+ final Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
+ assertThat(foo, notNullValue());
+ assertThat(foo.getName(), is("bar"));
+ }
+
+ @Test
+ public void givenFooService_whenPostForLocation_thenCreatedLocationIsReturned() {
+ final HttpEntity request = new HttpEntity<>(new Foo("bar"));
+ final URI location = restTemplate.postForLocation(fooResourceUrl, request);
+ assertThat(location, notNullValue());
+ }
+
+ @Test
+ public void givenFooService_whenPostResource_thenResourceIsCreated() {
+ final Foo foo = new Foo("bar");
+ final ResponseEntity response = restTemplate.postForEntity(fooResourceUrl, foo, Foo.class);
+
+ assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
+ final Foo fooResponse = response.getBody();
+ assertThat(fooResponse, notNullValue());
+ assertThat(fooResponse.getName(), is("bar"));
+ }
+
+ @Test
+ public void givenFooService_whenCallOptionsForAllow_thenReceiveValueOfAllowHeader() {
+ final Set optionsForAllow = restTemplate.optionsForAllow(fooResourceUrl);
+ final HttpMethod[] supportedMethods = { HttpMethod.GET, HttpMethod.POST, HttpMethod.HEAD };
+
+ assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods)));
+ }
+
+ // PUT
+
+ @Test
+ public void givenFooService_whenPutExistingEntity_thenItIsUpdated() {
+ final HttpHeaders headers = prepareBasicAuthHeaders();
+ final HttpEntity request = new HttpEntity<>(new Foo("bar"), headers);
+
+ // Create Resource
+ final ResponseEntity createResponse = restTemplate.exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
+
+ // Update Resource
+ final Foo updatedInstance = new Foo("newName");
+ updatedInstance.setId(createResponse.getBody()
+ .getId());
+ final String resourceUrl = fooResourceUrl + '/' + createResponse.getBody()
+ .getId();
+ final HttpEntity requestUpdate = new HttpEntity<>(updatedInstance, headers);
+ restTemplate.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, Void.class);
+
+ // Check that Resource was updated
+ final ResponseEntity updateResponse = restTemplate.exchange(resourceUrl, HttpMethod.GET, new HttpEntity<>(headers), Foo.class);
+ final Foo foo = updateResponse.getBody();
+ assertThat(foo.getName(), is(updatedInstance.getName()));
+ }
+
+ @Test
+ public void givenFooService_whenPutExistingEntityWithCallback_thenItIsUpdated() {
+ final HttpHeaders headers = prepareBasicAuthHeaders();
+ final HttpEntity request = new HttpEntity<>(new Foo("bar"), headers);
+
+ // Create entity
+ ResponseEntity response = restTemplate.exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
+ assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
+
+ // Update entity
+ final Foo updatedInstance = new Foo("newName");
+ updatedInstance.setId(response.getBody()
+ .getId());
+ final String resourceUrl = fooResourceUrl + '/' + response.getBody()
+ .getId();
+ restTemplate.execute(resourceUrl, HttpMethod.PUT, requestCallback(updatedInstance), clientHttpResponse -> null);
+
+ // Check that entity was updated
+ response = restTemplate.exchange(resourceUrl, HttpMethod.GET, new HttpEntity<>(headers), Foo.class);
+ final Foo foo = response.getBody();
+ assertThat(foo.getName(), is(updatedInstance.getName()));
+ }
+
+ // PATCH
+
+ @Test
+ public void givenFooService_whenPatchExistingEntity_thenItIsUpdated() {
+ final HttpHeaders headers = prepareBasicAuthHeaders();
+ final HttpEntity request = new HttpEntity<>(new Foo("bar"), headers);
+
+ // Create Resource
+ final ResponseEntity createResponse = restTemplate.exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
+
+ // Update Resource
+ final Foo updatedResource = new Foo("newName");
+ updatedResource.setId(createResponse.getBody()
+ .getId());
+ final String resourceUrl = fooResourceUrl + '/' + createResponse.getBody()
+ .getId();
+ final HttpEntity requestUpdate = new HttpEntity<>(updatedResource, headers);
+ final ClientHttpRequestFactory requestFactory = getSimpleClientHttpRequestFactory();
+ final RestTemplate template = new RestTemplate(requestFactory);
+ template.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
+ template.patchForObject(resourceUrl, requestUpdate, Void.class);
+
+ // Check that Resource was updated
+ final ResponseEntity updateResponse = restTemplate.exchange(resourceUrl, HttpMethod.GET, new HttpEntity<>(headers), Foo.class);
+ final Foo foo = updateResponse.getBody();
+ assertThat(foo.getName(), is(updatedResource.getName()));
+ }
+
+ // DELETE
+
+ @Test
+ public void givenFooService_whenCallDelete_thenEntityIsRemoved() {
+ final Foo foo = new Foo("remove me");
+ final ResponseEntity response = restTemplate.postForEntity(fooResourceUrl, foo, Foo.class);
+ assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
+
+ final String entityUrl = fooResourceUrl + "/" + response.getBody()
+ .getId();
+ restTemplate.delete(entityUrl);
+ try {
+ restTemplate.getForEntity(entityUrl, Foo.class);
+ fail();
+ } catch (final HttpClientErrorException ex) {
+ assertThat(ex.getStatusCode(), is(HttpStatus.NOT_FOUND));
+ }
+ }
+
+ @Test
+ public void givenFooService_whenFormSubmit_thenResourceIsCreated() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+
+ MultiValueMap map= new LinkedMultiValueMap<>();
+ map.add("id", "1");
+
+ HttpEntity> request = new HttpEntity<>(map, headers);
+
+ ResponseEntity response = restTemplate.postForEntity( fooResourceUrl+"/form", request , String.class);
+
+ assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
+ final String fooResponse = response.getBody();
+ assertThat(fooResponse, notNullValue());
+ assertThat(fooResponse, is("1"));
+ }
+
+ private HttpHeaders prepareBasicAuthHeaders() {
+ final HttpHeaders headers = new HttpHeaders();
+ final String encodedLogPass = getBase64EncodedLogPass();
+ headers.add(HttpHeaders.AUTHORIZATION, "Basic " + encodedLogPass);
+ return headers;
+ }
+
+ private String getBase64EncodedLogPass() {
+ final String logPass = "user1:user1Pass";
+ final byte[] authHeaderBytes = encodeBase64(logPass.getBytes(Charsets.US_ASCII));
+ return new String(authHeaderBytes, Charsets.US_ASCII);
+ }
+
+ private RequestCallback requestCallback(final Foo updatedInstance) {
+ return clientHttpRequest -> {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(clientHttpRequest.getBody(), updatedInstance);
+ clientHttpRequest.getHeaders()
+ .add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+ clientHttpRequest.getHeaders()
+ .add(HttpHeaders.AUTHORIZATION, "Basic " + getBase64EncodedLogPass());
+ };
+ }
+
+ // Simply setting restTemplate timeout using ClientHttpRequestFactory
+
+ ClientHttpRequestFactory getSimpleClientHttpRequestFactory() {
+ final int timeout = 5;
+ final HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
+ clientHttpRequestFactory.setConnectTimeout(timeout * 1000);
+ return clientHttpRequestFactory;
+ }
+
+}
diff --git a/spring-rest-simple/src/test/java/org/baeldung/web/test/TestRestTemplateBasicLiveTest.java b/spring-rest-simple/src/test/java/org/baeldung/web/test/TestRestTemplateBasicLiveTest.java
new file mode 100644
index 0000000000..b920ed38da
--- /dev/null
+++ b/spring-rest-simple/src/test/java/org/baeldung/web/test/TestRestTemplateBasicLiveTest.java
@@ -0,0 +1,122 @@
+package org.baeldung.web.test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import okhttp3.Request;
+import okhttp3.RequestBody;
+
+public class TestRestTemplateBasicLiveTest {
+
+ private RestTemplate restTemplate;
+
+ private static final String FOO_RESOURCE_URL = "http://localhost:" + 8082 + "/spring-rest/foos";
+ private static final String URL_SECURED_BY_AUTHENTICATION = "http://httpbin.org/basic-auth/user/passwd";
+ private static final String BASE_URL = "http://localhost:" + 8082 + "/spring-rest";
+
+ @Before
+ public void beforeTest() {
+ restTemplate = new RestTemplate();
+ }
+
+ // GET
+ @Test
+ public void givenTestRestTemplate_whenSendGetForEntity_thenStatusOk() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate();
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenRestTemplateWrapper_whenSendGetForEntity_thenStatusOk() {
+ RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
+ restTemplateBuilder.configure(restTemplate);
+ TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder);
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenRestTemplateBuilderWrapper_whenSendGetForEntity_thenStatusOk() {
+ RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
+ restTemplateBuilder.build();
+ TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder);
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenRestTemplateWrapperWithCredentials_whenSendGetForEntity_thenStatusOk() {
+ RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
+ restTemplateBuilder.configure(restTemplate);
+ TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder, "user", "passwd");
+ ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION,
+ String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenTestRestTemplateWithCredentials_whenSendGetForEntity_thenStatusOk() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd");
+ ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION,
+ String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenTestRestTemplateWithBasicAuth_whenSendGetForEntity_thenStatusOk() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate();
+ ResponseEntity response = testRestTemplate.withBasicAuth("user", "passwd").
+ getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ @Test
+ public void givenTestRestTemplateWithCredentialsAndEnabledCookies_whenSendGetForEntity_thenStatusOk() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd", TestRestTemplate.
+ HttpClientOption.ENABLE_COOKIES);
+ ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION,
+ String.class);
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
+ }
+
+ // HEAD
+ @Test
+ public void givenFooService_whenCallHeadForHeaders_thenReceiveAllHeaders() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate();
+ final HttpHeaders httpHeaders = testRestTemplate.headForHeaders(FOO_RESOURCE_URL);
+ assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));
+ }
+
+ // POST
+ @Test
+ public void givenService_whenPostForObject_thenCreatedObjectIsReturned() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd");
+ final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"),
+ "{\"id\":1,\"name\":\"Jim\"}");
+ final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build();
+ testRestTemplate.postForObject(URL_SECURED_BY_AUTHENTICATION, request, String.class);
+ }
+
+ // PUT
+ @Test
+ public void givenService_whenPutForObject_thenCreatedObjectIsReturned() {
+ TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd");
+ final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"),
+ "{\"id\":1,\"name\":\"Jim\"}");
+ final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build();
+ testRestTemplate.put(URL_SECURED_BY_AUTHENTICATION, request, String.class);
+ }
+
+}
diff --git a/spring-resttemplate/pom.xml b/spring-resttemplate/pom.xml
index 9a0978f120..06d4eed9fc 100644
--- a/spring-resttemplate/pom.xml
+++ b/spring-resttemplate/pom.xml
@@ -25,6 +25,10 @@
org.springframework.boot
spring-boot-devtools
+
+ org.springframework.boot
+ spring-boot-starter-web
+
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/RestTemplateConfigurationApplication.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/RestTemplateConfigurationApplication.java
similarity index 89%
rename from spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/RestTemplateConfigurationApplication.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/RestTemplateConfigurationApplication.java
index 76fc346aca..9a361e92c9 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/RestTemplateConfigurationApplication.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/RestTemplateConfigurationApplication.java
@@ -1,4 +1,4 @@
-package org.baeldung.resttemplate.configuration;
+package org.baeldung.resttemplate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
diff --git a/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/FooController.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/FooController.java
new file mode 100644
index 0000000000..a9d400b199
--- /dev/null
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/FooController.java
@@ -0,0 +1,124 @@
+package org.baeldung.resttemplate.configuration;
+
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+
+import org.baeldung.resttemplate.web.dto.Foo;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+@Controller
+public class FooController {
+
+ private Map fooRepository = Maps.newHashMap(ImmutableMap.of(1L, new Foo(1L, randomAlphabetic(4))));
+
+ public FooController() {
+ super();
+ }
+
+ @RequestMapping(method = RequestMethod.GET, value = "/foos")
+ @ResponseBody
+ public Collection findListOfFoo() {
+ return fooRepository.values();
+ }
+
+ // API - read
+
+ @RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
+ @ResponseBody
+ public Foo findById(@PathVariable final long id) throws HttpClientErrorException {
+ Foo foo = fooRepository.get(id);
+
+ if (foo == null) {
+ throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
+ } else {
+ return foo;
+ }
+ }
+
+ // API - write
+
+ @RequestMapping(method = RequestMethod.PUT, value = "/foos/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public Foo updateFoo(@PathVariable("id") final long id, @RequestBody final Foo foo) {
+ fooRepository.put(id, foo);
+ return foo;
+ }
+
+ @RequestMapping(method = RequestMethod.PATCH, value = "/foos/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public Foo patchFoo(@PathVariable("id") final long id, @RequestBody final Foo foo) {
+ fooRepository.put(id, foo);
+ return foo;
+ }
+
+ @RequestMapping(method = RequestMethod.POST, value = "/foos")
+ @ResponseStatus(HttpStatus.CREATED)
+ @ResponseBody
+ public ResponseEntity postFoo(@RequestBody final Foo foo) {
+
+ fooRepository.put(foo.getId(), foo);
+ final URI location = ServletUriComponentsBuilder
+ .fromCurrentServletMapping()
+ .path("/foos/{id}")
+ .build()
+ .expand(foo.getId())
+ .toUri();
+
+ final HttpHeaders headers = new HttpHeaders();
+ headers.setLocation(location);
+
+ final ResponseEntity entity = new ResponseEntity(foo, headers, HttpStatus.CREATED);
+ return entity;
+ }
+
+ @RequestMapping(method = RequestMethod.HEAD, value = "/foos")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public Foo headFoo() {
+ return new Foo(1, randomAlphabetic(4));
+ }
+
+ @RequestMapping(method = RequestMethod.POST, value = "/foos/new")
+ @ResponseStatus(HttpStatus.CREATED)
+ @ResponseBody
+ public Foo createFoo(@RequestBody final Foo foo) {
+ fooRepository.put(foo.getId(), foo);
+ return foo;
+ }
+
+ @RequestMapping(method = RequestMethod.DELETE, value = "/foos/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ @ResponseBody
+ public long deleteFoo(@PathVariable final long id) {
+ fooRepository.remove(id);
+ return id;
+ }
+
+ @RequestMapping(method = RequestMethod.POST, value = "/foos/form")
+ @ResponseStatus(HttpStatus.CREATED)
+ @ResponseBody
+ public String submitFoo(@RequestParam("id") String id) {
+ return id;
+ }
+
+}
diff --git a/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/SpringConfig.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/SpringConfig.java
index 4e121185b1..966d5bcaa1 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/SpringConfig.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/configuration/SpringConfig.java
@@ -7,6 +7,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
+import org.springframework.web.client.RestTemplate;
@Configuration
@EnableAutoConfiguration
@@ -25,4 +26,8 @@ public class SpringConfig {
return new RestTemplateBuilder(customRestTemplateCustomizer());
}
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
}
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/dto/Foo.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/dto/Foo.java
similarity index 93%
rename from spring-resttemplate/src/main/java/org/baeldung/web/dto/Foo.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/dto/Foo.java
index 240b368b50..ed0a42c429 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/dto/Foo.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/dto/Foo.java
@@ -1,4 +1,4 @@
-package org.baeldung.web.dto;
+package org.baeldung.resttemplate.web.dto;
import com.thoughtworks.xstream.annotations.XStreamAlias;
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/exception/NotFoundException.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/exception/NotFoundException.java
similarity index 55%
rename from spring-resttemplate/src/main/java/org/baeldung/web/exception/NotFoundException.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/exception/NotFoundException.java
index 5b4d80a659..3e606e9314 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/exception/NotFoundException.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/exception/NotFoundException.java
@@ -1,4 +1,4 @@
-package org.baeldung.web.exception;
+package org.baeldung.resttemplate.web.exception;
public class NotFoundException extends RuntimeException {
}
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/handler/RestTemplateResponseErrorHandler.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/handler/RestTemplateResponseErrorHandler.java
similarity index 82%
rename from spring-resttemplate/src/main/java/org/baeldung/web/handler/RestTemplateResponseErrorHandler.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/handler/RestTemplateResponseErrorHandler.java
index b1b87e89a5..214de38746 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/handler/RestTemplateResponseErrorHandler.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/handler/RestTemplateResponseErrorHandler.java
@@ -1,13 +1,14 @@
-package org.baeldung.web.handler;
+package org.baeldung.resttemplate.web.handler;
-import org.baeldung.web.exception.NotFoundException;
+import java.io.IOException;
+
+import org.baeldung.resttemplate.web.exception.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResponseErrorHandler;
-import java.io.IOException;
-
@Component
public class RestTemplateResponseErrorHandler
implements ResponseErrorHandler {
@@ -31,6 +32,7 @@ public class RestTemplateResponseErrorHandler
.getStatusCode()
.series() == HttpStatus.Series.SERVER_ERROR) {
//Handle SERVER_ERROR
+ throw new HttpClientErrorException(httpResponse.getStatusCode());
} else if (httpResponse
.getStatusCode()
.series() == HttpStatus.Series.CLIENT_ERROR) {
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/model/Bar.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Bar.java
similarity index 87%
rename from spring-resttemplate/src/main/java/org/baeldung/web/model/Bar.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Bar.java
index 474e2070a5..cf5279697f 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/model/Bar.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Bar.java
@@ -1,4 +1,4 @@
-package org.baeldung.web.model;
+package org.baeldung.resttemplate.web.model;
public class Bar {
private String id;
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/model/Employee.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Employee.java
similarity index 95%
rename from spring-resttemplate/src/main/java/org/baeldung/web/model/Employee.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Employee.java
index 7cab4a0430..a9b84a77b4 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/model/Employee.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/model/Employee.java
@@ -1,4 +1,4 @@
-package org.baeldung.web.model;
+package org.baeldung.resttemplate.web.model;
import java.util.Date;
import java.util.Objects;
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/service/BarConsumerService.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/BarConsumerService.java
similarity index 78%
rename from spring-resttemplate/src/main/java/org/baeldung/web/service/BarConsumerService.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/BarConsumerService.java
index 0bf24bd480..54a66ea591 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/service/BarConsumerService.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/BarConsumerService.java
@@ -1,7 +1,7 @@
-package org.baeldung.web.service;
+package org.baeldung.resttemplate.web.service;
-import org.baeldung.web.handler.RestTemplateResponseErrorHandler;
-import org.baeldung.web.model.Bar;
+import org.baeldung.resttemplate.web.handler.RestTemplateResponseErrorHandler;
+import org.baeldung.resttemplate.web.model.Bar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
diff --git a/spring-resttemplate/src/main/java/org/baeldung/web/service/EmployeeService.java b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/EmployeeService.java
similarity index 89%
rename from spring-resttemplate/src/main/java/org/baeldung/web/service/EmployeeService.java
rename to spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/EmployeeService.java
index 91614e90ad..c6562fbc94 100644
--- a/spring-resttemplate/src/main/java/org/baeldung/web/service/EmployeeService.java
+++ b/spring-resttemplate/src/main/java/org/baeldung/resttemplate/web/service/EmployeeService.java
@@ -1,6 +1,6 @@
-package org.baeldung.web.service;
+package org.baeldung.resttemplate.web.service;
-import org.baeldung.web.model.Employee;
+import org.baeldung.resttemplate.web.model.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/spring-resttemplate/src/test/java/org/baeldung/SpringContextIntegrationTest.java b/spring-resttemplate/src/test/java/org/baeldung/SpringContextIntegrationTest.java
index 3a5a20cfeb..3c762b894b 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/SpringContextIntegrationTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/SpringContextIntegrationTest.java
@@ -1,6 +1,6 @@
package org.baeldung;
-import org.baeldung.resttemplate.configuration.RestTemplateConfigurationApplication;
+import org.baeldung.resttemplate.RestTemplateConfigurationApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
diff --git a/spring-resttemplate/src/test/java/org/baeldung/client/RestTemplateBasicLiveTest.java b/spring-resttemplate/src/test/java/org/baeldung/client/RestTemplateBasicLiveTest.java
index 143aa079d5..a54c124d5f 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/client/RestTemplateBasicLiveTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/client/RestTemplateBasicLiveTest.java
@@ -14,7 +14,8 @@ import java.net.URI;
import java.util.Arrays;
import java.util.Set;
-import org.baeldung.web.dto.Foo;
+import org.baeldung.resttemplate.web.dto.Foo;
+import org.baeldung.resttemplate.web.handler.RestTemplateResponseErrorHandler;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpEntity;
@@ -34,6 +35,7 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.common.base.Charsets;
public class RestTemplateBasicLiveTest {
@@ -44,6 +46,7 @@ public class RestTemplateBasicLiveTest {
@Before
public void beforeTest() {
restTemplate = new RestTemplate();
+ restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
// restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
}
@@ -61,7 +64,7 @@ public class RestTemplateBasicLiveTest {
final RestTemplate template = new RestTemplate();
final ResponseEntity response = template.getForEntity(fooResourceUrl + "/1", String.class);
- final ObjectMapper mapper = new ObjectMapper();
+ final ObjectMapper mapper = new XmlMapper();
final JsonNode root = mapper.readTree(response.getBody());
final JsonNode name = root.path("name");
assertThat(name.asText(), notNullValue());
@@ -97,7 +100,7 @@ public class RestTemplateBasicLiveTest {
@Test
public void givenFooService_whenPostForLocation_thenCreatedLocationIsReturned() {
final HttpEntity request = new HttpEntity<>(new Foo("bar"));
- final URI location = restTemplate.postForLocation(fooResourceUrl, request);
+ final URI location = restTemplate.postForLocation(fooResourceUrl, request, Foo.class);
assertThat(location, notNullValue());
}
@@ -211,7 +214,7 @@ public class RestTemplateBasicLiveTest {
restTemplate.getForEntity(entityUrl, Foo.class);
fail();
} catch (final HttpClientErrorException ex) {
- assertThat(ex.getStatusCode(), is(HttpStatus.NOT_FOUND));
+ assertThat(ex.getStatusCode(), is(HttpStatus.INTERNAL_SERVER_ERROR));
}
}
@@ -221,7 +224,7 @@ public class RestTemplateBasicLiveTest {
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap map= new LinkedMultiValueMap<>();
- map.add("id", "1");
+ map.add("id", "10");
HttpEntity> request = new HttpEntity<>(map, headers);
@@ -230,7 +233,7 @@ public class RestTemplateBasicLiveTest {
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
final String fooResponse = response.getBody();
assertThat(fooResponse, notNullValue());
- assertThat(fooResponse, is("1"));
+ assertThat(fooResponse, is("10"));
}
private HttpHeaders prepareBasicAuthHeaders() {
diff --git a/spring-resttemplate/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java b/spring-resttemplate/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java
index e303c75a28..967c4a6188 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java
@@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
+import org.baeldung.resttemplate.web.dto.Foo;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -34,7 +35,7 @@ public class TestRestTemplateBasicLiveTest {
@Test
public void givenTestRestTemplate_whenSendGetForEntity_thenStatusOk() {
TestRestTemplate testRestTemplate = new TestRestTemplate();
- ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", Foo.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
@@ -43,7 +44,7 @@ public class TestRestTemplateBasicLiveTest {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
restTemplateBuilder.configure(restTemplate);
TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder);
- ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", Foo.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
@@ -52,7 +53,7 @@ public class TestRestTemplateBasicLiveTest {
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
restTemplateBuilder.build();
TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder);
- ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class);
+ ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", Foo.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
diff --git a/spring-resttemplate/src/test/java/org/baeldung/web/handler/RestTemplateResponseErrorHandlerIntegrationTest.java b/spring-resttemplate/src/test/java/org/baeldung/web/handler/RestTemplateResponseErrorHandlerIntegrationTest.java
index 2dfa81f441..60069cea71 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/web/handler/RestTemplateResponseErrorHandlerIntegrationTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/web/handler/RestTemplateResponseErrorHandlerIntegrationTest.java
@@ -1,7 +1,8 @@
package org.baeldung.web.handler;
-import org.baeldung.web.exception.NotFoundException;
-import org.baeldung.web.model.Bar;
+import org.baeldung.resttemplate.web.exception.NotFoundException;
+import org.baeldung.resttemplate.web.handler.RestTemplateResponseErrorHandler;
+import org.baeldung.resttemplate.web.model.Bar;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java b/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java
index a45af318f1..f93ba71666 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceMockRestServiceServerUnitTest.java
@@ -7,7 +7,8 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
import java.net.URI;
import org.baeldung.SpringTestConfig;
-import org.baeldung.web.model.Employee;
+import org.baeldung.resttemplate.web.model.Employee;
+import org.baeldung.resttemplate.web.service.EmployeeService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
diff --git a/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceUnitTest.java b/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceUnitTest.java
index 23cd9a8fd2..f4b391573a 100644
--- a/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceUnitTest.java
+++ b/spring-resttemplate/src/test/java/org/baeldung/web/service/EmployeeServiceUnitTest.java
@@ -1,6 +1,7 @@
package org.baeldung.web.service;
-import org.baeldung.web.model.Employee;
+import org.baeldung.resttemplate.web.model.Employee;
+import org.baeldung.resttemplate.web.service.EmployeeService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java
index 3a3e632c51..55e1397823 100644
--- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java
+++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java
@@ -64,11 +64,11 @@ public class ForkJoinStateMachineConfiguration extends StateMachineConfigurerAda
@Bean
public Guard mediumGuard() {
- return (ctx) -> false;
+ return ctx -> false;
}
@Bean
public Guard highGuard() {
- return (ctx) -> false;
+ return ctx -> false;
}
}
\ No newline at end of file
diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java
index 2f48a9dbb5..21b37381df 100644
--- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java
+++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java
@@ -50,11 +50,11 @@ public class JunctionStateMachineConfiguration extends StateMachineConfigurerAda
@Bean
public Guard mediumGuard() {
- return (ctx) -> false;
+ return ctx -> false;
}
@Bean
public Guard highGuard() {
- return (ctx) -> false;
+ return ctx -> false;
}
}
\ No newline at end of file
diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java
index d1b1ce001c..0c392c2c35 100644
--- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java
+++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java
@@ -18,7 +18,7 @@ import java.util.logging.Logger;
@EnableStateMachine
public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter {
- public static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName());
@Override
public void configure(StateMachineConfigurationConfigurer config) throws Exception {
@@ -80,7 +80,7 @@ public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapt
@Bean
public Guard simpleGuard() {
- return (ctx) -> {
+ return ctx -> {
int approvalCount = (int) ctx
.getExtendedState()
.getVariables()
diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java
index 47a274404e..09e8946810 100644
--- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java
+++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java
@@ -7,10 +7,10 @@ import java.util.logging.Logger;
public class StateMachineListener extends StateMachineListenerAdapter {
- public static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName());
@Override
public void stateChanged(State from, State to) {
- LOGGER.info(String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId()));
+ LOGGER.info(() -> String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId()));
}
}
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java
index b34d5c47c6..66acad901c 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineIntegrationTest.java
@@ -1,18 +1,15 @@
package com.baeldung.spring.statemachine;
import com.baeldung.spring.statemachine.config.ForkJoinStateMachineConfiguration;
-import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import javax.annotation.Resource;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java
index 3d7c0be828..00a9c9a08c 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineIntegrationTest.java
@@ -1,18 +1,15 @@
package com.baeldung.spring.statemachine;
import com.baeldung.spring.statemachine.config.HierarchicalStateMachineConfiguration;
-import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import javax.annotation.Resource;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java
index 93de23fad3..f0ee757522 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineIntegrationTest.java
@@ -1,20 +1,16 @@
package com.baeldung.spring.statemachine;
import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration;
-import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import javax.annotation.Resource;
-
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JunctionStateMachineConfiguration.class)
public class JunctionStateMachineIntegrationTest {
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java
index 9074ece001..d345cd0dfc 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineIntegrationTest.java
@@ -3,19 +3,15 @@ package com.baeldung.spring.statemachine;
import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents;
import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates;
import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration;
-import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import javax.annotation.Resource;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderIntegrationTest.java
index c1de8b8958..e5431d9d83 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderIntegrationTest.java
@@ -1,11 +1,11 @@
package com.baeldung.spring.statemachine;
-import static org.junit.Assert.assertEquals;
-
import org.junit.Test;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
+import static org.junit.Assert.assertEquals;
+
public class StateMachineBuilderIntegrationTest {
@Test
diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java
index 25df7c8cd3..aab07225a3 100644
--- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java
+++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java
@@ -1,22 +1,17 @@
package com.baeldung.spring.statemachine;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
+import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.statemachine.StateMachine;
-
-import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.junit4.SpringRunner;
-import javax.annotation.Resource;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SimpleStateMachineConfiguration.class)
@@ -42,7 +37,7 @@ public class StateMachineIntegrationTest {
}
@Test
- public void whenSimpleStringMachineActionState_thenActionExecuted() throws InterruptedException {
+ public void whenSimpleStringMachineActionState_thenActionExecuted() {
stateMachine.sendEvent("E3");
assertEquals("S3", stateMachine.getState().getId());
diff --git a/testing-modules/groovy-spock/README.md b/testing-modules/groovy-spock/README.md
index 18d26e8fc0..a98df27172 100644
--- a/testing-modules/groovy-spock/README.md
+++ b/testing-modules/groovy-spock/README.md
@@ -1,3 +1,4 @@
### Relevant articles
- [Introduction to Testing with Spock and Groovy](http://www.baeldung.com/groovy-spock)
+- [Difference Between Stub, Mock, and Spy in the Spock Framework](https://www.baeldung.com/spock-stub-mock-spy)
diff --git a/testing-modules/rest-assured/pom.xml b/testing-modules/rest-assured/pom.xml
index 5d3cac4aa3..cd342ccd11 100644
--- a/testing-modules/rest-assured/pom.xml
+++ b/testing-modules/rest-assured/pom.xml
@@ -19,6 +19,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-json
+
org.springframework.boot
spring-boot-starter-test
@@ -97,7 +101,7 @@
joda-time
- joda-time
+ joda-time
@@ -143,15 +147,6 @@
wiremock
${wiremock.version}
-
- io.rest-assured
- rest-assured
- test
-
-
- io.rest-assured
- json-schema-validator
-
com.github.fge
json-schema-validator
@@ -167,6 +162,23 @@
commons-collections
${commons-collections.version}
+
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ io.rest-assured
+ spring-mock-mvc
+ test
+
+
+ io.rest-assured
+ json-schema-validator
+ test
+
diff --git a/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/Course.java b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/Course.java
new file mode 100644
index 0000000000..ac958b3239
--- /dev/null
+++ b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/Course.java
@@ -0,0 +1,17 @@
+package com.baeldung.restassured.learner;
+
+class Course {
+
+ private String code;
+
+ public Course() {
+ }
+
+ Course(String code) {
+ this.code = code;
+ }
+
+ String getCode() {
+ return code;
+ }
+}
diff --git a/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseController.java b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseController.java
new file mode 100644
index 0000000000..f4a3f56608
--- /dev/null
+++ b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseController.java
@@ -0,0 +1,31 @@
+package com.baeldung.restassured.learner;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Collection;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+
+@RestController
+@RequestMapping(path = "/courses")
+public class CourseController {
+
+ private final CourseService courseService;
+
+ public CourseController(CourseService courseService) {
+ this.courseService = courseService;
+ }
+
+ @GetMapping(produces = APPLICATION_JSON_UTF8_VALUE)
+ public Collection getCourses() {
+ return courseService.getCourses();
+ }
+
+ @GetMapping(path = "/{code}", produces = APPLICATION_JSON_UTF8_VALUE)
+ public Course getCourse(@PathVariable String code) {
+ return courseService.getCourse(code);
+ }
+}
diff --git a/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseControllerExceptionHandler.java b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseControllerExceptionHandler.java
new file mode 100644
index 0000000000..b17e95c31c
--- /dev/null
+++ b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseControllerExceptionHandler.java
@@ -0,0 +1,18 @@
+package com.baeldung.restassured.learner;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice(assignableTypes = CourseController.class)
+public class CourseControllerExceptionHandler extends ResponseEntityExceptionHandler {
+
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ @ExceptionHandler(CourseNotFoundException.class)
+ @SuppressWarnings("ThrowablePrintedToSystemOut")
+ public void handleCourseNotFoundException(CourseNotFoundException cnfe) {
+ System.out.println(cnfe);
+ }
+}
diff --git a/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseNotFoundException.java b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseNotFoundException.java
new file mode 100644
index 0000000000..dc1a3f796d
--- /dev/null
+++ b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseNotFoundException.java
@@ -0,0 +1,8 @@
+package com.baeldung.restassured.learner;
+
+class CourseNotFoundException extends RuntimeException {
+
+ CourseNotFoundException(String code) {
+ super(code);
+ }
+}
diff --git a/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseService.java b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseService.java
new file mode 100644
index 0000000000..11508ab9ba
--- /dev/null
+++ b/testing-modules/rest-assured/src/main/java/com/baeldung/restassured/learner/CourseService.java
@@ -0,0 +1,27 @@
+package com.baeldung.restassured.learner;
+
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+class CourseService {
+
+ private static final Map COURSE_MAP = new ConcurrentHashMap<>();
+
+ static {
+ Course wizardry = new Course("Wizardry");
+ COURSE_MAP.put(wizardry.getCode(), wizardry);
+ }
+
+ Collection getCourses() {
+ return COURSE_MAP.values();
+ }
+
+ Course getCourse(String code) {
+ return Optional.ofNullable(COURSE_MAP.get(code)).orElseThrow(() -> new CourseNotFoundException(code));
+ }
+}
diff --git a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerIntegrationTest.java b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerIntegrationTest.java
new file mode 100644
index 0000000000..5e36cd169b
--- /dev/null
+++ b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.restassured.learner;
+
+import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+
+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.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.context.WebApplicationContext;
+
+import io.restassured.module.mockmvc.RestAssuredMockMvc;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+public class CourseControllerIntegrationTest {
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ @Before
+ public void initialiseRestAssuredMockMvcWebApplicationContext() {
+ RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
+ }
+
+ @Test
+ public void givenNoMatchingCourseCodeWhenGetCourseThenRespondWithStatusNotFound() {
+ String nonMatchingCourseCode = "nonMatchingCourseCode";
+
+ given()
+ .when()
+ .get("/courses/" + nonMatchingCourseCode)
+ .then()
+ .log().ifValidationFails()
+ .statusCode(NOT_FOUND.value());
+ }
+}
diff --git a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerUnitTest.java b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerUnitTest.java
new file mode 100644
index 0000000000..2a795e2b0b
--- /dev/null
+++ b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/learner/CourseControllerUnitTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.restassured.learner;
+
+import io.restassured.module.mockmvc.RestAssuredMockMvc;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static io.restassured.http.ContentType.JSON;
+import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.when;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+import static org.springframework.http.HttpStatus.OK;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CourseControllerUnitTest {
+
+ @Mock
+ private CourseService courseService;
+ @InjectMocks
+ private CourseController courseController;
+ @InjectMocks
+ private CourseControllerExceptionHandler courseControllerExceptionHandler;
+
+ @Before
+ public void initialiseRestAssuredMockMvcStandalone() {
+ RestAssuredMockMvc.standaloneSetup(courseController, courseControllerExceptionHandler);
+ }
+
+ @Test
+ public void givenNoExistingCoursesWhenGetCoursesThenRespondWithStatusOkAndEmptyArray() {
+ when(courseService.getCourses()).thenReturn(Collections.emptyList());
+
+ given()
+ .when()
+ .get("/courses")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(OK.value())
+ .contentType(JSON)
+ .body(is(equalTo("[]")));
+ }
+
+ @Test
+ public void givenNoMatchingCoursesWhenGetCoursesThenRespondWithStatusNotFound() {
+ String nonMatchingCourseCode = "nonMatchingCourseCode";
+
+ when(courseService.getCourse(nonMatchingCourseCode)).thenThrow(new CourseNotFoundException(nonMatchingCourseCode));
+
+ given()
+ .when()
+ .get("/courses/" + nonMatchingCourseCode)
+ .then()
+ .log().ifValidationFails()
+ .statusCode(NOT_FOUND.value());
+ }
+}