diff --git a/apache-kafka-2/README.md b/apache-kafka-2/README.md
index dc675a0811..40ee701be1 100644
--- a/apache-kafka-2/README.md
+++ b/apache-kafka-2/README.md
@@ -13,3 +13,4 @@ You can build the project from the command line using: *mvn clean install*, or i
- [Read Data From the Beginning Using Kafka Consumer API](https://www.baeldung.com/java-kafka-consumer-api-read)
- [Get Partition Count for a Topic in Kafka](https://www.baeldung.com/java-kafka-partition-count-topic)
- [bootstrap-server in Kafka Configuration](https://www.baeldung.com/java-kafka-bootstrap-server)
+- [Introduction to Apache Kafka](https://www.baeldung.com/apache-kafka)
diff --git a/core-java-modules/core-java-21/README.md b/core-java-modules/core-java-21/README.md
index 2c51db275a..ffb999a4ba 100644
--- a/core-java-modules/core-java-21/README.md
+++ b/core-java-modules/core-java-21/README.md
@@ -1,3 +1,5 @@
## Relevant Articles
- [Sequenced Collections in Java 21](https://www.baeldung.com/java-21-sequenced-collections)
- [String Templates in Java 21](https://www.baeldung.com/java-21-string-templates)
+- [Unnamed Classes and Instance Main Methods in Java 21](https://www.baeldung.com/java-21-unnamed-class-instance-main)
+- [Unnamed Patterns and Variables in Java 21](https://www.baeldung.com/java-unnamed-patterns-variables)
diff --git a/core-java-modules/core-java-char/README.md b/core-java-modules/core-java-char/README.md
index e4af3121c5..56040a3ea5 100644
--- a/core-java-modules/core-java-char/README.md
+++ b/core-java-modules/core-java-char/README.md
@@ -5,3 +5,4 @@ This module contains articles about Java Character Class
### Relevant Articles:
- [Character#isAlphabetic vs. Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic)
- [Difference Between Java’s “char” and “String”](https://www.baeldung.com/java-char-vs-string)
+- [Increment Character in Java](https://www.baeldung.com/java-char-sequence)
diff --git a/core-java-modules/core-java-collections-5/README.md b/core-java-modules/core-java-collections-5/README.md
index 1769d11686..e478d87ad0 100644
--- a/core-java-modules/core-java-collections-5/README.md
+++ b/core-java-modules/core-java-collections-5/README.md
@@ -5,4 +5,6 @@
### Relevant Articles:
- [Introduction to Roaring Bitmap](https://www.baeldung.com/java-roaring-bitmap-intro)
- [Creating Custom Iterator in Java](https://www.baeldung.com/java-creating-custom-iterator)
+- [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods)
+- [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration)
- More articles: [[<-- prev]](/core-java-modules/core-java-collections-4)
diff --git a/core-java-modules/core-java-collections-array-list-2/README.md b/core-java-modules/core-java-collections-array-list-2/README.md
index dbf5c47edb..575e0dbb07 100644
--- a/core-java-modules/core-java-collections-array-list-2/README.md
+++ b/core-java-modules/core-java-collections-array-list-2/README.md
@@ -3,4 +3,4 @@
This module contains articles about the Java ArrayList collection
### Relevant Articles:
-- [Create an ArrayList with Multiple Object Types](https://www.baeldung.com/arraylist-with-multiple-object-types)
+- [Create an ArrayList with Multiple Object Types](https://www.baeldung.com/java-arraylist-multiple-object-types)
diff --git a/core-java-modules/core-java-collections-conversions-3/README.md b/core-java-modules/core-java-collections-conversions-3/README.md
index f89cd199ff..653f518840 100644
--- a/core-java-modules/core-java-collections-conversions-3/README.md
+++ b/core-java-modules/core-java-collections-conversions-3/README.md
@@ -3,3 +3,4 @@
This module contains articles about conversions among Collection types in Java.
### Relevant Articles:
+- [Converting HashMap Values to an ArrayList in Java](https://www.baeldung.com/java-hashmap-arraylist)
diff --git a/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java b/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java
index 6e3b250938..80c403bb4e 100644
--- a/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java
+++ b/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java
@@ -12,10 +12,10 @@ public class EpochTimeToLocalDateTimeConverterUnitTest {
@Test
public void testConvertEpochTimeToLocalDateTime() {
long epochTimeMillis = 1624962431000L; // Example epoch time in milliseconds
- LocalDateTime expectedDateTime = LocalDateTime.of(2021, 6, 29, 12, 13, 51);
+ LocalDateTime expectedDateTime = LocalDateTime.of(2021, 6, 29, 10, 27, 11);
Instant instant = Instant.ofEpochMilli(epochTimeMillis);
- ZoneId zoneId = ZoneId.systemDefault();
+ ZoneId zoneId = ZoneId.of("UTC");
LocalDateTime actualDateTime = instant.atZone(zoneId).toLocalDateTime();
assertEquals(expectedDateTime, actualDateTime);
diff --git a/core-java-modules/core-java-io-5/README.md b/core-java-modules/core-java-io-5/README.md
index 6072e0b6d9..9fb4b967a4 100644
--- a/core-java-modules/core-java-io-5/README.md
+++ b/core-java-modules/core-java-io-5/README.md
@@ -3,6 +3,6 @@
This module contains articles about core Java input and output (IO)
### Relevant Articles:
-
+- [Get File Extension From MIME Type in Java](https://www.baeldung.com/java-mime-type-file-extension)
- [[<-- Prev]](/core-java-modules/core-java-io-4)
diff --git a/core-java-modules/core-java-numbers-6/pom.xml b/core-java-modules/core-java-numbers-6/pom.xml
index 531f1293d1..7a3b3d4426 100644
--- a/core-java-modules/core-java-numbers-6/pom.xml
+++ b/core-java-modules/core-java-numbers-6/pom.xml
@@ -25,8 +25,12 @@
${commons-codec}
test
+
+ com.google.guava
+ guava
+ ${guava.version}
+
-
core-java-numbers-6
@@ -39,5 +43,6 @@
1.15
+ 32.1.2-jre
\ No newline at end of file
diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java
new file mode 100644
index 0000000000..2ac57a9c0c
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java
@@ -0,0 +1,8 @@
+package com.baeldung.bigintegerroot;
+
+public class BenchmarkRunner {
+ public static void main(String[] args) throws Exception {
+ org.openjdk.jmh.Main.main(args);
+ }
+}
+
diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java
new file mode 100644
index 0000000000..c2d2f30827
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java
@@ -0,0 +1,10 @@
+package com.baeldung.bigintegerroot;
+
+public class BigIntegerHolder {
+
+ private BigIntegerHolder() {
+ }
+ public static final String BIG_NUMBER = "179769313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
+ public static final String VERY_BIG_NUMBER = "32473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834";
+ public static final String INSANELY_BIG_NUMBER = "3247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834";
+}
diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java
new file mode 100644
index 0000000000..645e4eb2dd
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java
@@ -0,0 +1,60 @@
+package com.baeldung.bigintegerroot;
+
+import static com.baeldung.bigintegerroot.BigIntegerHolder.*;
+
+import com.baeldung.bigintegerroot.algorithms.Newton;
+import com.baeldung.bigintegerroot.algorithms.NewtonPlus;
+import com.google.common.math.BigIntegerMath;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.concurrent.TimeUnit;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+@Warmup(iterations = 1)
+@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.MINUTES)
+@Fork(1)
+@State(Scope.Benchmark)
+public class BigIntegerSquareRootBenchmark {
+
+ @Param({BIG_NUMBER, VERY_BIG_NUMBER, INSANELY_BIG_NUMBER})
+ public String number;
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ public void calculateRootWithJava(Blackhole blackhole) {
+ final BigInteger integer = new BigInteger(number);
+ final BigInteger root = integer.sqrt();
+ blackhole.consume(root);
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ public void calculateRootWithGuava(Blackhole blackhole) {
+ final BigInteger integer = new BigInteger(number);
+ final BigInteger root = BigIntegerMath.sqrt(integer, RoundingMode.DOWN);
+ blackhole.consume(root);
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ public void calculateRootWithNewtonPlus(Blackhole blackhole) {
+ final BigInteger integer = new BigInteger(number);
+ final BigInteger root = NewtonPlus.sqrt(integer);
+ blackhole.consume(root);
+ }
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ public void calculateRootWithNewton(Blackhole blackhole) {
+ final BigInteger integer = new BigInteger(number);
+ final BigInteger root = Newton.sqrt(integer);
+ blackhole.consume(root);
+ }
+}
diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java
new file mode 100644
index 0000000000..07acba7537
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java
@@ -0,0 +1,29 @@
+package com.baeldung.bigintegerroot.algorithms;
+
+import java.math.BigInteger;
+
+public class Newton {
+
+ private Newton() {
+ }
+
+ public static BigInteger sqrt(BigInteger n) {
+ // Initial approximation
+ BigInteger x = n.divide(BigInteger.TWO);
+
+ // Tolerance level (small positive integer)
+ BigInteger tolerance = BigInteger.ONE;
+
+ while (true) {
+ // x_new = 0.5 * (x + n / x)
+ BigInteger xNew = x.add(n.divide(x)).divide(BigInteger.TWO);
+
+ // Check for convergence within tolerance
+ if (x.subtract(xNew).abs().compareTo(tolerance) <= 0) {
+ return xNew;
+ }
+
+ x = xNew;
+ }
+ }
+}
diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java
new file mode 100644
index 0000000000..80d50c0ca5
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java
@@ -0,0 +1,108 @@
+package com.baeldung.bigintegerroot.algorithms;
+
+import java.math.BigInteger;
+
+public class NewtonPlus {
+
+ private NewtonPlus() {
+ }
+
+ // A fast square root by Ryan Scott White.
+ public static BigInteger sqrt(BigInteger x) {
+ if (x.compareTo(BigInteger.valueOf(144838757784765629L)) < 0) {
+ long xAsLong = x.longValue();
+ long vInt = (long)Math.sqrt(xAsLong);
+ if (vInt * vInt > xAsLong)
+ vInt--;
+ return BigInteger.valueOf(vInt); }
+
+ double xAsDub = x.doubleValue();
+ BigInteger val;
+ if (xAsDub < 2.1267e37) // 2.12e37 largest here
+ // since sqrt(long.max*long.max) > long.max
+ {
+ long vInt = (long)Math.sqrt(xAsDub);
+ val = BigInteger.valueOf
+ ((vInt + x.divide(BigInteger.valueOf(vInt)).longValue()) >> 1);
+ }
+ else if (xAsDub < 4.3322e127) {
+ // Convert a double to a BigInteger
+ long bits = Double.doubleToLongBits(Math.sqrt(xAsDub));
+ int exp = ((int) (bits >> 52) & 0x7ff) - 1075;
+ val = BigInteger.valueOf((bits & ((1L << 52)) - 1) | (1L << 52)).shiftLeft(exp);
+
+ val = x.divide(val).add(val).shiftRight(1);
+ if (xAsDub > 2e63) {
+ val = x.divide(val).add(val).shiftRight(1); }
+ }
+ else // handle large numbers over 4.3322e127
+ {
+ int xLen = x.bitLength();
+ int wantedPrecision = ((xLen + 1) / 2);
+ int xLenMod = xLen + (xLen & 1) + 1;
+
+ //////// Do the first Sqrt on Hardware ////////
+ long tempX = x.shiftRight(xLenMod - 63).longValue();
+ double tempSqrt1 = Math.sqrt(tempX);
+ long valLong = Double.doubleToLongBits(tempSqrt1) & 0x1fffffffffffffL;
+
+ if (valLong == 0)
+ valLong = 1L << 53;
+
+ //////// Classic Newton Iterations ////////
+ val = BigInteger.valueOf(valLong).shiftLeft(53 - 1)
+ .add((x.shiftRight(xLenMod -
+ (3 * 53))).divide(BigInteger.valueOf(valLong)));
+
+ int size = 106;
+ for (; size < 256; size <<= 1) {
+ val = val.shiftLeft(size - 1).add(x.shiftRight
+ (xLenMod - (3*size)).divide(val));}
+
+ if (xAsDub > 4e254) { // 4e254 = 1<<845.77
+ int numOfNewtonSteps = 31 -
+ Integer.numberOfLeadingZeros(wantedPrecision / size)+1;
+
+ ////// Apply Starting Size ////////
+ int wantedSize = (wantedPrecision >> numOfNewtonSteps) + 2;
+ int needToShiftBy = size - wantedSize;
+ val = val.shiftRight(needToShiftBy);
+
+ size = wantedSize;
+ do {
+ //////// Newton Plus Iteration ////////
+ int shiftX = xLenMod - (3 * size);
+ BigInteger valSqrd = val.multiply(val).shiftLeft(size - 1);
+ BigInteger valSU = x.shiftRight(shiftX).subtract(valSqrd);
+ val = val.shiftLeft(size).add(valSU.divide(val));
+ size *= 2;
+ } while (size < wantedPrecision);
+ }
+ val = val.shiftRight(size - wantedPrecision);
+ }
+
+ // Detect a round ups. This function can be further optimized - see article.
+ // For a ~7% speed bump the following line can be removed but round-ups will occur.
+ if (val.multiply(val).compareTo(x) > 0)
+ val = val.subtract(BigInteger.ONE);
+
+ // Enabling the below will guarantee an error is stopped for larger numbers.
+ // Note: As of this writing, there are no known errors.
+ BigInteger tmp = val.multiply(val);
+ if (tmp.compareTo(x) > 0) {
+ System.out.println("val^2(" + val.multiply(val).toString()
+ + ") ≥ x(" + x.toString()+")");
+ System.console().readLine();
+ //throw new Exception("Sqrt function had internal error - value too high");
+ }
+ if (tmp.add(val.shiftLeft(1)).add(BigInteger.ONE).compareTo(x) <= 0) {
+ System.out.println("(val+1)^2("
+ + val.add(BigInteger.ONE).multiply(val.add(BigInteger.ONE)).toString()
+ + ") ≥ x(" + x.toString() + ")");
+ System.console().readLine();
+ //throw new Exception("Sqrt function had internal error - value too low");
+ }
+
+ return val;
+ }
+}
diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java
new file mode 100644
index 0000000000..edb75b16ef
--- /dev/null
+++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.bigintegerroot;
+
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.baeldung.bigintegerroot.algorithms.Newton;
+import com.baeldung.bigintegerroot.algorithms.NewtonPlus;
+import com.google.common.math.BigIntegerMath;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.stream.Stream;
+import org.apache.commons.math3.util.Pair;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class BigIntegerSquareRootUnitTest {
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ BigIntegerHolder.BIG_NUMBER,
+ BigIntegerHolder.VERY_BIG_NUMBER,
+ BigIntegerHolder.VERY_BIG_NUMBER
+ })
+ void squareRootTest(String number) {
+ final BigInteger bigInteger = new BigInteger(number);
+ final BigInteger javaRoot = bigInteger.sqrt();
+ final BigInteger guavaRoot = BigIntegerMath.sqrt(bigInteger, RoundingMode.DOWN);
+ final BigInteger newtonRoot = Newton.sqrt(bigInteger);
+ final BigInteger newtonPlusRoot = NewtonPlus.sqrt(bigInteger);
+
+ assertTrue(Stream.of(
+ new Pair<>(javaRoot, guavaRoot),
+ new Pair<>(guavaRoot, newtonRoot),
+ new Pair<>(newtonRoot, newtonPlusRoot)
+ ).allMatch(pair -> pair.getFirst().equals(pair.getSecond())));
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-os/README.md b/core-java-modules/core-java-os/README.md
index 6d477de70a..81e67e4663 100644
--- a/core-java-modules/core-java-os/README.md
+++ b/core-java-modules/core-java-os/README.md
@@ -14,5 +14,6 @@ This module contains articles about working with the operating system (OS) in Ja
- [How to Run a Shell Command in Java](http://www.baeldung.com/run-shell-command-in-java)
- [Taking Screenshots Using Java](https://www.baeldung.com/java-taking-screenshots)
- [Java Sound API – Capturing Microphone](https://www.baeldung.com/java-sound-api-capture-mic)
+- [How to Detect the Username Using Java](https://www.baeldung.com/java-get-username)
This module uses Java 9, so make sure to have the JDK 9 installed to run it.
diff --git a/core-java-modules/core-java-regex-2/README.md b/core-java-modules/core-java-regex-2/README.md
index 02e470cce5..404b33b65f 100644
--- a/core-java-modules/core-java-regex-2/README.md
+++ b/core-java-modules/core-java-regex-2/README.md
@@ -9,4 +9,5 @@
- [Regular Expression: \z vs \Z Anchors in Java](https://www.baeldung.com/java-regular-expression-z-vs-z-anchors)
- [Extract Text Between Square Brackets](https://www.baeldung.com/java-get-content-between-square-brackets)
- [Get the Indexes of Regex Pattern Matches in Java](https://www.baeldung.com/java-indexes-regex-pattern-matches)
+- [Check if a String is Strictly Alphanumeric With Java](https://www.baeldung.com/java-check-string-contains-only-letters-numbers)
- More articles: [[<-- prev]](/core-java-modules/core-java-regex)
diff --git a/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java b/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java
index df7a57a984..ab5993d816 100644
--- a/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java
+++ b/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java
@@ -45,8 +45,8 @@ public class FruitArrayJacksonUnmarshalUnitTest {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
- from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class))
- .to("mock:marshalledObject");
+ from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class))
+ .to("mock:marshalledObject");
}
};
}
diff --git a/parent-spring-6/pom.xml b/parent-spring-6/pom.xml
index 7b61ac524b..7b28afc9b1 100644
--- a/parent-spring-6/pom.xml
+++ b/parent-spring-6/pom.xml
@@ -35,7 +35,7 @@
- 6.0.10
+ 6.0.12
diff --git a/spring-6-rsocket/README.md b/spring-6-rsocket/README.md
index 21ab282bd1..95c805b0bf 100644
--- a/spring-6-rsocket/README.md
+++ b/spring-6-rsocket/README.md
@@ -3,5 +3,5 @@
This module contains articles about RSocket in Spring Framework 6.
### Relevant articles
+- [RSocket Interface in Spring 6](https://www.baeldung.com/spring-rsocket)
-- [Introduction to RSocket](#)
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml
index 1ccf436bbf..7f630fa96e 100644
--- a/spring-boot-modules/spring-boot-actuator/pom.xml
+++ b/spring-boot-modules/spring-boot-actuator/pom.xml
@@ -9,9 +9,10 @@
This is simple boot application for Spring boot actuator test
- com.baeldung.spring-boot-modules
- spring-boot-modules
- 1.0.0-SNAPSHOT
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
@@ -39,16 +40,6 @@
com.h2database
h2
-
- javax.servlet
- javax.servlet-api
- provided
-
-
- javax.servlet
- jstl
- runtime
-
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java
index 894c24693e..20ee834d52 100644
--- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java
@@ -1,36 +1,56 @@
package com.baeldung.endpoints.enabling;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.crypto.factory.PasswordEncoderFactories;
-import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
-@Configuration
@EnableWebSecurity
-public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+@Configuration
+public class SecurityConfiguration {
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
- auth.inMemoryAuthentication()
- .withUser("user")
- .password(encoder.encode("password"))
- .roles("USER")
- .and()
- .withUser("admin")
- .password(encoder.encode("admin"))
- .roles("USER", "ADMIN");
+ @Bean
+ MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
+ return new MvcRequestMatcher.Builder(introspector);
}
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint())
- .authorizeRequests((requests) -> requests.anyRequest()
- .hasRole("ADMIN"));
- http.httpBasic();
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
+ http.httpBasic(Customizer.withDefaults());
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests(authz -> {
+ authz.requestMatchers(mvc.pattern("/actuator/**"))
+ .hasRole("ADMIN")
+ .anyRequest()
+ .authenticated();
+ });
+
+ return http.build();
+ }
+
+
+
+ @Bean
+ public InMemoryUserDetailsManager userDetailsService() {
+ UserDetails user = User.withDefaultPasswordEncoder()
+ .username("user")
+ .password("password")
+ .roles("USER")
+ .build();
+ UserDetails admin = User.withDefaultPasswordEncoder()
+ .username("admin")
+ .password("password")
+ .roles("USER", "ADMIN")
+ .build();
+ return new InMemoryUserDetailsManager(user, admin);
}
}
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java
index db4e69127a..88f39d8ccc 100644
--- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java
@@ -1,9 +1,9 @@
package com.baeldung.endpoints.info;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
@Entity
@Table(name = "users")
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java
index 729b3c0b96..ee87412986 100644
--- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java
@@ -15,7 +15,7 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.context.request.RequestContextListener;
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
@EnableScheduling
@ComponentScan("com.baeldung.metrics")
diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
index 0f7579f060..a7aaddf0fb 100644
--- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
+++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java
@@ -7,14 +7,14 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
@Component
public class MetricFilter implements Filter {
diff --git a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java
index 8274619517..079195714b 100644
--- a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java
@@ -13,20 +13,20 @@ import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest
@AutoConfigureMockMvc
-public class EndpointEnablingIntegrationTest {
+class EndpointEnablingIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(username = "user", password = "password", roles = "USER")
- public void givenWrongAuthentication_whenCallingActuator_thenReturns401() throws Exception {
+ void givenWrongAuthentication_whenCallingActuator_thenReturns401() throws Exception {
mockMvc.perform(get("/actuator"))
.andExpect(status().isForbidden());
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
- public void givenProperAuthentication_whenCallingActuator_thenReturnsExpectedEndpoints() throws Exception {
+ void givenProperAuthentication_whenCallingActuator_thenReturnsExpectedEndpoints() throws Exception {
mockMvc.perform(get("/actuator"))
.andExpect(jsonPath("$._links").exists())
.andExpect(jsonPath("$._links.beans").exists())
diff --git a/spring-boot-modules/spring-boot-documentation/pom.xml b/spring-boot-modules/spring-boot-documentation/pom.xml
index f0806b3c75..3ac2799258 100644
--- a/spring-boot-modules/spring-boot-documentation/pom.xml
+++ b/spring-boot-modules/spring-boot-documentation/pom.xml
@@ -93,8 +93,8 @@
2.2.11
- 0.12.1
- 0.8.0
+ 0.14.0
+ 0.14.0
1.18.3
diff --git a/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json b/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json
index 0198733c1c..45be27f571 100644
--- a/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json
+++ b/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json
@@ -18,7 +18,9 @@
"operationId": "incoming-topic_publish",
"description": "More details for the incoming topic",
"bindings": {
- "kafka": { }
+ "kafka": {
+ "bindingVersion": "0.4.0"
+ }
},
"message": {
"schemaFormat": "application/vnd.oai.openapi+json;version=3.0.0",
@@ -32,7 +34,9 @@
"$ref": "#/components/schemas/SpringKafkaDefaultHeadersIncomingPayloadDto"
},
"bindings": {
- "kafka": { }
+ "kafka": {
+ "bindingVersion": "0.4.0"
+ }
}
}
}
@@ -42,7 +46,9 @@
"operationId": "outgoing-topic_subscribe",
"description": "More details for the outgoing topic",
"bindings": {
- "kafka": { }
+ "kafka": {
+ "bindingVersion": "0.4.0"
+ }
},
"message": {
"schemaFormat": "application/vnd.oai.openapi+json;version=3.0.0",
@@ -56,7 +62,9 @@
"$ref": "#/components/schemas/SpringKafkaDefaultHeadersOutgoingPayloadDto"
},
"bindings": {
- "kafka": { }
+ "kafka": {
+ "bindingVersion": "0.4.0"
+ }
}
}
}
diff --git a/spring-boot-modules/spring-boot-graalvm-docker/README.md b/spring-boot-modules/spring-boot-graalvm-docker/README.md
new file mode 100644
index 0000000000..10a764053e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-graalvm-docker/README.md
@@ -0,0 +1,2 @@
+## Relevant Articles
+- [Create a GraalVM Docker Image](https://www.baeldung.com/java-graalvm-docker-image)
diff --git a/spring-boot-modules/spring-boot-mvc-4/pom.xml b/spring-boot-modules/spring-boot-mvc-4/pom.xml
index dbbb03ad0e..0ae05a764b 100644
--- a/spring-boot-modules/spring-boot-mvc-4/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-4/pom.xml
@@ -9,9 +9,10 @@
Module For Spring Boot MVC Web
- com.baeldung.spring-boot-modules
- spring-boot-modules
- 1.0.0-SNAPSHOT
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java
index 80c75aa8b5..cedc60bd92 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java
@@ -4,10 +4,10 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
public class HelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java
index f276f94b7c..dbb8ea1b32 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java
@@ -4,10 +4,10 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
public class SpringHelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java
index 3f51a4ab69..b2d6f249a5 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java
@@ -2,7 +2,7 @@ package com.baeldung.common.error;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
-import javax.servlet.Servlet;
+import jakarta.servlet.Servlet;
public class SpringHelloServletRegistrationBean extends ServletRegistrationBean {
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java
index eadd40355a..7fd83fd59a 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java
@@ -4,9 +4,9 @@ import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRegistration;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRegistration;
public class WebAppInitializer implements WebApplicationInitializer {
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java
index 49dd9404b7..ee70a142be 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java
@@ -1,9 +1,9 @@
package com.baeldung.servlets.servlets;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java
index 992976ca0e..62706be348 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java
@@ -1,10 +1,10 @@
package com.baeldung.servlets.servlets.javaee;
-import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "AnnotationServlet", description = "Example Servlet Using Annotations", urlPatterns = { "/annotationservlet" })
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java
index c7b373064f..299a1b5d95 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java
@@ -1,9 +1,9 @@
package com.baeldung.servlets.servlets.javaee;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java
index ce3eae7ce0..eab5869cd5 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java
@@ -1,6 +1,6 @@
package com.baeldung.utils;
-import javax.annotation.security.RolesAllowed;
+import jakarta.annotation.security.RolesAllowed;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java
index 8c7f2f932a..40acc1801f 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java
@@ -1,6 +1,6 @@
package com.baeldung.utils.controller;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
diff --git a/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java b/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java
index 080f660c40..f59d00f871 100644
--- a/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java
@@ -1,32 +1,34 @@
package com.baeldung.utils;
import com.baeldung.utils.controller.UtilsController;
-import org.junit.Before;
-import org.junit.Test;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
-import org.mockito.MockitoAnnotations;
+
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import static org.mockito.MockitoAnnotations.openMocks;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-public class UtilsControllerIntegrationTest {
+class UtilsControllerIntegrationTest {
@InjectMocks
private UtilsController utilsController;
private MockMvc mockMvc;
- @Before
+ @BeforeEach
public void setup() {
- MockitoAnnotations.initMocks(this);
+ openMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(utilsController).build();
}
@Test
- public void givenParameter_setRequestParam_andSetSessionAttribute() throws Exception {
+ void givenParameter_setRequestParam_andSetSessionAttribute() throws Exception {
String param = "testparam";
this.mockMvc.perform(post("/setParam").param("param", param).sessionAttr("parameter", param)).andExpect(status().isOk());
}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java
new file mode 100644
index 0000000000..fd321a0e68
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java
@@ -0,0 +1,6 @@
+package com.baeldung.spytest;
+
+public interface ExternalAlertService {
+ public boolean alert(Order order);
+
+}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java
new file mode 100644
index 0000000000..f0b2f92bee
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java
@@ -0,0 +1,18 @@
+package com.baeldung.spytest;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class NotificationService {
+
+ private ExternalAlertService externalAlertService;
+
+ public void notify(Order order) {
+ System.out.println(order);
+ }
+
+ public boolean raiseAlert(Order order) {
+ return externalAlertService.alert(order);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java
new file mode 100644
index 0000000000..23f5a05e9d
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java
@@ -0,0 +1,49 @@
+package com.baeldung.spytest;
+
+import java.util.UUID;
+
+public class Order {
+
+ private UUID id;
+
+ private String name;
+
+ private OrderType orderType;
+
+ private double orderQuantity;
+
+ private String address;
+
+ public Order(UUID id, String name, double orderQuantity, String address) {
+ this.id = id;
+ this.name = name;
+ this.orderQuantity = orderQuantity;
+ this.address = address;
+ }
+
+ public enum OrderType {
+ INDIVIDUAL, BULK;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public void setId(UUID id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public double getOrderQuantity() {
+ return orderQuantity;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+}
+
+
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java
new file mode 100644
index 0000000000..90fe5ba2b8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java
@@ -0,0 +1,19 @@
+package com.baeldung.spytest;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class OrderRepository {
+
+ public static final HashMap orders = new HashMap<>();
+
+ public Order save(Order order) {
+ UUID orderId = UUID.randomUUID();
+ order.setId(orderId);
+ orders.put(UUID.randomUUID(), order);
+ return order;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java
new file mode 100644
index 0000000000..47647b8721
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java
@@ -0,0 +1,25 @@
+package com.baeldung.spytest;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class OrderService {
+
+ public final OrderRepository orderRepository;
+
+ public final NotificationService notificationService;
+
+ public OrderService(OrderRepository orderRepository, NotificationService notificationService) {
+ this.orderRepository = orderRepository;
+ this.notificationService = notificationService;
+ }
+
+ public Order save(Order order) {
+ order = orderRepository.save(order);
+ notificationService.notify(order);
+ if (!notificationService.raiseAlert(order)) {
+ throw new RuntimeException("Alert not raised");
+ }
+ return order;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java
new file mode 100644
index 0000000000..d8d81378df
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.spytest;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpyTestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpyTestApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java
new file mode 100644
index 0000000000..96c7b09dfc
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java
@@ -0,0 +1,35 @@
+package com.baeldung.spytest;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+
+@SpringBootTest
+class OrderServiceIntegrationTest {
+
+ @Autowired
+ OrderRepository orderRepository;
+ @SpyBean
+ NotificationService notificationService;
+ @SpyBean
+ OrderService orderService;
+
+ @Test
+ void givenNotificationServiceIsUsingSpyBean_whenOrderServiceIsCalled_thenNotificationServiceSpyBeanShouldBeInvoked() {
+
+ Order orderInput = new Order(null, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP");
+ doReturn(true).when(notificationService)
+ .raiseAlert(any(Order.class));
+ Order order = orderService.save(orderInput);
+ Assertions.assertNotNull(order);
+ Assertions.assertNotNull(order.getId());
+ verify(notificationService).notify(any(Order.class));
+ }
+
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java
new file mode 100644
index 0000000000..21ae849bcf
--- /dev/null
+++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java
@@ -0,0 +1,41 @@
+package com.baeldung.spytest;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.util.UUID;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+class OrderServiceUnitTest {
+
+ @Spy
+ OrderRepository orderRepository;
+ @Spy
+ NotificationService notificationService;
+ @InjectMocks
+ OrderService orderService;
+
+ @Test
+ void givenNotificationServiceIsUsingSpy_whenOrderServiceIsCalled_thenNotificationServiceSpyShouldBeInvoked() {
+
+ UUID orderId = UUID.randomUUID();
+ Order orderInput = new Order(orderId, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP");
+ doReturn(orderInput).when(orderRepository)
+ .save(any());
+ doReturn(true).when(notificationService)
+ .raiseAlert(any(Order.class));
+ Order order = orderService.save(orderInput);
+ Assertions.assertNotNull(order);
+ Assertions.assertEquals(orderId, order.getId());
+ verify(notificationService).notify(any(Order.class));
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md
new file mode 100644
index 0000000000..b76ae19f26
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md
@@ -0,0 +1,7 @@
+## Spring Cloud Gateway
+
+This module contains articles about Spring Cloud Gateway
+
+### Relevant Articles:
+
+- [Exploring the New Spring Cloud Gateway](http://www.baeldung.com/spring-cloud-gateway)
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml
new file mode 100644
index 0000000000..3d64edc338
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+ gateway-2
+ gateway-2
+ jar
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../../parent-boot-2
+
+
+
+
+
+ org.junit
+ junit-bom
+ ${junit-jupiter.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+
+
+ 2021.0.3
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java
new file mode 100644
index 0000000000..e209b6cdf0
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.springcloudgateway.custompredicates;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+@SpringBootApplication
+public class CustomPredicatesApplication {
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(CustomPredicatesApplication.class)
+ .profiles("customroutes")
+ .run(args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java
new file mode 100644
index 0000000000..ea58eb7e46
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java
@@ -0,0 +1,43 @@
+package com.baeldung.springcloudgateway.custompredicates.config;
+
+import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.baeldung.springcloudgateway.custompredicates.factories.GoldenCustomerRoutePredicateFactory;
+import com.baeldung.springcloudgateway.custompredicates.factories.GoldenCustomerRoutePredicateFactory.Config;
+import com.baeldung.springcloudgateway.custompredicates.service.GoldenCustomerService;
+
+@Configuration
+public class CustomPredicatesConfig {
+
+
+ @Bean
+ public GoldenCustomerRoutePredicateFactory goldenCustomer(GoldenCustomerService goldenCustomerService) {
+ return new GoldenCustomerRoutePredicateFactory(goldenCustomerService);
+ }
+
+
+ //@Bean
+ public RouteLocator routes(RouteLocatorBuilder builder, GoldenCustomerRoutePredicateFactory gf ) {
+
+ return builder.routes()
+ .route("dsl_golden_route", r ->
+ r.predicate(gf.apply(new Config(true, "customerId")))
+ .and()
+ .path("/dsl_api/**")
+ .filters(f -> f.stripPrefix(1))
+ .uri("https://httpbin.org")
+ )
+ .route("dsl_common_route", r ->
+ r.predicate(gf.apply(new Config(false, "customerId")))
+ .and()
+ .path("/dsl_api/**")
+ .filters(f -> f.stripPrefix(1))
+ .uri("https://httpbin.org")
+ )
+ .build();
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java
new file mode 100644
index 0000000000..cb5c3a0b50
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java
@@ -0,0 +1,102 @@
+/**
+ *
+ */
+package com.baeldung.springcloudgateway.custompredicates.factories;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.validation.constraints.NotEmpty;
+
+import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
+import org.springframework.http.HttpCookie;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.server.ServerWebExchange;
+
+import com.baeldung.springcloudgateway.custompredicates.service.GoldenCustomerService;
+
+/**
+ * @author Philippe
+ *
+ */
+public class GoldenCustomerRoutePredicateFactory extends AbstractRoutePredicateFactory {
+
+ private final GoldenCustomerService goldenCustomerService;
+
+ public GoldenCustomerRoutePredicateFactory(GoldenCustomerService goldenCustomerService ) {
+ super(Config.class);
+ this.goldenCustomerService = goldenCustomerService;
+ }
+
+
+ @Override
+ public List shortcutFieldOrder() {
+ return Arrays.asList("isGolden","customerIdCookie");
+ }
+
+
+ @Override
+ public Predicate apply(Config config) {
+
+ return (ServerWebExchange t) -> {
+ List cookies = t.getRequest()
+ .getCookies()
+ .get(config.getCustomerIdCookie());
+
+ boolean isGolden;
+ if ( cookies == null || cookies.isEmpty()) {
+ isGolden = false;
+ }
+ else {
+ String customerId = cookies.get(0).getValue();
+ isGolden = goldenCustomerService.isGoldenCustomer(customerId);
+ }
+
+ return config.isGolden()?isGolden:!isGolden;
+ };
+ }
+
+
+ @Validated
+ public static class Config {
+ boolean isGolden = true;
+
+ @NotEmpty
+ String customerIdCookie = "customerId";
+
+
+ public Config() {}
+
+ public Config( boolean isGolden, String customerIdCookie) {
+ this.isGolden = isGolden;
+ this.customerIdCookie = customerIdCookie;
+ }
+
+ public boolean isGolden() {
+ return isGolden;
+ }
+
+ public void setGolden(boolean value) {
+ this.isGolden = value;
+ }
+
+ /**
+ * @return the customerIdCookie
+ */
+ public String getCustomerIdCookie() {
+ return customerIdCookie;
+ }
+
+ /**
+ * @param customerIdCookie the customerIdCookie to set
+ */
+ public void setCustomerIdCookie(String customerIdCookie) {
+ this.customerIdCookie = customerIdCookie;
+ }
+
+
+
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java
new file mode 100644
index 0000000000..82bf2e6ae9
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java
@@ -0,0 +1,26 @@
+/**
+ *
+ */
+package com.baeldung.springcloudgateway.custompredicates.service;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Philippe
+ *
+ */
+@Component
+public class GoldenCustomerService {
+
+ public boolean isGoldenCustomer(String customerId) {
+
+ // TODO: Add some AI logic to check is this customer deserves a "golden" status ;^)
+ if ( "baeldung".equalsIgnoreCase(customerId)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java
new file mode 100644
index 0000000000..d276597a6b
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.springcloudgateway.introduction;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+
+@SpringBootApplication
+@PropertySource("classpath:introduction-application.properties")
+public class IntroductionGatewayApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(IntroductionGatewayApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml
new file mode 100644
index 0000000000..859aa60bda
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml
@@ -0,0 +1,26 @@
+spring:
+ cloud:
+ gateway:
+ routes:
+ - id: golden_route
+ uri: https://httpbin.org
+ predicates:
+ - Path=/api/**
+ - GoldenCustomer=true
+ filters:
+ - StripPrefix=1
+ - AddRequestHeader=GoldenCustomer,true
+ - id: common_route
+ uri: https://httpbin.org
+ predicates:
+ - Path=/api/**
+ - name: GoldenCustomer
+ args:
+ golden: false
+ customerIdCookie: customerId
+ filters:
+ - StripPrefix=1
+ - AddRequestHeader=GoldenCustomer,false
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml
new file mode 100644
index 0000000000..a33bca2055
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml
@@ -0,0 +1,4 @@
+logging:
+ level:
+ org.springframework.cloud.gateway: DEBUG
+ reactor.netty.http.client: DEBUG
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties
new file mode 100644
index 0000000000..d7a6c4e072
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties
@@ -0,0 +1,7 @@
+spring.cloud.gateway.routes[0].id=baeldung_route
+spring.cloud.gateway.routes[0].uri=http://www.baeldung.com
+spring.cloud.gateway.routes[0].predicates[0]=Path=/baeldung
+
+management.endpoints.web.exposure.include=*
+
+server.port=80
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/logback.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/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-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java
new file mode 100644
index 0000000000..d9988ceb5e
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java
@@ -0,0 +1,67 @@
+package com.baeldung.springcloudgateway.custompredicates;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.net.URI;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.Before;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+
+/**
+ * This test requires
+ */
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("customroutes")
+public class CustomPredicatesApplicationLiveTest {
+
+ @LocalServerPort
+ String serverPort;
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Test
+ void givenNormalCustomer_whenCallHeadersApi_thenResponseForNormalCustomer() throws JSONException {
+
+ String url = "http://localhost:" + serverPort + "/api/headers";
+ ResponseEntity response = restTemplate.getForEntity(url, String.class);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+ JSONObject json = new JSONObject(response.getBody());
+ JSONObject headers = json.getJSONObject("headers");
+ assertThat(headers.getString("Goldencustomer")).isEqualTo("false");
+
+ }
+
+ @Test
+ void givenGoldenCustomer_whenCallHeadersApi_thenResponseForGoldenCustomer() throws JSONException {
+
+ String url = "http://localhost:" + serverPort + "/api/headers";
+ RequestEntity request = RequestEntity
+ .get(URI.create(url))
+ .header("Cookie", "customerId=baeldung")
+ .build();
+
+ ResponseEntity response = restTemplate.exchange(request, String.class);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+ JSONObject json = new JSONObject(response.getBody());
+ JSONObject headers = json.getJSONObject("headers");
+ assertThat(headers.getString("Goldencustomer")).isEqualTo("true");
+
+ }
+
+}
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java
new file mode 100644
index 0000000000..33855cd15d
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java
@@ -0,0 +1,25 @@
+package com.baeldung.springcloudgateway.introduction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+public class LoggerListAppender extends AppenderBase {
+
+ static private List events = new ArrayList<>();
+
+ @Override
+ protected void append(ILoggingEvent eventObject) {
+ events.add(eventObject);
+ }
+
+ public static List getEvents() {
+ return events;
+ }
+
+ public static void clearEventList() {
+ events.clear();
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java
new file mode 100644
index 0000000000..1550265f22
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java
@@ -0,0 +1,15 @@
+package com.baeldung.springcloudgateway.introduction;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import com.baeldung.springcloudgateway.introduction.IntroductionGatewayApplication;
+
+
+@SpringBootTest(classes = IntroductionGatewayApplication.class)
+public class SpringContextTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..90c8f570aa
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml
index e7fe7e7485..f4d4073ef1 100644
--- a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml
+++ b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml
@@ -18,6 +18,7 @@
config
discovery
gateway
+ gateway-2
svc-book
svc-rating
customer-service
diff --git a/testing-modules/junit-5-advanced/README.md b/testing-modules/junit-5-advanced/README.md
index a89bcd2de2..873ab0835b 100644
--- a/testing-modules/junit-5-advanced/README.md
+++ b/testing-modules/junit-5-advanced/README.md
@@ -9,3 +9,4 @@
- [JUnit – Testing Methods That Call System.exit()](https://www.baeldung.com/junit-system-exit)
- [Single Assert Call for Multiple Properties in Java Unit Testing](https://www.baeldung.com/java-testing-single-assert-multiple-properties)
- [Creating a Test Suite With JUnit](https://www.baeldung.com/java-junit-test-suite)
+- [Testing Interface Contract in Java](https://www.baeldung.com/java-junit-verify-interface-contract)