diff --git a/aws-lambda/README.md b/aws-lambda/README.md
index 2fbdaace10..759c9dd506 100644
--- a/aws-lambda/README.md
+++ b/aws-lambda/README.md
@@ -5,3 +5,4 @@ This module contains articles about AWS Lambda
### Relevant Articles:
- [Using AWS Lambda with API Gateway](https://www.baeldung.com/aws-lambda-api-gateway)
- [Introduction to AWS Serverless Application Model](https://www.baeldung.com/aws-serverless)
+- [How to Implement Hibernate in an AWS Lambda Function in Java](https://www.baeldung.com/java-aws-lambda-hibernate)
diff --git a/core-groovy-strings/pom.xml b/core-groovy-strings/pom.xml
new file mode 100644
index 0000000000..1144d748ee
--- /dev/null
+++ b/core-groovy-strings/pom.xml
@@ -0,0 +1,122 @@
+
+
+ 4.0.0
+ core-groovy-strings
+ 1.0-SNAPSHOT
+ core-groovy-strings
+ jar
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.codehaus.groovy
+ groovy
+ ${groovy.version}
+
+
+ org.codehaus.groovy
+ groovy-all
+ ${groovy-all.version}
+ pom
+
+
+ org.codehaus.groovy
+ groovy-dateutil
+ ${groovy.version}
+
+
+ org.codehaus.groovy
+ groovy-sql
+ ${groovy-sql.version}
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.hsqldb
+ hsqldb
+ ${hsqldb.version}
+ test
+
+
+ org.spockframework
+ spock-core
+ ${spock-core.version}
+ test
+
+
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ ${gmavenplus-plugin.version}
+
+
+
+ addSources
+ addTestSources
+ compile
+ compileTests
+
+
+
+
+
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ ${junit.platform.version}
+
+
+
+
+ junit5
+
+ integration-test
+ verify
+
+
+
+ **/*Test5.java
+
+
+
+
+
+
+
+
+
+
+ central
+ https://jcenter.bintray.com
+
+
+
+
+ 1.0.0
+ 2.5.6
+ 2.5.6
+ 2.5.6
+ 2.4.0
+ 1.1-groovy-2.4
+ 1.6
+
+
+
\ No newline at end of file
diff --git a/core-groovy-strings/src/test/groovy/com/baeldung/removeprefix/RemovePrefixTest.groovy b/core-groovy-strings/src/test/groovy/com/baeldung/removeprefix/RemovePrefixTest.groovy
new file mode 100644
index 0000000000..61b81fe1b2
--- /dev/null
+++ b/core-groovy-strings/src/test/groovy/com/baeldung/removeprefix/RemovePrefixTest.groovy
@@ -0,0 +1,70 @@
+package com.baeldung.removeprefix
+
+import org.junit.Assert
+import org.junit.Test
+
+class RemovePrefixTest {
+
+
+ @Test
+ public void whenCasePrefixIsRemoved_thenReturnTrue() {
+ def trimPrefix = {
+ it.startsWith('Groovy-') ? it.minus('Groovy-') : it
+ }
+
+ def actual = trimPrefix("Groovy-Tutorials at Baeldung")
+ def expected = "Tutorials at Baeldung"
+
+ Assert.assertEquals(expected, actual)
+ }
+
+ @Test
+ public void whenPrefixIsRemoved_thenReturnTrue() {
+
+ String prefix = "groovy-"
+ String trimPrefix = "Groovy-Tutorials at Baeldung"
+ def actual;
+ if(trimPrefix.startsWithIgnoreCase(prefix)) {
+ actual = trimPrefix.substring(prefix.length())
+ }
+
+ def expected = "Tutorials at Baeldung"
+
+ Assert.assertEquals(expected, actual)
+ }
+
+ @Test
+ public void whenPrefixIsRemovedUsingRegex_thenReturnTrue() {
+
+ def regex = ~"^([Gg])roovy-"
+ String trimPrefix = "Groovy-Tutorials at Baeldung"
+ String actual = trimPrefix - regex
+
+ def expected = "Tutorials at Baeldung"
+
+ Assert.assertEquals(expected, actual)
+ }
+
+ @Test
+ public void whenPrefixIsRemovedUsingReplaceFirst_thenReturnTrue() {
+ def regex = ~"^groovy"
+ String trimPrefix = "groovyTutorials at Baeldung's groovy page"
+ String actual = trimPrefix.replaceFirst(regex, "")
+
+ def expected = "Tutorials at Baeldung's groovy page"
+
+ Assert.assertEquals(expected, actual)
+ }
+
+ @Test
+ public void whenPrefixIsRemovedUsingReplaceAll_thenReturnTrue() {
+
+ String trimPrefix = "groovyTutorials at Baeldung groovy"
+ String actual = trimPrefix.replaceAll(/^groovy/, "")
+
+ def expected = "Tutorials at Baeldung groovy"
+
+ Assert.assertEquals(expected, actual)
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-arrays-guides/README.md b/core-java-modules/core-java-arrays-guides/README.md
index 621443e4a9..934833b31b 100644
--- a/core-java-modules/core-java-arrays-guides/README.md
+++ b/core-java-modules/core-java-arrays-guides/README.md
@@ -6,3 +6,4 @@ This module contains complete guides about arrays in Java
- [Arrays in Java: A Reference Guide](https://www.baeldung.com/java-arrays-guide)
- [Guide to the java.util.Arrays Class](https://www.baeldung.com/java-util-arrays)
- [What is \[Ljava.lang.Object;?](https://www.baeldung.com/java-tostring-array)
+- [Guide to ArrayStoreException](https://www.baeldung.com/java-arraystoreexception)
diff --git a/core-java-modules/core-java-date-operations-1/src/test/java/com/baeldung/date/DateDiffUnitTest.java b/core-java-modules/core-java-date-operations-1/src/test/java/com/baeldung/date/DateDiffUnitTest.java
index 226556d4bb..9a0779ccac 100644
--- a/core-java-modules/core-java-date-operations-1/src/test/java/com/baeldung/date/DateDiffUnitTest.java
+++ b/core-java-modules/core-java-date-operations-1/src/test/java/com/baeldung/date/DateDiffUnitTest.java
@@ -1,6 +1,8 @@
package com.baeldung.date;
-import static org.junit.Assert.assertEquals;
+import org.joda.time.Days;
+import org.joda.time.Minutes;
+import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -16,7 +18,7 @@ import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
-import org.junit.Test;
+import static org.junit.Assert.*;
public class DateDiffUnitTest {
@@ -29,18 +31,39 @@ public class DateDiffUnitTest {
long diffInMillies = Math.abs(secondDate.getTime() - firstDate.getTime());
long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
- assertEquals(diff, 6);
+ assertEquals(6, diff);
}
@Test
- public void givenTwoDatesInJava8_whenDifferentiating_thenWeGetSix() {
- LocalDate now = LocalDate.now();
- LocalDate sixDaysBehind = now.minusDays(6);
+ public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenWorks() {
+ LocalDate aDate = LocalDate.of(2020, 9, 11);
+ LocalDate sixDaysBehind = aDate.minusDays(6);
- Period period = Period.between(now, sixDaysBehind);
+ Period period = Period.between(aDate, sixDaysBehind);
int diff = Math.abs(period.getDays());
- assertEquals(diff, 6);
+ assertEquals(6, diff);
+ }
+
+ @Test
+ public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenDoesNotWork() {
+ LocalDate aDate = LocalDate.of(2020, 9, 11);
+ LocalDate sixtyDaysBehind = aDate.minusDays(60);
+ Period period = Period.between(aDate, sixtyDaysBehind);
+ int diff = Math.abs(period.getDays());
+ //not equals
+ assertNotEquals(60, diff);
+ }
+
+ @Test
+ public void givenTwoDatesInJava8_whenUsingPeriod_thenWeGet0Year1Month29Days() {
+ LocalDate aDate = LocalDate.of(2020, 9, 11);
+ LocalDate sixtyDaysBehind = aDate.minusDays(60);
+ Period period = Period.between(aDate, sixtyDaysBehind);
+ int years = Math.abs(period.getYears());
+ int months = Math.abs(period.getMonths());
+ int days = Math.abs(period.getDays());
+ assertArrayEquals(new int[] { 0, 1, 29 }, new int[] { years, months, days });
}
@Test
@@ -51,7 +74,7 @@ public class DateDiffUnitTest {
Duration duration = Duration.between(now, sixMinutesBehind);
long diff = Math.abs(duration.toMinutes());
- assertEquals(diff, 6);
+ assertEquals(6, diff);
}
@Test
@@ -61,7 +84,7 @@ public class DateDiffUnitTest {
long diff = ChronoUnit.SECONDS.between(now, tenSecondsLater);
- assertEquals(diff, 10);
+ assertEquals(10, diff);
}
@Test
@@ -69,9 +92,9 @@ public class DateDiffUnitTest {
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime now = ldt.atZone(ZoneId.of("America/Montreal"));
ZonedDateTime sixDaysBehind = now.withZoneSameInstant(ZoneId.of("Asia/Singapore"))
- .minusDays(6);
+ .minusDays(6);
long diff = ChronoUnit.DAYS.between(sixDaysBehind, now);
- assertEquals(diff, 6);
+ assertEquals(6, diff);
}
@Test
@@ -81,7 +104,7 @@ public class DateDiffUnitTest {
long diff = now.until(tenSecondsLater, ChronoUnit.SECONDS);
- assertEquals(diff, 10);
+ assertEquals(10, diff);
}
@Test
@@ -89,10 +112,9 @@ public class DateDiffUnitTest {
org.joda.time.LocalDate now = org.joda.time.LocalDate.now();
org.joda.time.LocalDate sixDaysBehind = now.minusDays(6);
- org.joda.time.Period period = new org.joda.time.Period(now, sixDaysBehind);
- long diff = Math.abs(period.getDays());
+ long diff = Math.abs(Days.daysBetween(now, sixDaysBehind).getDays());
- assertEquals(diff, 6);
+ assertEquals(6, diff);
}
@Test
@@ -100,8 +122,9 @@ public class DateDiffUnitTest {
org.joda.time.LocalDateTime now = org.joda.time.LocalDateTime.now();
org.joda.time.LocalDateTime sixMinutesBehind = now.minusMinutes(6);
- org.joda.time.Period period = new org.joda.time.Period(now, sixMinutesBehind);
- long diff = Math.abs(period.getDays());
+ long diff = Math.abs(Minutes.minutesBetween(now, sixMinutesBehind).getMinutes());
+ assertEquals(6, diff);
+
}
@Test
@@ -111,6 +134,6 @@ public class DateDiffUnitTest {
long diff = Math.abs(now.numDaysFrom(sixDaysBehind));
- assertEquals(diff, 6);
+ assertEquals(6, diff);
}
-}
\ No newline at end of file
+}
diff --git a/core-java-modules/core-java-exceptions-3/README.md b/core-java-modules/core-java-exceptions-3/README.md
index e6c7eda881..4e3dd22bb8 100644
--- a/core-java-modules/core-java-exceptions-3/README.md
+++ b/core-java-modules/core-java-exceptions-3/README.md
@@ -1,3 +1,5 @@
### Relevant Articles:
- [NoSuchMethodError in Java](https://www.baeldung.com/java-nosuchmethod-error)
+- [IllegalArgumentException or NullPointerException for a Null Parameter?](https://www.baeldung.com/java-illegalargumentexception-or-nullpointerexception)
+- [IllegalMonitorStateException in Java](https://www.baeldung.com/java-illegalmonitorstateexception)
diff --git a/core-java-modules/core-java-io-3/README.md b/core-java-modules/core-java-io-3/README.md
index 06a2a5a0fc..4b1a14ab3e 100644
--- a/core-java-modules/core-java-io-3/README.md
+++ b/core-java-modules/core-java-io-3/README.md
@@ -10,4 +10,5 @@ This module contains articles about core Java input and output (IO)
- [Copy a Directory in Java](https://www.baeldung.com/java-copy-directory)
- [Java Files Open Options](https://www.baeldung.com/java-file-options)
- [Creating Temporary Directories in Java](https://www.baeldung.com/java-temp-directories)
+- [Reading a Line at a Given Line Number From a File in Java](https://www.baeldung.com/java-read-line-at-number)
- [[<-- Prev]](/core-java-modules/core-java-io-2)
diff --git a/core-java-modules/core-java-lang-3/README.md b/core-java-modules/core-java-lang-3/README.md
index 0fa08ef397..121d30f20f 100644
--- a/core-java-modules/core-java-lang-3/README.md
+++ b/core-java-modules/core-java-lang-3/README.md
@@ -5,4 +5,5 @@ This module contains articles about core features in the Java language
- [Class.isInstance vs Class.isAssignableFrom](https://www.baeldung.com/java-isinstance-isassignablefrom)
- [Converting a Java String Into a Boolean](https://www.baeldung.com/java-string-to-boolean)
- [When are Static Variables Initialized in Java?](https://www.baeldung.com/java-static-variables-initialization)
+- [Checking if a Class Exists in Java](https://www.baeldung.com/java-check-class-exists)
- [[<-- Prev]](/core-java-modules/core-java-lang-2)
diff --git a/core-java-modules/core-java-lang-math-2/README.md b/core-java-modules/core-java-lang-math-2/README.md
index 09039f6ed0..69ee00b5a5 100644
--- a/core-java-modules/core-java-lang-math-2/README.md
+++ b/core-java-modules/core-java-lang-math-2/README.md
@@ -13,4 +13,5 @@
- [Convert Latitude and Longitude to a 2D Point in Java](https://www.baeldung.com/java-convert-latitude-longitude)
- [Debugging with Eclipse](https://www.baeldung.com/eclipse-debugging)
- [Matrix Multiplication in Java](https://www.baeldung.com/java-matrix-multiplication)
+- [Largest Power of 2 That Is Less Than the Given Number](https://www.baeldung.com/java-largest-power-of-2-less-than-number)
- More articles: [[<-- Prev]](/core-java-modules/core-java-lang-math)
diff --git a/core-java-modules/core-java-reflection/README.MD b/core-java-modules/core-java-reflection/README.md
similarity index 100%
rename from core-java-modules/core-java-reflection/README.MD
rename to core-java-modules/core-java-reflection/README.md
diff --git a/core-java-modules/pre-jpms/README.MD b/core-java-modules/pre-jpms/README.md
similarity index 100%
rename from core-java-modules/pre-jpms/README.MD
rename to core-java-modules/pre-jpms/README.md
diff --git a/core-kotlin-modules/core-kotlin-collections-2/README.md b/core-kotlin-modules/core-kotlin-collections-2/README.md
index 2dc180b5b3..64062ee704 100644
--- a/core-kotlin-modules/core-kotlin-collections-2/README.md
+++ b/core-kotlin-modules/core-kotlin-collections-2/README.md
@@ -2,6 +2,6 @@
This module contains articles about core Kotlin collections.
-### Relevant articles:
-
+## Relevant articles:
+- [Aggregate Operations in Kotlin](https://www.baeldung.com/kotlin/aggregate-operations)
diff --git a/drools/README.MD b/drools/README.md
similarity index 100%
rename from drools/README.MD
rename to drools/README.md
diff --git a/gradle-5/cmd-line-args/README.md b/gradle-5/cmd-line-args/README.md
new file mode 100644
index 0000000000..de797c8588
--- /dev/null
+++ b/gradle-5/cmd-line-args/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Passing Command Line Arguments in Gradle](https://www.baeldung.com/gradle-command-line-arguments)
diff --git a/gradle-5/source-sets/README.md b/gradle-5/source-sets/README.md
new file mode 100644
index 0000000000..19fe1e1fae
--- /dev/null
+++ b/gradle-5/source-sets/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Gradle Source Sets](https://www.baeldung.com/gradle-source-sets)
diff --git a/libraries-primitive/README.MD b/libraries-primitive/README.md
similarity index 100%
rename from libraries-primitive/README.MD
rename to libraries-primitive/README.md
diff --git a/libraries-security/README.md b/libraries-security/README.md
index 5ec85a15e9..1a0e16a776 100644
--- a/libraries-security/README.md
+++ b/libraries-security/README.md
@@ -11,3 +11,4 @@ This module contains articles about security libraries.
- [Intro to Jasypt](https://www.baeldung.com/jasypt)
- [Digital Signatures in Java](https://www.baeldung.com/java-digital-signature)
- [How to Read PEM File to Get Public and Private Keys](https://www.baeldung.com/java-read-pem-file-keys)
+- [SSH Connection With Java](https://www.baeldung.com/java-ssh-connection)
diff --git a/libraries-testing/README.md b/libraries-testing/README.md
index 43d7673e2d..447f3f32b9 100644
--- a/libraries-testing/README.md
+++ b/libraries-testing/README.md
@@ -12,3 +12,4 @@ This module contains articles about test libraries.
- [Introduction to Hoverfly in Java](https://www.baeldung.com/hoverfly)
- [Testing with Hamcrest](https://www.baeldung.com/java-junit-hamcrest-guide)
- [Introduction To DBUnit](https://www.baeldung.com/java-dbunit)
+- [Introduction to ArchUnit](https://www.baeldung.com/java-archunit-intro)
diff --git a/patterns/design-patterns-architectural/README.md b/patterns/design-patterns-architectural/README.md
index fbe4221752..5b6011c159 100644
--- a/patterns/design-patterns-architectural/README.md
+++ b/patterns/design-patterns-architectural/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [Service Locator Pattern](https://www.baeldung.com/java-service-locator-pattern)
- [The DAO Pattern in Java](https://www.baeldung.com/java-dao-pattern)
+- [DAO vs Repository Patterns](https://www.baeldung.com/java-dao-vs-repository)
diff --git a/persistence-modules/flyway-repair/README.MD b/persistence-modules/flyway-repair/README.md
similarity index 100%
rename from persistence-modules/flyway-repair/README.MD
rename to persistence-modules/flyway-repair/README.md
diff --git a/persistence-modules/flyway/README.MD b/persistence-modules/flyway/README.md
similarity index 69%
rename from persistence-modules/flyway/README.MD
rename to persistence-modules/flyway/README.md
index daeb9012b5..bd5f9bbe03 100644
--- a/persistence-modules/flyway/README.MD
+++ b/persistence-modules/flyway/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [Database Migrations with Flyway](http://www.baeldung.com/database-migrations-with-flyway)
- [A Guide to Flyway Callbacks](http://www.baeldung.com/flyway-callbacks)
+- [Rolling Back Migrations with Flyway](https://www.baeldung.com/flyway-roll-back)
diff --git a/persistence-modules/spring-boot-persistence/README.MD b/persistence-modules/spring-boot-persistence/README.md
similarity index 100%
rename from persistence-modules/spring-boot-persistence/README.MD
rename to persistence-modules/spring-boot-persistence/README.md
diff --git a/persistence-modules/spring-data-dynamodb/README.MD b/persistence-modules/spring-data-dynamodb/README.md
similarity index 100%
rename from persistence-modules/spring-data-dynamodb/README.MD
rename to persistence-modules/spring-data-dynamodb/README.md
diff --git a/pom.xml b/pom.xml
index 030e56107c..8e3fc2aaa4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -383,6 +383,7 @@
core-groovy
core-groovy-2
core-groovy-collections
+ core-groovy-strings
core-java-modules
core-kotlin-modules
@@ -894,6 +895,7 @@
core-groovy
core-groovy-2
core-groovy-collections
+ core-groovy-strings
core-java-modules
core-kotlin-modules
diff --git a/quarkus/README.md b/quarkus/README.md
index 3ff08c6f9e..94b71dd954 100644
--- a/quarkus/README.md
+++ b/quarkus/README.md
@@ -1,3 +1,4 @@
## Relevant Articles:
- [Guide to QuarkusIO](https://www.baeldung.com/quarkus-io)
+- [Testing Quarkus Applications](https://www.baeldung.com/java-quarkus-testing)
diff --git a/quarkus/pom.xml b/quarkus/pom.xml
index 67356abdef..7fdf1557fb 100644
--- a/quarkus/pom.xml
+++ b/quarkus/pom.xml
@@ -99,6 +99,8 @@
maven-surefire-plugin
${surefire-plugin.version}
+ 1
+ true
org.jboss.logmanager.LogManager
diff --git a/raml/README.MD b/raml/README.md
similarity index 100%
rename from raml/README.MD
rename to raml/README.md
diff --git a/spring-5-security-oauth/README.md b/spring-5-security-oauth/README.md
index 43cab33598..35e64da639 100644
--- a/spring-5-security-oauth/README.md
+++ b/spring-5-security-oauth/README.md
@@ -7,3 +7,4 @@ This module contains articles about Spring 5 OAuth Security
- [Spring Security 5 – OAuth2 Login](https://www.baeldung.com/spring-security-5-oauth2-login)
- [Extracting Principal and Authorities using Spring Security OAuth](https://www.baeldung.com/spring-security-oauth-principal-authorities-extractor)
- [Customizing Authorization and Token Requests with Spring Security 5.1 Client](https://www.baeldung.com/spring-security-custom-oauth-requests)
+- [Social Login with Spring Security in a Jersey Application](https://www.baeldung.com/spring-security-social-login-jersey)
diff --git a/spring-5-security/src/main/java/com/baeldung/inmemory/InMemoryNoOpAuthWebSecurityConfigurer.java b/spring-5-security/src/main/java/com/baeldung/inmemory/InMemoryNoOpAuthWebSecurityConfigurer.java
new file mode 100644
index 0000000000..4b6494f666
--- /dev/null
+++ b/spring-5-security/src/main/java/com/baeldung/inmemory/InMemoryNoOpAuthWebSecurityConfigurer.java
@@ -0,0 +1,29 @@
+package com.baeldung.inmemory;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+//@Configuration
+public class InMemoryNoOpAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication()
+ .withUser("spring")
+ .password("{noop}secret")
+ .roles("USER");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .antMatchers("/private/**")
+ .authenticated()
+ .antMatchers("/public/**")
+ .permitAll()
+ .and()
+ .httpBasic();
+ }
+}
diff --git a/spring-5-webflux/README.md b/spring-5-webflux/README.md
index 55ab0b8af4..9f9a12f997 100644
--- a/spring-5-webflux/README.md
+++ b/spring-5-webflux/README.md
@@ -8,3 +8,4 @@ This module contains articles about Spring 5 WebFlux
- [How to Return 404 with Spring WebFlux](https://www.baeldung.com/spring-webflux-404)
- [Spring WebClient Requests with Parameters](https://www.baeldung.com/webflux-webclient-parameters)
- [RSocket Using Spring Boot](https://www.baeldung.com/spring-boot-rsocket)
+- [Spring MVC Async vs Spring WebFlux](https://www.baeldung.com/spring-mvc-async-vs-webflux)
diff --git a/spring-boot-modules/spring-boot-actuator/README.md b/spring-boot-modules/spring-boot-actuator/README.md
index 6f31ee4a5e..3e8ef3411b 100644
--- a/spring-boot-modules/spring-boot-actuator/README.md
+++ b/spring-boot-modules/spring-boot-actuator/README.md
@@ -9,3 +9,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Liveness and Readiness Probes in Spring Boot](https://www.baeldung.com/spring-liveness-readiness-probes)
- [Custom Information in Spring Boot Info Endpoint](https://www.baeldung.com/spring-boot-info-actuator-custom)
+- [Health Indicators in Spring Boot](https://www.baeldung.com/spring-boot-health-indicators)
diff --git a/spring-boot-modules/spring-boot-autoconfiguration/README.md b/spring-boot-modules/spring-boot-autoconfiguration/README.md
index d1b5fde7ed..881c88467b 100644
--- a/spring-boot-modules/spring-boot-autoconfiguration/README.md
+++ b/spring-boot-modules/spring-boot-autoconfiguration/README.md
@@ -10,4 +10,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Create a Custom Auto-Configuration with Spring Boot](https://www.baeldung.com/spring-boot-custom-auto-configuration)
- [Guide to ApplicationContextRunner in Spring Boot](https://www.baeldung.com/spring-boot-context-runner)
- [A Guide to Spring Boot Configuration Metadata](https://www.baeldung.com/spring-boot-configuration-metadata)
-- [Display Auto-Configuration Report in Spring Boot](https://www.baeldung.com/spring-boot-auto-configuration-report)
\ No newline at end of file
+- [Display Auto-Configuration Report in Spring Boot](https://www.baeldung.com/spring-boot-auto-configuration-report)
+- [The Spring @ConditionalOnProperty Annotation](https://www.baeldung.com/spring-conditionalonproperty)
diff --git a/spring-boot-modules/spring-boot-client/README.MD b/spring-boot-modules/spring-boot-client/README.md
similarity index 100%
rename from spring-boot-modules/spring-boot-client/README.MD
rename to spring-boot-modules/spring-boot-client/README.md
diff --git a/spring-boot-modules/spring-boot-exceptions/README.MD b/spring-boot-modules/spring-boot-exceptions/README.md
similarity index 100%
rename from spring-boot-modules/spring-boot-exceptions/README.MD
rename to spring-boot-modules/spring-boot-exceptions/README.md
diff --git a/spring-boot-modules/spring-boot-exceptions/pom.xml b/spring-boot-modules/spring-boot-exceptions/pom.xml
index 4b9d787b61..1bfe8e6751 100644
--- a/spring-boot-modules/spring-boot-exceptions/pom.xml
+++ b/spring-boot-modules/spring-boot-exceptions/pom.xml
@@ -14,15 +14,7 @@
jar
spring-boot-exceptions
- Demo project for working with Spring Boot exceptions
-
-
-
- org.springframework.boot
- spring-boot-starter
- ${spring-boot.version}
-
-
+ Demo project for working with Spring Boot exceptions
spring-boot-exceptions
@@ -32,68 +24,6 @@
true
-
-
-
-
- org.apache.maven.plugins
- maven-war-plugin
-
-
-
- org.apache.maven.plugins
- maven-resources-plugin
-
-
- @
-
- false
-
-
-
-
-
-
- autoconfiguration
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- integration-test
-
- test
-
-
-
- **/*LiveTest.java
- **/*IntegrationTest.java
- **/*IntTest.java
-
-
- **/AutoconfigurationTest.java
-
-
-
-
-
-
- json
-
-
-
-
-
-
-
-
-
-
- com.baeldung.intro.App
- 2.2.3.RELEASE
-
diff --git a/spring-boot-modules/spring-boot-keycloak/README.md b/spring-boot-modules/spring-boot-keycloak/README.md
index 2dfe3fc331..74fbbb6f09 100644
--- a/spring-boot-modules/spring-boot-keycloak/README.md
+++ b/spring-boot-modules/spring-boot-keycloak/README.md
@@ -4,3 +4,4 @@ This module contains articles about Keycloak in Spring Boot projects.
## Relevant articles:
- [A Quick Guide to Using Keycloak with Spring Boot](https://www.baeldung.com/spring-boot-keycloak)
+- [Custom User Attributes with Keycloak](https://www.baeldung.com/keycloak-custom-user-attributes)
diff --git a/spring-boot-modules/spring-boot-mvc-3/README.md b/spring-boot-modules/spring-boot-mvc-3/README.md
index 0562224337..6627bd8eea 100644
--- a/spring-boot-modules/spring-boot-mvc-3/README.md
+++ b/spring-boot-modules/spring-boot-mvc-3/README.md
@@ -6,4 +6,5 @@ This module contains articles about Spring Web MVC in Spring Boot projects.
- [Circular View Path Error](https://www.baeldung.com/spring-circular-view-path-error)
- [Download an Image or a File with Spring MVC](https://www.baeldung.com/spring-controller-return-image-file)
+- [Spring MVC Async vs Spring WebFlux](https://www.baeldung.com/spring-mvc-async-vs-webflux)
- More articles: [[prev -->]](/spring-boot-modules/spring-boot-mvc-2)
diff --git a/spring-boot-modules/spring-boot-mvc-3/pom.xml b/spring-boot-modules/spring-boot-mvc-3/pom.xml
index 31c43461d2..1290b0432f 100644
--- a/spring-boot-modules/spring-boot-mvc-3/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-3/pom.xml
@@ -22,6 +22,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.springframework.boot
spring-boot-starter-thymeleaf
@@ -37,4 +41,4 @@
2.7
-
\ No newline at end of file
+
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/ServletInitializer.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/ServletInitializer.java
new file mode 100644
index 0000000000..f365582999
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/ServletInitializer.java
@@ -0,0 +1,12 @@
+package com.baeldung.springvalidation;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+public class ServletInitializer extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ return application.sources(SpringValidationApplication.class);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/SpringValidationApplication.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/SpringValidationApplication.java
new file mode 100644
index 0000000000..ccfe990ce7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/SpringValidationApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.springvalidation;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringValidationApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringValidationApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/controller/UserAccountController.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/controller/UserAccountController.java
new file mode 100644
index 0000000000..48de7b35d0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/controller/UserAccountController.java
@@ -0,0 +1,44 @@
+package com.baeldung.springvalidation.controller;
+
+import javax.validation.Valid;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.ui.ModelMap;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.baeldung.springvalidation.domain.UserAccount;
+import com.baeldung.springvalidation.interfaces.BasicInfo;
+
+@Controller
+public class UserAccountController {
+
+ @GetMapping("/getUserForm")
+ public String showUserForm(Model theModel) {
+ UserAccount theUser = new UserAccount();
+ theModel.addAttribute("useraccount", theUser);
+ return "userHome";
+ }
+
+ @RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST)
+ public String saveBasicInfo(@Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) {
+ if (result.hasErrors()) {
+ return "error";
+ }
+ return "success";
+ }
+
+ @RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST)
+ public String saveBasicInfoStep1(@Validated(BasicInfo.class) @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) {
+ if (result.hasErrors()) {
+ return "error";
+ }
+ return "success";
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAccount.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAccount.java
new file mode 100644
index 0000000000..fd5437fe5e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAccount.java
@@ -0,0 +1,80 @@
+package com.baeldung.springvalidation.domain;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.baeldung.springvalidation.interfaces.AdvanceInfo;
+import com.baeldung.springvalidation.interfaces.BasicInfo;
+
+public class UserAccount {
+
+ @NotNull(groups = BasicInfo.class)
+ @Size(min = 4, max = 15, groups = BasicInfo.class)
+ private String password;
+
+ @NotBlank(groups = BasicInfo.class)
+ private String name;
+
+ @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class)
+ private int age;
+
+ @NotBlank(groups = AdvanceInfo.class)
+ private String phone;
+
+ @Valid
+ @NotNull(groups = AdvanceInfo.class)
+ private UserAddress useraddress;
+
+ public UserAddress getUseraddress() {
+ return useraddress;
+ }
+
+ public void setUseraddress(UserAddress useraddress) {
+ this.useraddress = useraddress;
+ }
+
+ public UserAccount() {
+
+ }
+
+ public UserAccount(String email, String password, String name, int age) {
+ this.password = password;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAddress.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAddress.java
new file mode 100644
index 0000000000..9aa9656eed
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/domain/UserAddress.java
@@ -0,0 +1,17 @@
+package com.baeldung.springvalidation.domain;
+
+import javax.validation.constraints.NotBlank;
+public class UserAddress {
+
+ @NotBlank
+ private String countryCode;
+
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/AdvanceInfo.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/AdvanceInfo.java
new file mode 100644
index 0000000000..dd0c7c69d0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/AdvanceInfo.java
@@ -0,0 +1,6 @@
+package com.baeldung.springvalidation.interfaces;
+
+public interface AdvanceInfo {
+ // validation group marker interface
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/BasicInfo.java b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/BasicInfo.java
new file mode 100644
index 0000000000..95d2b5e6a2
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/BasicInfo.java
@@ -0,0 +1,6 @@
+package com.baeldung.springvalidation.interfaces;
+
+public interface BasicInfo {
+ // validation group marker interface
+
+}
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/application.properties
new file mode 100644
index 0000000000..89390eaace
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+spring.mvc.view.prefix=/WEB-INF/views/
+spring.mvc.view.suffix=.html
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/error.html b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/error.html
new file mode 100644
index 0000000000..341cc73603
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/error.html
@@ -0,0 +1,10 @@
+
+
+
+SpringValidation
+
+
+
+ Error!!!
+
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/success.html b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/success.html
new file mode 100644
index 0000000000..b422152153
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/main/resources/templates/success.html
@@ -0,0 +1,10 @@
+
+
+
+SpringValidation
+
+
+
+ SUCCESS!!!
+
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-3/src/test/java/com/baeldung/springvalidation/UserAccountUnitTest.java b/spring-boot-modules/spring-boot-mvc-3/src/test/java/com/baeldung/springvalidation/UserAccountUnitTest.java
new file mode 100644
index 0000000000..507321bf8e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-mvc-3/src/test/java/com/baeldung/springvalidation/UserAccountUnitTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.springvalidation;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@AutoConfigureMockMvc
+public class UserAccountUnitTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception {
+ this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo")
+ .accept(MediaType.TEXT_HTML)
+ .param("name", "test123")
+ .param("password", "pass"))
+ .andExpect(view().name("success"))
+ .andExpect(status().isOk())
+ .andDo(print());
+ }
+
+ @Test
+ public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception {
+ this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1")
+ .accept(MediaType.TEXT_HTML)
+ .param("name", "test123")
+ .param("password", "pass"))
+ .andExpect(view().name("success"))
+ .andExpect(status().isOk())
+ .andDo(print());
+ }
+
+ @Test
+ public void givenSaveBasicInfoStep1_whenIncorrectInput_thenError() throws Exception {
+ this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1")
+ .accept(MediaType.TEXT_HTML))
+ // .param("name", "test123")
+ // .param("password", "pass"))
+ .andExpect(model().errorCount(2))
+ // .andExpect(view().name("error"))
+ .andExpect(status().isOk())
+ .andDo(print());
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-properties-3/README.md b/spring-boot-modules/spring-boot-properties-3/README.md
index d89f825c86..df272be7f4 100644
--- a/spring-boot-modules/spring-boot-properties-3/README.md
+++ b/spring-boot-modules/spring-boot-properties-3/README.md
@@ -6,4 +6,4 @@
### Relevant Articles:
- [How to Define a Map in YAML for a POJO?](https://www.baeldung.com/yaml-map-pojo)
-
+- [Using application.yml vs application.properties in Spring Boot](https://www.baeldung.com/spring-boot-yaml-vs-properties)
diff --git a/spring-boot-modules/spring-boot-swagger/README.md b/spring-boot-modules/spring-boot-swagger/README.md
new file mode 100644
index 0000000000..1038031210
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)
diff --git a/spring-cloud-data-flow/apache-spark-job/README.MD b/spring-cloud-data-flow/apache-spark-job/README.md
similarity index 100%
rename from spring-cloud-data-flow/apache-spark-job/README.MD
rename to spring-cloud-data-flow/apache-spark-job/README.md
diff --git a/spring-cloud-data-flow/batch-job/README.MD b/spring-cloud-data-flow/batch-job/README.md
similarity index 100%
rename from spring-cloud-data-flow/batch-job/README.MD
rename to spring-cloud-data-flow/batch-job/README.md
diff --git a/spring-cloud-data-flow/spring-cloud-data-flow-etl/README.MD b/spring-cloud-data-flow/spring-cloud-data-flow-etl/README.md
similarity index 100%
rename from spring-cloud-data-flow/spring-cloud-data-flow-etl/README.MD
rename to spring-cloud-data-flow/spring-cloud-data-flow-etl/README.md
diff --git a/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/README.MD b/spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/README.md
similarity index 100%
rename from spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/README.MD
rename to spring-cloud-data-flow/spring-cloud-data-flow-stream-processing/README.md
diff --git a/spring-cloud/spring-cloud-consul/README.md b/spring-cloud/spring-cloud-consul/README.md
index 47dc39f0d5..e587572ffa 100644
--- a/spring-cloud/spring-cloud-consul/README.md
+++ b/spring-cloud/spring-cloud-consul/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [A Quick Guide to Spring Cloud Consul](http://www.baeldung.com/spring-cloud-consul)
+- [Leadership Election With Consul](https://www.baeldung.com/consul-leadership-election)
diff --git a/spring-cloud/spring-cloud-functions/README.MD b/spring-cloud/spring-cloud-functions/README.md
similarity index 100%
rename from spring-cloud/spring-cloud-functions/README.MD
rename to spring-cloud/spring-cloud-functions/README.md
diff --git a/spring-cloud/spring-cloud-hystrix/README.MD b/spring-cloud/spring-cloud-hystrix/README.md
similarity index 100%
rename from spring-cloud/spring-cloud-hystrix/README.MD
rename to spring-cloud/spring-cloud-hystrix/README.md
diff --git a/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsConfig.java b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsConfig.java
new file mode 100644
index 0000000000..f2049554ab
--- /dev/null
+++ b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsConfig.java
@@ -0,0 +1,36 @@
+package com.baeldung.jsonparams.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.view.InternalResourceViewResolver;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Configuration
+@EnableWebMvc
+@ComponentScan(basePackages = { "com.baeldung.jsonparams" })
+public class JsonParamsConfig implements WebMvcConfigurer {
+ @Override
+ public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+ configurer.enable();
+ }
+
+ @Bean
+ public ViewResolver viewResolver() {
+ InternalResourceViewResolver bean = new InternalResourceViewResolver();
+ bean.setPrefix("/WEB-INF/");
+ bean.setSuffix(".jsp");
+ return bean;
+ }
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper();
+ }
+
+}
diff --git a/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsInit.java b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsInit.java
new file mode 100644
index 0000000000..6db2a92350
--- /dev/null
+++ b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/config/JsonParamsInit.java
@@ -0,0 +1,29 @@
+package com.baeldung.jsonparams.config;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+
+public class JsonParamsInit // implements WebApplicationInitializer
+{
+
+ //uncomment to run the product controller example
+ //@Override
+ public void onStartup(ServletContext sc) throws ServletException {
+ AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
+ root.register(JsonParamsConfig.class);
+ root.setServletContext(sc);
+ sc.addListener(new ContextLoaderListener(root));
+
+ DispatcherServlet dv = new DispatcherServlet(root);
+
+ ServletRegistration.Dynamic appServlet = sc.addServlet("jsonparams-mvc", dv);
+ appServlet.setLoadOnStartup(1);
+ appServlet.addMapping("/");
+ }
+
+}
diff --git a/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/controller/ProductController.java b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/controller/ProductController.java
new file mode 100644
index 0000000000..e4e2ce085d
--- /dev/null
+++ b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/controller/ProductController.java
@@ -0,0 +1,57 @@
+package com.baeldung.jsonparams.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.baeldung.jsonparams.model.Product;
+import com.baeldung.jsonparams.propertyeditor.ProductEditor;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Controller
+@RequestMapping("/products")
+public class ProductController {
+
+ private ObjectMapper objectMapper;
+
+ @Autowired
+ public void setObjectMapper(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ @InitBinder
+ public void initBinder(WebDataBinder binder) {
+ binder.registerCustomEditor(Product.class, new ProductEditor(objectMapper));
+ }
+
+ @PostMapping("/create")
+ @ResponseBody
+ public Product createProduct(@RequestBody Product product) {
+ // custom logic
+ return product;
+ }
+
+ @GetMapping("/get")
+ @ResponseBody
+ public Product getProduct(@RequestParam String product) throws JsonMappingException, JsonProcessingException {
+ final Product prod = objectMapper.readValue(product, Product.class);
+ return prod;
+ }
+
+ @GetMapping("/get2")
+ @ResponseBody
+ public Product get2Product(@RequestParam Product product) {
+ // custom logic
+ return product;
+ }
+
+}
diff --git a/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/model/Product.java b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/model/Product.java
new file mode 100644
index 0000000000..b0baf0aa57
--- /dev/null
+++ b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/model/Product.java
@@ -0,0 +1,37 @@
+package com.baeldung.jsonparams.model;
+
+public class Product {
+
+ private int id;
+ private String name;
+ private double price;
+
+ public Product() {
+
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String nom) {
+ this.name = nom;
+ }
+
+ public double getPrice() {
+ return price;
+ }
+
+ public void setPrice(double price) {
+ this.price = price;
+ }
+
+}
diff --git a/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/propertyeditor/ProductEditor.java b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/propertyeditor/ProductEditor.java
new file mode 100644
index 0000000000..11766118cd
--- /dev/null
+++ b/spring-mvc-basics-4/src/main/java/com/baeldung/jsonparams/propertyeditor/ProductEditor.java
@@ -0,0 +1,34 @@
+package com.baeldung.jsonparams.propertyeditor;
+
+import java.beans.PropertyEditorSupport;
+
+import org.springframework.util.StringUtils;
+
+import com.baeldung.jsonparams.model.Product;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class ProductEditor extends PropertyEditorSupport {
+
+ private ObjectMapper objectMapper;
+
+ public ProductEditor(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public void setAsText(String text) throws IllegalArgumentException {
+ if (StringUtils.isEmpty(text)) {
+ setValue(null);
+ } else {
+ Product prod = new Product();
+ try {
+ prod = objectMapper.readValue(text, Product.class);
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException(e);
+ }
+ setValue(prod);
+ }
+ }
+
+}
diff --git a/spring-mvc-basics-4/src/test/java/com/baeldung/jsonparams/JsonParamsIntegrationTest.java b/spring-mvc-basics-4/src/test/java/com/baeldung/jsonparams/JsonParamsIntegrationTest.java
new file mode 100644
index 0000000000..bceadc4896
--- /dev/null
+++ b/spring-mvc-basics-4/src/test/java/com/baeldung/jsonparams/JsonParamsIntegrationTest.java
@@ -0,0 +1,71 @@
+package com.baeldung.jsonparams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import com.baeldung.jsonparams.config.JsonParamsConfig;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration(classes = { JsonParamsConfig.class }, loader = AnnotationConfigWebContextLoader.class)
+public class JsonParamsIntegrationTest {
+
+ private MockMvc mockMvc;
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ @Before
+ public void setUp() {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
+ .build();
+ }
+
+ @Test
+ public void whenJsonIsPassedWithPost_thenResponseOK() throws Exception {
+
+ this.mockMvc.perform(post("/products/create").accept(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"id\": 1,\"name\": \"Asus Zenbook\",\"price\": 800}"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value("1"))
+ .andExpect(jsonPath("$.name").value("Asus Zenbook"));
+
+ }
+
+ @Test
+ public void whenJsonIsPassedWithGet_thenResponseOK() throws Exception {
+
+ this.mockMvc.perform(get("/products/get").contentType(MediaType.APPLICATION_JSON)
+ .queryParam("product", "{\"id\": 2,\"name\": \"HP EliteBook\",\"price\": 700}"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value("2"))
+ .andExpect(jsonPath("$.name").value("HP EliteBook"));
+
+ }
+
+ @Test
+ public void whenJsonIsPassedWithGet2_thenResponseOK() throws Exception {
+
+ this.mockMvc.perform(get("/products/get2").contentType(MediaType.APPLICATION_JSON)
+ .queryParam("product", "{\"id\": 3,\"name\": \"Dell G5 15\",\"price\": 1200}"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value("3"))
+ .andExpect(jsonPath("$.name").value("Dell G5 15"));
+
+ }
+
+}
diff --git a/spring-rest-http-2/README.md b/spring-rest-http-2/README.md
index 74ec59e0b5..97cdc2d068 100644
--- a/spring-rest-http-2/README.md
+++ b/spring-rest-http-2/README.md
@@ -7,4 +7,4 @@ The "REST With Spring 2" Classes: http://bit.ly/restwithspring
### Relevant Articles:
-
+- [How to Turn Off Swagger-ui in Production](https://www.baeldung.com/swagger-ui-turn-off-in-production)
diff --git a/spring-rest-http/README.md b/spring-rest-http/README.md
index bb4cfd829d..2271858f0a 100644
--- a/spring-rest-http/README.md
+++ b/spring-rest-http/README.md
@@ -14,3 +14,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Guide to DeferredResult in Spring](https://www.baeldung.com/spring-deferred-result)
- [Using JSON Patch in Spring REST APIs](https://www.baeldung.com/spring-rest-json-patch)
- [OpenAPI JSON Objects as Query Parameters](https://www.baeldung.com/openapi-json-query-parameters)
+- [Dates in OpenAPI Files](https://www.baeldung.com/openapi-dates)
diff --git a/spring-thymeleaf-3/README.md b/spring-thymeleaf-3/README.md
index 34bd1b4b35..8bb8861daf 100644
--- a/spring-thymeleaf-3/README.md
+++ b/spring-thymeleaf-3/README.md
@@ -8,3 +8,4 @@ This module contains articles about Spring with Thymeleaf
- [Formatting Currencies in Spring Using Thymeleaf](https://www.baeldung.com/spring-thymeleaf-currencies)
- [Working with Select and Option in Thymeleaf](https://www.baeldung.com/thymeleaf-select-option)
- [Conditional CSS Classes in Thymeleaf](https://www.baeldung.com/spring-mvc-thymeleaf-conditional-css-classes)
+- [Using Hidden Inputs with Spring and Thymeleaf](https://www.baeldung.com/spring-thymeleaf-hidden-inputs)
diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md
index 9ca5a38e2e..c7b62182b5 100644
--- a/testing-modules/mockito-2/README.md
+++ b/testing-modules/mockito-2/README.md
@@ -8,3 +8,4 @@
- [Introduction to Mockito’s AdditionalAnswers](https://www.baeldung.com/mockito-additionalanswers)
- [Mockito – Using Spies](https://www.baeldung.com/mockito-spy)
- [Using Mockito ArgumentCaptor](https://www.baeldung.com/mockito-argumentcaptor)
+- [Difference Between when() and doXxx() Methods in Mockito](https://www.baeldung.com/java-mockito-when-vs-do)
diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml
index f1d30cd7a1..0416423239 100644
--- a/testing-modules/pom.xml
+++ b/testing-modules/pom.xml
@@ -41,7 +41,8 @@
junit-5-advanced
xmlunit-2
junit-4
- testing-libraries
+ testing-libraries
+ testing-libraries-2
powermock
diff --git a/testing-modules/spring-testing-2/README.md b/testing-modules/spring-testing-2/README.md
index 729105e3fd..702a02ff27 100644
--- a/testing-modules/spring-testing-2/README.md
+++ b/testing-modules/spring-testing-2/README.md
@@ -1 +1,3 @@
## Relevant Articles:
+
+- [Guide to @DynamicPropertySource in Spring](https://www.baeldung.com/spring-dynamicpropertysource)
diff --git a/testing-modules/testing-assertions/README.md b/testing-modules/testing-assertions/README.md
index 08e2e66062..ea238af599 100644
--- a/testing-modules/testing-assertions/README.md
+++ b/testing-modules/testing-assertions/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [Asserting Log Messages With JUnit](https://www.baeldung.com/junit-asserting-logs)
+- [Assert Two Lists for Equality Ignoring Order in Java](https://www.baeldung.com/java-assert-lists-equality-ignore-order)
diff --git a/testing-modules/testing-libraries-2/pom.xml b/testing-modules/testing-libraries-2/pom.xml
new file mode 100644
index 0000000000..282583c882
--- /dev/null
+++ b/testing-modules/testing-libraries-2/pom.xml
@@ -0,0 +1,43 @@
+
+ 4.0.0
+ testing-libraries-2
+ testing-libraries-2
+
+
+ com.baeldung
+ testing-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ com.github.stefanbirkner
+ system-rules
+ ${system-rules.version}
+ test
+
+
+ com.github.stefanbirkner
+ system-lambda
+ ${system-lambda.version}
+ test
+
+
+
+
+ testing-libraries
+
+
+ src/test/resources
+ true
+
+
+
+
+
+ 1.19.0
+ 1.0.0
+
+
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ClearSystemPropertiesWithRuleUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ClearSystemPropertiesWithRuleUnitTest.java
new file mode 100644
index 0000000000..699b40bad9
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ClearSystemPropertiesWithRuleUnitTest.java
@@ -0,0 +1,19 @@
+package com.baeldung.systemrules;
+
+import static org.junit.Assert.assertNull;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.ClearSystemProperties;
+
+public class ClearSystemPropertiesWithRuleUnitTest {
+
+ @Rule
+ public final ClearSystemProperties userNameIsClearedRule = new ClearSystemProperties("user.name");
+
+ @Test
+ public void givenClearUsernameProperty_whenGetUserName_thenNull() {
+ assertNull(System.getProperty("user.name"));
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyUnitTest.java
new file mode 100644
index 0000000000..361a338508
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyUnitTest.java
@@ -0,0 +1,26 @@
+package com.baeldung.systemrules;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+class ProvidesSystemPropertyUnitTest {
+
+ @BeforeAll
+ static void setUpBeforeClass() throws Exception {
+ System.setProperty("log_dir", "/tmp/baeldung/logs");
+ }
+
+ @Test
+ void givenSetSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully() throws Exception {
+ restoreSystemProperties(() -> {
+ System.setProperty("log_dir", "test/resources");
+ assertEquals("log_dir should be provided", "test/resources", System.getProperty("log_dir"));
+ });
+
+ assertEquals("log_dir should be provided", "/tmp/baeldung/logs", System.getProperty("log_dir"));
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyWithRuleUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyWithRuleUnitTest.java
new file mode 100644
index 0000000000..3a90592a0d
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/ProvidesSystemPropertyWithRuleUnitTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.systemrules;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.ProvideSystemProperty;
+
+public class ProvidesSystemPropertyWithRuleUnitTest {
+
+ @Rule
+ public final ProvideSystemProperty providesSystemPropertyRule = new ProvideSystemProperty("log_dir", "test/resources").and("another_property", "another_value");
+
+ @Rule
+ public final ProvideSystemProperty providesSystemPropertyFromFileRule = ProvideSystemProperty.fromResource("/test.properties");
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ setLogs();
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ System.out.println(System.getProperty("log_dir"));
+ }
+
+ @Test
+ public void givenProvideSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully() {
+ assertEquals("log_dir should be provided", "test/resources", System.getProperty("log_dir"));
+ }
+
+ @Test
+ public void givenProvideSystemPropertyFromFile_whenGetName_thenNameIsProvidedSuccessfully() {
+ assertEquals("name should be provided", "baeldung", System.getProperty("name"));
+ assertEquals("version should be provided", "1.0", System.getProperty("version"));
+ }
+
+ private static void setLogs() {
+ System.setProperty("log_dir", "/tmp/baeldung/logs");
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnUnitTest.java
new file mode 100644
index 0000000000..3345ddae8a
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnUnitTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.systemrules;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemErr;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+class SystemErrPrintlnUnitTest {
+
+ @Test
+ void givenTapSystemErr_whenInvokePrintln_thenOutputIsReturnedSuccessfully() throws Exception {
+
+ String text = tapSystemErr(() -> {
+ printError("An error occurred Baeldung Readers!!");
+ });
+
+ Assert.assertEquals("An error occurred Baeldung Readers!!", text.trim());
+ }
+
+ private void printError(String output) {
+ System.err.println(output);
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnWithRuleUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnWithRuleUnitTest.java
new file mode 100644
index 0000000000..7994f91063
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemErrPrintlnWithRuleUnitTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.systemrules;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.SystemErrRule;
+
+public class SystemErrPrintlnWithRuleUnitTest {
+
+ @Rule
+ public final SystemErrRule systemErrRule = new SystemErrRule().enableLog();
+
+ @Test
+ public void givenSystemErrRule_whenInvokePrintln_thenLogSuccess() {
+ printError("An Error occurred Baeldung Readers!!");
+
+ Assert.assertEquals("An Error occurred Baeldung Readers!!", systemErrRule.getLog()
+ .trim());
+ }
+
+ private void printError(String output) {
+ System.err.println(output);
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitUnitTest.java
new file mode 100644
index 0000000000..1e2548e714
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.systemrules;
+
+import static org.junit.Assert.assertEquals;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.catchSystemExit;
+
+import org.junit.jupiter.api.Test;
+
+class SystemExitUnitTest {
+
+ @Test
+ void givenCatchSystemExit_whenAppCallsSystemExit_thenStatusIsReturnedSuccessfully() throws Exception {
+ int statusCode = catchSystemExit(() -> {
+ exit();
+ });
+ assertEquals("status code should be 1:", 1, statusCode);
+ }
+
+ private void exit() {
+ System.exit(1);
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitWithRuleUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitWithRuleUnitTest.java
new file mode 100644
index 0000000000..471aab1989
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemExitWithRuleUnitTest.java
@@ -0,0 +1,22 @@
+package com.baeldung.systemrules;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.ExpectedSystemExit;
+
+public class SystemExitWithRuleUnitTest {
+
+ @Rule
+ public final ExpectedSystemExit exitRule = ExpectedSystemExit.none();
+
+ @Test
+ public void givenSystemExitRule_whenAppCallsSystemExit_thenExitRuleWorkssAsExpected() {
+ exitRule.expectSystemExitWithStatus(1);
+ exit();
+ }
+
+ private void exit() {
+ System.exit(1);
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInUnitTest.java
new file mode 100644
index 0000000000..96badb59d4
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.systemrules;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.withTextFromSystemIn;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Scanner;
+
+import org.junit.jupiter.api.Test;
+
+class SystemInUnitTest {
+
+ @Test
+ void givenTwoNames_whenSystemInMock_thenNamesJoinedTogether() throws Exception {
+ withTextFromSystemIn("Jonathan", "Cook").execute(() -> {
+ assertEquals("Names should be concatenated", "Jonathan Cook", getFullname());
+ });
+ }
+
+ private String getFullname() {
+ try (Scanner scanner = new Scanner(System.in)) {
+ String firstName = scanner.next();
+ String surname = scanner.next();
+ return String.join(" ", firstName, surname);
+ }
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInWithRuleUnitTest.java b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInWithRuleUnitTest.java
new file mode 100644
index 0000000000..5730e2f592
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/java/com/baeldung/systemrules/SystemInWithRuleUnitTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.systemrules;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Scanner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.TextFromStandardInputStream;
+import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.emptyStandardInputStream;
+
+public class SystemInWithRuleUnitTest {
+
+ @Rule
+ public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();
+
+ @Test
+ public void givenTwoNames_whenSystemInMock_thenNamesJoinedTogether() {
+ systemInMock.provideLines("Jonathan", "Cook");
+ assertEquals("Names should be concatenated", "Jonathan Cook", getFullname());
+ }
+
+ private String getFullname() {
+ try (Scanner scanner = new Scanner(System.in)) {
+ String firstName = scanner.next();
+ String surname = scanner.next();
+ return String.join(" ", firstName, surname);
+ }
+ }
+
+}
diff --git a/testing-modules/testing-libraries-2/src/test/resources/test.properties b/testing-modules/testing-libraries-2/src/test/resources/test.properties
new file mode 100644
index 0000000000..144847cdb0
--- /dev/null
+++ b/testing-modules/testing-libraries-2/src/test/resources/test.properties
@@ -0,0 +1,2 @@
+name=baeldung
+version=1.0
\ No newline at end of file