diff --git a/.gitignore b/.gitignore
index 88c5e49808..48b52bec06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,5 +89,14 @@ testing-modules/report-*.json
libraries-2/*.db
+apache-spark/data/output
+logs/
+libraries-data-io/*.docx
+persistence-modules/spring-hibernate-5/com.*
+spring-boot-modules/spring-boot-react/frontend/build
+spring-boot-modules/spring-boot-react/frontend/node
+spring-boot-modules/spring-boot-react/frontend/yarn.lock
+spring-boot-modules/spring-boot-properties-3/*.log
+
# SDKMan
-.sdkmanrc
+.sdkmanrc
\ No newline at end of file
diff --git a/algorithms-miscellaneous-3/pom.xml b/algorithms-miscellaneous-3/pom.xml
index d5416e543d..19eca8eca7 100644
--- a/algorithms-miscellaneous-3/pom.xml
+++ b/algorithms-miscellaneous-3/pom.xml
@@ -20,19 +20,16 @@
${org.assertj.core.version}
test
-
org.apache.commons
commons-collections4
${commons-collections4.version}
-
com.google.guava
guava
${guava.version}
-
com.squareup.retrofit2
retrofit
@@ -43,13 +40,11 @@
converter-jackson
${retrofit.version}
-
org.apache.commons
commons-lang3
${commons.lang3.version}
-
pl.pragmatists
JUnitParams
diff --git a/algorithms-miscellaneous-5/pom.xml b/algorithms-miscellaneous-5/pom.xml
index 8b7cfc7b25..1ba535dfbc 100644
--- a/algorithms-miscellaneous-5/pom.xml
+++ b/algorithms-miscellaneous-5/pom.xml
@@ -39,7 +39,6 @@
junit-platform-commons
${junit.platform.version}
-
org.assertj
assertj-core
diff --git a/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java b/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java
index 82aefe282b..078914595e 100644
--- a/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java
+++ b/algorithms-searching/src/main/java/com/baeldung/algorithms/binarysearch/BinarySearch.java
@@ -12,7 +12,7 @@ public class BinarySearch {
while (low <= high) {
- int mid = (low + high) / 2;
+ int mid = low + ((high - low) / 2);
if (sortedArray[mid] < key) {
low = mid + 1;
@@ -28,7 +28,7 @@ public class BinarySearch {
public int runBinarySearchRecursively(int[] sortedArray, int key, int low, int high) {
- int middle = (low + high) / 2;
+ int middle = low + ((high - low) / 2);
if (high < low) {
return -1;
}
diff --git a/apache-cxf/cxf-aegis/pom.xml b/apache-cxf/cxf-aegis/pom.xml
index 43a6750475..996c6fc0cd 100644
--- a/apache-cxf/cxf-aegis/pom.xml
+++ b/apache-cxf/cxf-aegis/pom.xml
@@ -20,4 +20,4 @@
-
+
\ No newline at end of file
diff --git a/apache-cxf/cxf-introduction/pom.xml b/apache-cxf/cxf-introduction/pom.xml
index d56c7ecd81..12529e55c1 100644
--- a/apache-cxf/cxf-introduction/pom.xml
+++ b/apache-cxf/cxf-introduction/pom.xml
@@ -37,4 +37,4 @@
-
+
\ No newline at end of file
diff --git a/apache-cxf/cxf-jaxrs-implementation/pom.xml b/apache-cxf/cxf-jaxrs-implementation/pom.xml
index 04c94455e4..515f527a5b 100644
--- a/apache-cxf/cxf-jaxrs-implementation/pom.xml
+++ b/apache-cxf/cxf-jaxrs-implementation/pom.xml
@@ -52,4 +52,4 @@
4.5.2
-
+
\ No newline at end of file
diff --git a/apache-cxf/cxf-spring/pom.xml b/apache-cxf/cxf-spring/pom.xml
index 340b3d486a..772ece81da 100644
--- a/apache-cxf/cxf-spring/pom.xml
+++ b/apache-cxf/cxf-spring/pom.xml
@@ -107,4 +107,4 @@
1.6.1
-
+
\ No newline at end of file
diff --git a/apache-cxf/pom.xml b/apache-cxf/pom.xml
index 3f08c11db0..6b7e396b9e 100644
--- a/apache-cxf/pom.xml
+++ b/apache-cxf/pom.xml
@@ -38,4 +38,4 @@
3.1.8
-
+
\ No newline at end of file
diff --git a/apache-cxf/sse-jaxrs/pom.xml b/apache-cxf/sse-jaxrs/pom.xml
index 737fc43dc2..1ac2948439 100644
--- a/apache-cxf/sse-jaxrs/pom.xml
+++ b/apache-cxf/sse-jaxrs/pom.xml
@@ -18,4 +18,4 @@
sse-jaxrs-client
-
+
\ No newline at end of file
diff --git a/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml b/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml
index 187f03a431..0467fd9e5d 100644
--- a/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml
+++ b/apache-cxf/sse-jaxrs/sse-jaxrs-server/pom.xml
@@ -14,7 +14,6 @@
-
javax.ws.rs
javax.ws.rs-api
@@ -33,7 +32,6 @@
${bind-api.version}
provided
-
@@ -85,4 +83,4 @@
1.0
-
+
\ No newline at end of file
diff --git a/apache-libraries/pom.xml b/apache-libraries/pom.xml
index 856b01daa0..ded10b939f 100644
--- a/apache-libraries/pom.xml
+++ b/apache-libraries/pom.xml
@@ -183,7 +183,6 @@
-
org.apache.meecrowave
@@ -216,4 +215,4 @@
6.4.0
-
+
\ No newline at end of file
diff --git a/apache-olingo/olingo2/src/main/resources/application.yml b/apache-olingo/olingo2/src/main/resources/application.yml
index 71df0c4166..3e09ea8dd5 100644
--- a/apache-olingo/olingo2/src/main/resources/application.yml
+++ b/apache-olingo/olingo2/src/main/resources/application.yml
@@ -6,7 +6,12 @@ spring:
application-path: /odata
jpa:
+ defer-datasource-initialization: true
show-sql: true
open-in-view: false
hibernate:
- ddl-auto: update
\ No newline at end of file
+ ddl-auto: update
+
+ sql:
+ init:
+ mode: always
\ No newline at end of file
diff --git a/apache-rocketmq/pom.xml b/apache-rocketmq/pom.xml
index a5f552d6f1..48399b6d51 100644
--- a/apache-rocketmq/pom.xml
+++ b/apache-rocketmq/pom.xml
@@ -25,4 +25,5 @@
1.6.0
2.0.4
+
\ No newline at end of file
diff --git a/apache-spark/pom.xml b/apache-spark/pom.xml
index c942795532..05c5088662 100644
--- a/apache-spark/pom.xml
+++ b/apache-spark/pom.xml
@@ -94,20 +94,20 @@
SparkPackagesRepo
- http://dl.bintray.com/spark-packages/maven
+ https://repos.spark-packages.org
- 2.3.0
- 2.3.0
- 2.3.0
- 2.3.0
- 2.3.0
- 0.7.0-spark2.4-s_2.11
- 2.3.0
- 2.3.0
- 1.5.2
+ 2.4.8
+ 2.4.8
+ 2.4.8
+ 2.4.8
+ 2.4.8
+ 0.8.1-spark3.0-s_2.12
+ 2.4.8
+ 2.5.2
+ 1.6.0-M1
\ No newline at end of file
diff --git a/apache-tapestry/pom.xml b/apache-tapestry/pom.xml
index c7cdc0bf3d..13ae49b0ba 100644
--- a/apache-tapestry/pom.xml
+++ b/apache-tapestry/pom.xml
@@ -77,7 +77,6 @@
true
-
org.apache.maven.plugins
maven-surefire-plugin
@@ -88,7 +87,6 @@
-
org.mortbay.jetty
@@ -119,7 +117,6 @@
jboss
http://repository.jboss.org/nexus/content/groups/public/
-
diff --git a/asciidoctor/pom.xml b/asciidoctor/pom.xml
index ec998a67b8..e24917f2f4 100644
--- a/asciidoctor/pom.xml
+++ b/asciidoctor/pom.xml
@@ -68,4 +68,4 @@
1.5.0-alpha.15
-
+
\ No newline at end of file
diff --git a/atomix/pom.xml b/atomix/pom.xml
index 2b9fc9be37..77e554852c 100644
--- a/atomix/pom.xml
+++ b/atomix/pom.xml
@@ -31,4 +31,4 @@
1.0.0-rc9
-
+
\ No newline at end of file
diff --git a/aws-lambda/pom.xml b/aws-lambda/pom.xml
index 8014a87126..3264356977 100644
--- a/aws-lambda/pom.xml
+++ b/aws-lambda/pom.xml
@@ -20,4 +20,4 @@
todo-reminder/ToDoFunction
-
+
\ No newline at end of file
diff --git a/aws-lambda/todo-reminder/ToDoFunction/pom.xml b/aws-lambda/todo-reminder/ToDoFunction/pom.xml
index f80cbdf22f..832cee841d 100644
--- a/aws-lambda/todo-reminder/ToDoFunction/pom.xml
+++ b/aws-lambda/todo-reminder/ToDoFunction/pom.xml
@@ -1,15 +1,12 @@
-
4.0.0
helloworld
- HelloWorld
+ ToDoFunction
1.0
jar
- To Do Application Example.
-
- 1.8
- 1.8
-
+ ToDoFunction
@@ -18,9 +15,9 @@
1.2.1
- com.amazonaws
- aws-lambda-java-events
- 3.6.0
+ com.amazonaws
+ aws-lambda-java-events
+ 3.6.0
uk.org.webcompere
@@ -58,10 +55,10 @@
5.0.1
- junit
- junit
- 4.13.1
- test
+ junit
+ junit
+ 4.13.1
+ test
uk.org.webcompere
@@ -84,22 +81,28 @@
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.2.4
-
-
-
-
- package
-
- shade
-
-
-
-
-
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
-
+
+
+ 1.8
+ 1.8
+
+
+
\ No newline at end of file
diff --git a/aws-reactive/pom.xml b/aws-reactive/pom.xml
index 9f0144b15c..ea1e0c44a4 100644
--- a/aws-reactive/pom.xml
+++ b/aws-reactive/pom.xml
@@ -93,4 +93,5 @@
2.2.1.RELEASE
2.10.27
+
\ No newline at end of file
diff --git a/blade/pom.xml b/blade/pom.xml
index b763f69c85..6ab3a594f2 100644
--- a/blade/pom.xml
+++ b/blade/pom.xml
@@ -83,39 +83,6 @@
-
-
- com.bazaarvoice.maven.plugins
- process-exec-maven-plugin
- ${process-exec-maven-plugin.version}
-
-
-
- blade-process
- pre-integration-test
-
- start
-
-
- Blade
- false
-
- java
- -jar
- blade.jar
-
-
-
-
-
- stop-all
- post-integration-test
-
- stop-all
-
-
-
-
maven-assembly-plugin
${assembly.plugin.version}
diff --git a/bootique/pom.xml b/bootique/pom.xml
index 5710259632..34fc75a5c0 100644
--- a/bootique/pom.xml
+++ b/bootique/pom.xml
@@ -48,13 +48,11 @@
-
org.apache.maven.plugins
maven-shade-plugin
${maven-shade-plugin.version}
-
diff --git a/cloud-foundry-uaa/pom.xml b/cloud-foundry-uaa/pom.xml
index a8a46b921d..03a5b978d4 100644
--- a/cloud-foundry-uaa/pom.xml
+++ b/cloud-foundry-uaa/pom.xml
@@ -1,6 +1,5 @@
-
4.0.0
@@ -20,4 +19,4 @@
cf-uaa-oauth2-resource-server
-
+
\ No newline at end of file
diff --git a/core-groovy-2/pom.xml b/core-groovy-2/pom.xml
index 37f8629ae8..f8ef654293 100644
--- a/core-groovy-2/pom.xml
+++ b/core-groovy-2/pom.xml
@@ -161,21 +161,6 @@
-
-
- bintray
- Groovy Bintray
- https://dl.bintray.com/groovy/maven
-
-
- never
-
-
- false
-
-
-
-
1.0.0
2.4.0
diff --git a/core-java-modules/core-java-11-2/pom.xml b/core-java-modules/core-java-11-2/pom.xml
index 5f76c0bf3b..e26e31da44 100644
--- a/core-java-modules/core-java-11-2/pom.xml
+++ b/core-java-modules/core-java-11-2/pom.xml
@@ -55,6 +55,23 @@
commons-lang3
${commons-lang3.version}
+
+ jakarta.xml.ws
+ jakarta.xml.ws-api
+ ${jakarta.ws-api.version}
+
+
+ com.sun.xml.ws
+ jaxws-rt
+ ${jaxws-rt.version}
+ runtime
+
+
+ com.sun.xml.ws
+ jaxws-ri
+ ${jaxws-ri.version}
+ pom
+
@@ -68,6 +85,20 @@
${maven.compiler.target.version}
+
+
+ com.sun.xml.ws
+ jaxws-maven-plugin
+ ${jaxws-maven-plugin.version}
+
+
+ http://localhost:8888/ws/country?wsdl
+
+ true
+ com.baeldung.soap.ws.client.generated
+ src/main/java
+
+
@@ -79,6 +110,10 @@
3.17.2
5.11.1
3.12.0
+ 3.0.0
+ 3.0.0
+ 2.3.1
+ 2.3.2
\ No newline at end of file
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java
new file mode 100644
index 0000000000..950d588661
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Country.java
@@ -0,0 +1,135 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
Java class for country complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType name="country">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="capital" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * <element name="currency" type="{http://server.ws.soap.baeldung.com/}currency" minOccurs="0"/>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * <element name="population" type="{http://www.w3.org/2001/XMLSchema}int"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "country", propOrder = {
+ "capital",
+ "currency",
+ "name",
+ "population"
+})
+public class Country {
+
+ protected String capital;
+ @XmlSchemaType(name = "string")
+ protected Currency currency;
+ protected String name;
+ protected int population;
+
+ /**
+ * Gets the value of the capital property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCapital() {
+ return capital;
+ }
+
+ /**
+ * Sets the value of the capital property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCapital(String value) {
+ this.capital = value;
+ }
+
+ /**
+ * Gets the value of the currency property.
+ *
+ * @return
+ * possible object is
+ * {@link Currency }
+ *
+ */
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ /**
+ * Sets the value of the currency property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Currency }
+ *
+ */
+ public void setCurrency(Currency value) {
+ this.currency = value;
+ }
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the population property.
+ *
+ */
+ public int getPopulation() {
+ return population;
+ }
+
+ /**
+ * Sets the value of the population property.
+ *
+ */
+ public void setPopulation(int value) {
+ this.population = value;
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java
new file mode 100644
index 0000000000..807d152cf1
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryService.java
@@ -0,0 +1,40 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.jws.WebMethod;
+import javax.jws.WebParam;
+import javax.jws.WebResult;
+import javax.jws.WebService;
+import javax.jws.soap.SOAPBinding;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.ws.Action;
+
+
+/**
+ * This class was generated by the JAX-WS RI.
+ * JAX-WS RI 2.3.2
+ * Generated source version: 2.2
+ *
+ */
+@WebService(name = "CountryService", targetNamespace = "http://server.ws.soap.baeldung.com/")
+@SOAPBinding(style = SOAPBinding.Style.RPC)
+@XmlSeeAlso({
+ ObjectFactory.class
+})
+public interface CountryService {
+
+
+ /**
+ *
+ * @param arg0
+ * @return
+ * returns com.baeldung.soap.ws.client.generated.Country
+ */
+ @WebMethod
+ @WebResult(partName = "return")
+ @Action(input = "http://server.ws.soap.baeldung.com/CountryService/findByNameRequest", output = "http://server.ws.soap.baeldung.com/CountryService/findByNameResponse")
+ public Country findByName(
+ @WebParam(name = "arg0", partName = "arg0")
+ String arg0);
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java
new file mode 100644
index 0000000000..97d6c82145
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/CountryServiceImplService.java
@@ -0,0 +1,94 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.xml.namespace.QName;
+import javax.xml.ws.Service;
+import javax.xml.ws.WebEndpoint;
+import javax.xml.ws.WebServiceClient;
+import javax.xml.ws.WebServiceException;
+import javax.xml.ws.WebServiceFeature;
+
+
+/**
+ * This class was generated by the JAX-WS RI.
+ * JAX-WS RI 2.3.2
+ * Generated source version: 2.2
+ *
+ */
+@WebServiceClient(name = "CountryServiceImplService", targetNamespace = "http://server.ws.soap.baeldung.com/", wsdlLocation = "http://localhost:8888/ws/country?wsdl")
+public class CountryServiceImplService
+ extends Service
+{
+
+ private final static URL COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
+ private final static WebServiceException COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
+ private final static QName COUNTRYSERVICEIMPLSERVICE_QNAME = new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplService");
+
+ static {
+ URL url = null;
+ WebServiceException e = null;
+ try {
+ url = new URL("http://localhost:8888/ws/country?wsdl");
+ } catch (MalformedURLException ex) {
+ e = new WebServiceException(ex);
+ }
+ COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION = url;
+ COUNTRYSERVICEIMPLSERVICE_EXCEPTION = e;
+ }
+
+ public CountryServiceImplService() {
+ super(__getWsdlLocation(), COUNTRYSERVICEIMPLSERVICE_QNAME);
+ }
+
+ public CountryServiceImplService(WebServiceFeature... features) {
+ super(__getWsdlLocation(), COUNTRYSERVICEIMPLSERVICE_QNAME, features);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation) {
+ super(wsdlLocation, COUNTRYSERVICEIMPLSERVICE_QNAME);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, WebServiceFeature... features) {
+ super(wsdlLocation, COUNTRYSERVICEIMPLSERVICE_QNAME, features);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, QName serviceName) {
+ super(wsdlLocation, serviceName);
+ }
+
+ public CountryServiceImplService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
+ super(wsdlLocation, serviceName, features);
+ }
+
+ /**
+ *
+ * @return
+ * returns CountryService
+ */
+ @WebEndpoint(name = "CountryServiceImplPort")
+ public CountryService getCountryServiceImplPort() {
+ return super.getPort(new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplPort"), CountryService.class);
+ }
+
+ /**
+ *
+ * @param features
+ * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the features parameter will have their default values.
+ * @return
+ * returns CountryService
+ */
+ @WebEndpoint(name = "CountryServiceImplPort")
+ public CountryService getCountryServiceImplPort(WebServiceFeature... features) {
+ return super.getPort(new QName("http://server.ws.soap.baeldung.com/", "CountryServiceImplPort"), CountryService.class, features);
+ }
+
+ private static URL __getWsdlLocation() {
+ if (COUNTRYSERVICEIMPLSERVICE_EXCEPTION!= null) {
+ throw COUNTRYSERVICEIMPLSERVICE_EXCEPTION;
+ }
+ return COUNTRYSERVICEIMPLSERVICE_WSDL_LOCATION;
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java
new file mode 100644
index 0000000000..c010f5533c
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/Currency.java
@@ -0,0 +1,40 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for currency.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <simpleType name="currency">
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ * <enumeration value="EUR"/>
+ * <enumeration value="INR"/>
+ * <enumeration value="USD"/>
+ * </restriction>
+ * </simpleType>
+ *
+ *
+ */
+@XmlType(name = "currency")
+@XmlEnum
+public enum Currency {
+
+ EUR,
+ INR,
+ USD;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java
new file mode 100644
index 0000000000..9ed85fe2b9
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/ObjectFactory.java
@@ -0,0 +1,40 @@
+
+package com.baeldung.soap.ws.client.generated;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.baeldung.soap.ws.client.generated package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.baeldung.soap.ws.client.generated
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link Country }
+ *
+ */
+ public Country createCountry() {
+ return new Country();
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java
new file mode 100644
index 0000000000..dfc556859f
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/client/generated/package-info.java
@@ -0,0 +1,2 @@
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://server.ws.soap.baeldung.com/")
+package com.baeldung.soap.ws.client.generated;
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Country.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Country.java
new file mode 100644
index 0000000000..62ea4a22ed
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Country.java
@@ -0,0 +1,41 @@
+package com.baeldung.soap.ws.server;
+
+public class Country {
+ protected String name;
+ protected int population;
+ protected String capital;
+ protected Currency currency;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getPopulation() {
+ return population;
+ }
+
+ public void setPopulation(int population) {
+ this.population = population;
+ }
+
+ public String getCapital() {
+ return capital;
+ }
+
+ public void setCapital(String capital) {
+ this.capital = capital;
+ }
+
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(Currency currency) {
+ this.currency = currency;
+ }
+
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java
new file mode 100644
index 0000000000..558f7c1293
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryRepository.java
@@ -0,0 +1,43 @@
+package com.baeldung.soap.ws.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CountryRepository {
+
+ private static final Map countries = new HashMap<>();
+
+ {
+ initData();
+ }
+
+ private final static void initData() {
+ Country usa = new Country();
+ usa.setName("USA");
+ usa.setCapital("Washington D.C.");
+ usa.setCurrency(Currency.USD);
+ usa.setPopulation(323947000);
+
+ countries.put(usa.getName(), usa);
+
+ Country india = new Country();
+ india.setName("India");
+ india.setCapital("New Delhi");
+ india.setCurrency(Currency.INR);
+ india.setPopulation(1295210000);
+
+ countries.put(india.getName(), india);
+
+ Country france = new Country();
+ france.setName("France");
+ france.setCapital("Paris");
+ france.setCurrency(Currency.EUR);
+ france.setPopulation(66710000);
+
+ countries.put(france.getName(), france);
+ }
+
+ public Country findCountry(String name) {
+ return countries.get(name);
+ }
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java
new file mode 100644
index 0000000000..b11765b326
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryService.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+import jakarta.jws.WebMethod;
+import jakarta.jws.WebService;
+import jakarta.jws.soap.SOAPBinding;
+import jakarta.jws.soap.SOAPBinding.Style;
+
+@WebService
+@SOAPBinding(style=Style.RPC)
+public interface CountryService {
+
+ @WebMethod
+ Country findByName(String name);
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java
new file mode 100644
index 0000000000..da159a1475
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServiceImpl.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+import jakarta.jws.WebService;
+
+@WebService(endpointInterface = "com.baeldung.soap.ws.server.CountryService")
+public class CountryServiceImpl implements CountryService {
+
+ private CountryRepository countryRepository = new CountryRepository();
+
+ @Override
+ public Country findByName(String name) {
+ return countryRepository.findCountry(name);
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java
new file mode 100644
index 0000000000..314a91dc48
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/CountryServicePublisher.java
@@ -0,0 +1,19 @@
+package com.baeldung.soap.ws.server;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.xml.ws.Endpoint;
+
+
+public class CountryServicePublisher {
+
+ private static final Logger logger = LoggerFactory.getLogger(CountryServicePublisher.class);
+
+ public static void main(String[] args) {
+ Endpoint endpoint = Endpoint.create(new CountryServiceImpl());
+ endpoint.publish("http://localhost:8888/ws/country");
+
+ logger.info("Country web service ready to consume requests!");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Currency.java b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Currency.java
new file mode 100644
index 0000000000..d1b25a26c6
--- /dev/null
+++ b/core-java-modules/core-java-11-2/src/main/java/com/baeldung/soap/ws/server/Currency.java
@@ -0,0 +1,15 @@
+package com.baeldung.soap.ws.server;
+
+public enum Currency {
+
+ EUR, INR, USD;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java b/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java
index fb7d5647a5..128c7f60f4 100644
--- a/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java
+++ b/core-java-modules/core-java-11-2/src/test/java/com/baeldung/version/VersionUnitTest.java
@@ -9,7 +9,7 @@ public class VersionUnitTest {
@Test
public void givenJava_whenUsingRuntime_thenGetVersion() {
- String expectedVersion = "11";
+ String expectedVersion = "15";
Runtime.Version runtimeVersion = Runtime.version();
String version = String.valueOf(runtimeVersion.version().get(0));
Assertions.assertThat(version).isEqualTo(expectedVersion);
diff --git a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
index a3a5592cd9..3b9db284c8 100644
--- a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
+++ b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
@@ -17,8 +17,8 @@ import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
+import org.junit.Ignore;
import org.junit.Test;
-import org.junit.jupiter.api.Disabled;
public class HttpRequestUnitTest {
@@ -51,10 +51,10 @@ public class HttpRequestUnitTest {
/*
* This test will fail as soon as the given URL returns a HTTP 2 response.
- * Therefore, let's leave it commented out.
+ * Therefore, let's leave it ignored.
* */
- @Test
- @Disabled
+ @Test
+ @Ignore
public void shouldFallbackToHttp1_1WhenWebsiteDoesNotUseHttp2() throws IOException, InterruptedException, URISyntaxException, NoSuchAlgorithmException {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
diff --git a/core-java-modules/core-java-12/pom.xml b/core-java-modules/core-java-12/pom.xml
index 8f6abdda5b..ce7ec72aeb 100644
--- a/core-java-modules/core-java-12/pom.xml
+++ b/core-java-modules/core-java-12/pom.xml
@@ -1,49 +1,60 @@
- 4.0.0
- core-java-12
- 0.1.0-SNAPSHOT
- core-java-12
- jar
- http://maven.apache.org
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ core-java-12
+ 0.1.0-SNAPSHOT
+ core-java-12
+ jar
+ http://maven.apache.org
-
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
- ../../
-
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ../../
+
-
-
- org.assertj
- assertj-core
- ${assertj.version}
- test
-
-
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ commons-io
+ commons-io
+ 2.11.0
+
+
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- ${maven.compiler.source.version}
- ${maven.compiler.target.version}
- --enable-preview
-
-
-
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source.version}
+ ${maven.compiler.target.version}
+ --enable-preview
+
+
+
+ maven-surefire-plugin
+
+ --enable-preview
+
+
+
+
-
- 12
- 12
- 3.6.1
-
+
+ 12
+ 12
+ 3.6.1
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java b/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java
new file mode 100644
index 0000000000..637ddb4a91
--- /dev/null
+++ b/core-java-modules/core-java-12/src/main/java/com/baeldung/file/content/comparison/CompareFileContents.java
@@ -0,0 +1,88 @@
+package com.baeldung.file.content.comparison;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class CompareFileContents {
+
+ public static long filesCompareByByte(Path path1, Path path2) throws IOException {
+
+ if (path1.getFileSystem()
+ .provider()
+ .isSameFile(path1, path2)) {
+ return -1;
+ }
+
+ try (BufferedInputStream fis1 = new BufferedInputStream(new FileInputStream(path1.toFile()));
+ BufferedInputStream fis2 = new BufferedInputStream(new FileInputStream(path2.toFile()))) {
+ int ch = 0;
+ long pos = 1;
+ while ((ch = fis1.read()) != -1) {
+ if (ch != fis2.read()) {
+ return pos;
+ }
+ pos++;
+ }
+ if (fis2.read() == -1) {
+ return -1;
+ } else {
+ return pos;
+ }
+ }
+ }
+
+ public static long filesCompareByLine(Path path1, Path path2) throws IOException {
+
+ if (path1.getFileSystem()
+ .provider()
+ .isSameFile(path1, path2)) {
+ return -1;
+ }
+
+ try (BufferedReader bf1 = Files.newBufferedReader(path1);
+ BufferedReader bf2 = Files.newBufferedReader(path2)) {
+
+ long lineNumber = 1;
+ String line1 = "", line2 = "";
+ while ((line1 = bf1.readLine()) != null) {
+ line2 = bf2.readLine();
+ if (line2 == null || !line1.equals(line2)) {
+ return lineNumber;
+ }
+ lineNumber++;
+ }
+ if (bf2.readLine() == null) {
+ return -1;
+ } else {
+ return lineNumber;
+ }
+ }
+ }
+
+ public static boolean compareByMemoryMappedFiles(Path path1, Path path2) throws IOException {
+
+ try (RandomAccessFile randomAccessFile1 = new RandomAccessFile(path1.toFile(), "r");
+ RandomAccessFile randomAccessFile2 = new RandomAccessFile(path2.toFile(), "r")) {
+
+ FileChannel ch1 = randomAccessFile1.getChannel();
+ FileChannel ch2 = randomAccessFile2.getChannel();
+ if (ch1.size() != ch2.size()) {
+
+ return false;
+ }
+ long size = ch1.size();
+ MappedByteBuffer m1 = ch1.map(FileChannel.MapMode.READ_ONLY, 0L, size);
+ MappedByteBuffer m2 = ch2.map(FileChannel.MapMode.READ_ONLY, 0L, size);
+
+ return m1.equals(m2);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java
new file mode 100644
index 0000000000..0d86abae11
--- /dev/null
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CampareFileContentsApacheIOUnitTest.java
@@ -0,0 +1,88 @@
+package com.baeldung.file.content.comparison;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class CampareFileContentsApacheIOUnitTest {
+
+ public static Path path1 = null;
+ public static Path path2 = null;
+
+ @BeforeAll
+ public static void setup() throws IOException {
+
+ path1 = Files.createTempFile("file1Test", ".txt");
+ path2 = Files.createTempFile("file2Test", ".txt");
+ }
+
+ @Test
+ public void whenFilesIdentical_thenReturnTrue() throws IOException {
+
+ InputStream inputStream1 = new FileInputStream(path1.toFile());
+ InputStream inputStream2 = new FileInputStream(path2.toFile());
+
+ Files.writeString(path1, "testing line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2");
+
+ assertTrue(IOUtils.contentEquals(inputStream1, inputStream2));
+ }
+
+ @Test
+ public void whenFilesDifferent_thenReturnFalse() throws IOException {
+
+ InputStream inputStream1 = new FileInputStream(path1.toFile());
+ InputStream inputStream2 = new FileInputStream(path2.toFile());
+
+ Files.writeString(path1, "testing line " + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2");
+
+ assertFalse(IOUtils.contentEquals(inputStream1, inputStream2));
+ }
+
+ @Test
+ public void whenFilesIdenticalIgnoreEOF_thenReturnTrue() throws IOException {
+
+ Files.writeString(path1, "testing line 1 \n line 2");
+ Files.writeString(path2, "testing line 1 \r\n line 2");
+
+ Reader reader1 = new BufferedReader(new FileReader(path1.toFile()));
+ Reader reader2 = new BufferedReader(new FileReader(path2.toFile()));
+
+ assertTrue(IOUtils.contentEqualsIgnoreEOL(reader1, reader2));
+ }
+
+ @Test
+ public void whenFilesNotIdenticalIgnoreEOF_thenReturnFalse() throws IOException {
+
+ Files.writeString(path1, "testing line \n line 2");
+ Files.writeString(path2, "testing line 1 \r\n line 2");
+
+ Reader reader1 = new BufferedReader(new FileReader(path1.toFile()));
+ Reader reader2 = new BufferedReader(new FileReader(path2.toFile()));
+
+ assertFalse(IOUtils.contentEqualsIgnoreEOL(reader1, reader2));
+ }
+
+ @AfterAll
+ public static void shutDown() {
+
+ path1.toFile()
+ .deleteOnExit();
+ path2.toFile()
+ .deleteOnExit();
+ }
+}
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java
new file mode 100644
index 0000000000..0405ac3b52
--- /dev/null
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareByMemoryMappedFilesUnitTest.java
@@ -0,0 +1,42 @@
+package com.baeldung.file.content.comparison;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class CompareByMemoryMappedFilesUnitTest {
+
+ public static Path path1 = null;
+ public static Path path2 = null;
+
+ @BeforeAll
+ public static void setup() throws IOException {
+
+ path1 = Files.createTempFile("file1Test", ".txt");
+ path2 = Files.createTempFile("file2Test", ".txt");
+ }
+
+ @Test
+ public void whenFilesIdentical_thenReturnTrue() throws IOException {
+
+ Files.writeString(path1, "testing line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2");
+
+ assertTrue(CompareFileContents.compareByMemoryMappedFiles(path1, path2));
+ }
+
+ @Test
+ public void whenFilesDifferent_thenReturnFalse() throws IOException {
+
+ Files.writeString(path1, "testing line " + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing line 1" + System.lineSeparator() + "line 2");
+
+ assertFalse(CompareFileContents.compareByMemoryMappedFiles(path1, path2));
+ }
+}
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java
new file mode 100644
index 0000000000..15efc952c2
--- /dev/null
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByBytesUnitTest.java
@@ -0,0 +1,96 @@
+package com.baeldung.file.content.comparison;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class CompareFileContentsByBytesUnitTest {
+
+ public static Path path1 = null;
+ public static Path path2 = null;
+
+ @BeforeAll
+ public static void setup() throws IOException {
+
+ path1 = Files.createTempFile("file1Test", ".txt");
+ path2 = Files.createTempFile("file2Test", ".txt");
+ }
+
+ @Test
+ public void whenFirstFileShorter_thenPositionInSecondFile() throws IOException {
+
+ Files.writeString(path1, "testing");
+ Files.writeString(path2, "testing1");
+
+ assertEquals(8, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenSecondFileShorter_thenPositionInFirstFile() throws IOException {
+
+ Files.writeString(path1, "testing1");
+ Files.writeString(path2, "testing");
+
+ assertEquals(8, CompareFileContents.filesCompareByByte(path1, path2));
+
+ }
+
+ @Test
+ public void whenFilesIdentical_thenSuccess() throws IOException {
+
+ Files.writeString(path1, "testing");
+ Files.writeString(path2, "testing");
+
+ assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2));
+
+ }
+
+ @Test
+ public void whenFilesDifferent_thenPosition() throws IOException {
+
+ Files.writeString(path1, "tesXing");
+ Files.writeString(path2, "testing");
+
+ assertEquals(4, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenBothFilesEmpty_thenEqual() throws IOException {
+
+ Files.writeString(path1, "");
+ Files.writeString(path2, "");
+
+ assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenFirstEmpty_thenPositionFirst() throws IOException {
+
+ Files.writeString(path1, "");
+ Files.writeString(path2, "test");
+
+ assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenSecondEmpty_thenPositionFirst() throws IOException {
+
+ Files.writeString(path1, "test");
+ Files.writeString(path2, "");
+
+ assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @AfterAll
+ public static void shutDown() {
+
+ path1.toFile().deleteOnExit();
+ path2.toFile().deleteOnExit();
+ }
+}
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java
new file mode 100644
index 0000000000..63170221d3
--- /dev/null
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/file/content/comparison/CompareFileContentsByLinesUnitTest.java
@@ -0,0 +1,94 @@
+package com.baeldung.file.content.comparison;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class CompareFileContentsByLinesUnitTest {
+
+ public static Path path1 = null;
+ public static Path path2 = null;
+
+ @BeforeAll
+ public static void setup() throws IOException {
+
+ path1 = Files.createTempFile("file1Test", ".txt");
+ path2 = Files.createTempFile("file2Test", ".txt");
+ }
+
+ @Test
+ public void whenFirstFileShorter_thenLineNumbersFirstFile() throws IOException {
+
+ Files.writeString(path1, "testing line 1");
+ Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2");
+
+ assertEquals(1, CompareFileContents.filesCompareByLine(path1, path2));
+ }
+
+ @Test
+ public void whenSecondFileShorter_thenLineNumbersSecondFile() throws IOException {
+
+ Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing line 1");
+
+ assertEquals(1, CompareFileContents.filesCompareByLine(path1, path2));
+ }
+
+ @Test
+ public void whenFileIdentical_thenLineSuccess() throws IOException {
+
+ Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2");
+
+ assertEquals(-1, CompareFileContents.filesCompareByLine(path1, path2));
+ }
+
+ @Test
+ public void whenFilesDifferent_thenLineNumber() throws IOException {
+
+ Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "linX 2");
+
+ assertEquals(2, CompareFileContents.filesCompareByLine(path1, path2));
+ }
+
+ @Test
+ public void whenBothFilesEmpty_thenEqual() throws IOException {
+
+ Files.writeString(path1, "");
+ Files.writeString(path2, "");
+
+ assertEquals(-1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenFirstEmpty_thenPositionFirst() throws IOException {
+
+ Files.writeString(path1, "");
+ Files.writeString(path2, "testing1 line 1" + System.lineSeparator() + "line 2");
+
+ assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @Test
+ public void whenSecondEmpty_thenPositionFirst() throws IOException {
+
+ Files.writeString(path1, "testing1 line 1" + System.lineSeparator() + "line 2");
+ Files.writeString(path2, "");
+
+ assertEquals(1, CompareFileContents.filesCompareByByte(path1, path2));
+ }
+
+ @AfterAll
+ public static void shutDown() {
+
+ path1.toFile().deleteOnExit();
+ path2.toFile().deleteOnExit();
+ }
+}
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java
index 08a6d58d72..a557ce5545 100644
--- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/CompactNumbersUnitTest.java
@@ -1,6 +1,6 @@
-package java.com.baeldung.newfeatures;
+package com.baeldung.newfeatures;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.text.NumberFormat;
import java.util.Locale;
@@ -16,6 +16,6 @@ public class CompactNumbersUnitTest {
assertEquals("2.59K", likesShort.format(2592));
NumberFormat likesLong = NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.LONG);
likesLong.setMaximumFractionDigits(2);
- assertEquals("2.59 thousand", likesShort.format(2592));
+ assertEquals("2.59 thousand", likesLong.format(2592));
}
}
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java
index 7f081fe399..93fcfbda02 100644
--- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/FileMismatchUnitTest.java
@@ -1,6 +1,6 @@
-package java.com.baeldung.newfeatures;
+package com.baeldung.newfeatures;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java
index 5ae51bd960..1651fe0ee6 100644
--- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/StringUnitTest.java
@@ -1,6 +1,6 @@
-package java.com.baeldung.newfeatures;
+package com.baeldung.newfeatures;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
diff --git a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java
index 30a5cb40a7..a925e693ff 100644
--- a/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java
+++ b/core-java-modules/core-java-12/src/test/java/com/baeldung/newfeatures/TeeingCollectorUnitTest.java
@@ -1,6 +1,6 @@
-package java.com.baeldung.newfeatures;
+package com.baeldung.newfeatures;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -13,6 +13,6 @@ public class TeeingCollectorUnitTest {
public void givenSetOfNumbers_thenCalculateAverage() {
double mean = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.teeing(Collectors.summingDouble(i -> i), Collectors.counting(), (sum, count) -> sum / count));
- assertEquals(3.0, mean);
+ assertEquals(3.0, mean, 0);
}
}
diff --git a/core-java-modules/core-java-16/README.md b/core-java-modules/core-java-16/README.md
new file mode 100644
index 0000000000..760513189f
--- /dev/null
+++ b/core-java-modules/core-java-16/README.md
@@ -0,0 +1,3 @@
+### Relevant articles:
+
+- [Collect a Java Stream to an Immutable Collection](https://www.baeldung.com/java-stream-immutable-collection)
diff --git a/core-java-modules/core-java-16/pom.xml b/core-java-modules/core-java-16/pom.xml
new file mode 100644
index 0000000000..a8a84511db
--- /dev/null
+++ b/core-java-modules/core-java-16/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+ core-java-16
+ 0.1.0-SNAPSHOT
+ core-java-16
+ jar
+ http://maven.apache.org
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ../../
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source.version}
+ ${maven.compiler.target.version}
+
+
+
+
+
+
+ 16
+ 16
+ 3.6.1
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java
new file mode 100644
index 0000000000..adaa1044ff
--- /dev/null
+++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPost.java
@@ -0,0 +1,38 @@
+package com.baeldung.java_16_features.groupingby;
+
+public class BlogPost {
+
+ private String title;
+ private String author;
+ private BlogPostType type;
+ private int likes;
+ record AuthPostTypesLikes(String author, BlogPostType type, int likes) {};
+
+ public BlogPost(String title, String author, BlogPostType type, int likes) {
+ this.title = title;
+ this.author = author;
+ this.type = type;
+ this.likes = likes;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public BlogPostType getType() {
+ return type;
+ }
+
+ public int getLikes() {
+ return likes;
+ }
+
+ @Override
+ public String toString() {
+ return "BlogPost{" + "title='" + title + '\'' + ", type=" + type + ", likes=" + likes + '}';
+ }
+}
diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java
new file mode 100644
index 0000000000..df38b7e1c4
--- /dev/null
+++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/BlogPostType.java
@@ -0,0 +1,5 @@
+package com.baeldung.java_16_features.groupingby;
+
+public enum BlogPostType {
+ NEWS, REVIEW, GUIDE
+}
diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java
new file mode 100644
index 0000000000..ad41207aa4
--- /dev/null
+++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/java_16_features/groupingby/Tuple.java
@@ -0,0 +1,41 @@
+package com.baeldung.java_16_features.groupingby;
+
+import java.util.Objects;
+
+public class Tuple {
+ private final BlogPostType type;
+ private final String author;
+
+ public Tuple(BlogPostType type, String author) {
+ this.type = type;
+ this.author = author;
+ }
+
+ public BlogPostType getType() {
+ return type;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Tuple tuple = (Tuple) o;
+ return type == tuple.type && author.equals(tuple.author);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, author);
+ }
+
+ @Override
+ public String toString() {
+ return "Tuple{" + "type=" + type + ", author='" + author + '\'' + '}';
+ }
+}
diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java
new file mode 100644
index 0000000000..0e926246ff
--- /dev/null
+++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/java_16_features/groupingby/JavaGroupingByCollectorUnitTest.java
@@ -0,0 +1,254 @@
+package com.baeldung.java_16_features.groupingby;
+
+import static java.util.Comparator.comparingInt;
+import static java.util.stream.Collectors.averagingInt;
+import static java.util.stream.Collectors.counting;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.groupingByConcurrent;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.mapping;
+import static java.util.stream.Collectors.maxBy;
+import static java.util.stream.Collectors.summarizingInt;
+import static java.util.stream.Collectors.summingInt;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.IntSummaryStatistics;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.jupiter.api.Test;
+
+public class JavaGroupingByCollectorUnitTest {
+
+ private static final List posts = Arrays.asList(new BlogPost("News item 1", "Author 1", BlogPostType.NEWS, 15), new BlogPost("Tech review 1", "Author 2", BlogPostType.REVIEW, 5),
+ new BlogPost("Programming guide", "Author 1", BlogPostType.GUIDE, 20), new BlogPost("News item 2", "Author 2", BlogPostType.NEWS, 35), new BlogPost("Tech review 2", "Author 1", BlogPostType.REVIEW, 15));
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByType_thenGetAMapBetweenTypeAndPosts() {
+ Map> postsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType));
+
+ assertEquals(2, postsPerType.get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, postsPerType.get(BlogPostType.GUIDE)
+ .size());
+ assertEquals(2, postsPerType.get(BlogPostType.REVIEW)
+ .size());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndTheirTitlesAreJoinedInAString_thenGetAMapBetweenTypeAndCsvTitles() {
+ Map postsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, mapping(BlogPost::getTitle, joining(", ", "Post titles: [", "]"))));
+
+ assertEquals("Post titles: [News item 1, News item 2]", postsPerType.get(BlogPostType.NEWS));
+ assertEquals("Post titles: [Programming guide]", postsPerType.get(BlogPostType.GUIDE));
+ assertEquals("Post titles: [Tech review 1, Tech review 2]", postsPerType.get(BlogPostType.REVIEW));
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndSumTheLikes_thenGetAMapBetweenTypeAndPostLikes() {
+ Map likesPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes)));
+
+ assertEquals(50, likesPerType.get(BlogPostType.NEWS)
+ .intValue());
+ assertEquals(20, likesPerType.get(BlogPostType.REVIEW)
+ .intValue());
+ assertEquals(20, likesPerType.get(BlogPostType.GUIDE)
+ .intValue());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeInAnEnumMap_thenGetAnEnumMapBetweenTypeAndPosts() {
+ EnumMap> postsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, () -> new EnumMap<>(BlogPostType.class), toList()));
+
+ assertEquals(2, postsPerType.get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, postsPerType.get(BlogPostType.GUIDE)
+ .size());
+ assertEquals(2, postsPerType.get(BlogPostType.REVIEW)
+ .size());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeInSets_thenGetAMapBetweenTypesAndSetsOfPosts() {
+ Map> postsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, toSet()));
+
+ assertEquals(2, postsPerType.get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, postsPerType.get(BlogPostType.GUIDE)
+ .size());
+ assertEquals(2, postsPerType.get(BlogPostType.REVIEW)
+ .size());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeConcurrently_thenGetAMapBetweenTypeAndPosts() {
+ ConcurrentMap> postsPerType = posts.parallelStream()
+ .collect(groupingByConcurrent(BlogPost::getType));
+
+ assertEquals(2, postsPerType.get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, postsPerType.get(BlogPostType.GUIDE)
+ .size());
+ assertEquals(2, postsPerType.get(BlogPostType.REVIEW)
+ .size());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndAveragingLikes_thenGetAMapBetweenTypeAndAverageNumberOfLikes() {
+ Map averageLikesPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes)));
+
+ assertEquals(25, averageLikesPerType.get(BlogPostType.NEWS)
+ .intValue());
+ assertEquals(20, averageLikesPerType.get(BlogPostType.GUIDE)
+ .intValue());
+ assertEquals(10, averageLikesPerType.get(BlogPostType.REVIEW)
+ .intValue());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndCounted_thenGetAMapBetweenTypeAndNumberOfPosts() {
+ Map numberOfPostsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, counting()));
+
+ assertEquals(2, numberOfPostsPerType.get(BlogPostType.NEWS)
+ .intValue());
+ assertEquals(1, numberOfPostsPerType.get(BlogPostType.GUIDE)
+ .intValue());
+ assertEquals(2, numberOfPostsPerType.get(BlogPostType.REVIEW)
+ .intValue());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndMaxingLikes_thenGetAMapBetweenTypeAndMaximumNumberOfLikes() {
+ Map> maxLikesPerPostType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, maxBy(comparingInt(BlogPost::getLikes))));
+
+ assertTrue(maxLikesPerPostType.get(BlogPostType.NEWS)
+ .isPresent());
+ assertEquals(35, maxLikesPerPostType.get(BlogPostType.NEWS)
+ .get()
+ .getLikes());
+
+ assertTrue(maxLikesPerPostType.get(BlogPostType.GUIDE)
+ .isPresent());
+ assertEquals(20, maxLikesPerPostType.get(BlogPostType.GUIDE)
+ .get()
+ .getLikes());
+
+ assertTrue(maxLikesPerPostType.get(BlogPostType.REVIEW)
+ .isPresent());
+ assertEquals(15, maxLikesPerPostType.get(BlogPostType.REVIEW)
+ .get()
+ .getLikes());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByAuthorAndThenByType_thenGetAMapBetweenAuthorAndMapsBetweenTypeAndBlogPosts() {
+ Map>> map = posts.stream()
+ .collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));
+
+ assertEquals(1, map.get("Author 1")
+ .get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, map.get("Author 1")
+ .get(BlogPostType.GUIDE)
+ .size());
+ assertEquals(1, map.get("Author 1")
+ .get(BlogPostType.REVIEW)
+ .size());
+
+ assertEquals(1, map.get("Author 2")
+ .get(BlogPostType.NEWS)
+ .size());
+ assertEquals(1, map.get("Author 2")
+ .get(BlogPostType.REVIEW)
+ .size());
+ assertNull(map.get("Author 2")
+ .get(BlogPostType.GUIDE));
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByTypeAndSummarizingLikes_thenGetAMapBetweenTypeAndSummary() {
+ Map likeStatisticsPerType = posts.stream()
+ .collect(groupingBy(BlogPost::getType, summarizingInt(BlogPost::getLikes)));
+
+ IntSummaryStatistics newsLikeStatistics = likeStatisticsPerType.get(BlogPostType.NEWS);
+
+ assertEquals(2, newsLikeStatistics.getCount());
+ assertEquals(50, newsLikeStatistics.getSum());
+ assertEquals(25.0, newsLikeStatistics.getAverage(), 0.001);
+ assertEquals(35, newsLikeStatistics.getMax());
+ assertEquals(15, newsLikeStatistics.getMin());
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByComplexMapPairKeyType_thenGetAMapBetweenPairAndList() {
+
+ Map, List> postsPerTypeAndAuthor = posts.stream()
+ .collect(groupingBy(post -> new ImmutablePair<>(post.getType(), post.getAuthor())));
+
+ List result = postsPerTypeAndAuthor.get(new ImmutablePair<>(BlogPostType.GUIDE, "Author 1"));
+
+ assertThat(result.size()).isEqualTo(1);
+
+ BlogPost blogPost = result.get(0);
+
+ assertThat(blogPost.getTitle()).isEqualTo("Programming guide");
+ assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE);
+ assertThat(blogPost.getAuthor()).isEqualTo("Author 1");
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByComplexMapKeyType_thenGetAMapBetweenTupleAndList() {
+
+ Map> postsPerTypeAndAuthor = posts.stream()
+ .collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor())));
+
+ List result = postsPerTypeAndAuthor.get(new Tuple(BlogPostType.GUIDE, "Author 1"));
+
+ assertThat(result.size()).isEqualTo(1);
+
+ BlogPost blogPost = result.get(0);
+
+ assertThat(blogPost.getTitle()).isEqualTo("Programming guide");
+ assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE);
+ assertThat(blogPost.getAuthor()).isEqualTo("Author 1");
+ }
+
+ @Test
+ public void givenAListOfPosts_whenGroupedByRecord_thenGetAMapBetweenRecordAndList() {
+
+ Map> postsPerTypeAndAuthor = posts.stream()
+ .collect(groupingBy(post -> new BlogPost.AuthPostTypesLikes(post.getAuthor(), post.getType(), post.getLikes())));
+
+ List result = postsPerTypeAndAuthor.get(new BlogPost.AuthPostTypesLikes("Author 1", BlogPostType.GUIDE, 20));
+
+ assertThat(result.size()).isEqualTo(1);
+
+ BlogPost blogPost = result.get(0);
+
+ assertThat(blogPost.getTitle()).isEqualTo("Programming guide");
+ assertThat(blogPost.getType()).isEqualTo(BlogPostType.GUIDE);
+ assertThat(blogPost.getAuthor()).isEqualTo("Author 1");
+ assertThat(blogPost.getLikes()).isEqualTo(20);
+ }
+
+}
diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java
new file mode 100644
index 0000000000..afd7369d8d
--- /dev/null
+++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.streams;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class StreamToImmutableUnitTest {
+
+ @Test
+ public void whenUsingStreamToList_thenReturnImmutableList() {
+
+ List immutableList = Stream.of("a", "b", "c", "d")
+ .toList();
+
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> {
+ immutableList.add("e");
+ });
+
+ }
+
+}
diff --git a/core-java-modules/core-java-8-2/README.md b/core-java-modules/core-java-8-2/README.md
index c1c09d2192..7f2245ccc0 100644
--- a/core-java-modules/core-java-8-2/README.md
+++ b/core-java-modules/core-java-8-2/README.md
@@ -7,4 +7,5 @@ This module contains articles about Java 8 core features
- [Run a Java Application from the Command Line](https://www.baeldung.com/java-run-jar-with-arguments)
- [Java 8 Stream skip() vs limit()](https://www.baeldung.com/java-stream-skip-vs-limit)
- [Guide to Java BiFunction Interface](https://www.baeldung.com/java-bifunction-interface)
+- [Interface With Default Methods vs Abstract Class](https://www.baeldung.com/java-interface-default-method-vs-abstract-class)
- [[<-- Prev]](/core-java-modules/core-java-8)
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java
new file mode 100644
index 0000000000..2aadf2e2e8
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChidlCircleInterfaceImpl.java
@@ -0,0 +1,14 @@
+package com.baeldung.interfaceVsAbstractClass;
+
+public class ChidlCircleInterfaceImpl implements CircleInterface {
+ private String color;
+
+ @Override
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java
new file mode 100644
index 0000000000..8d26bda306
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/ChildCircleClass.java
@@ -0,0 +1,5 @@
+package com.baeldung.interfaceVsAbstractClass;
+
+public class ChildCircleClass extends CircleClass {
+
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java
new file mode 100644
index 0000000000..048ffa4fe9
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleClass.java
@@ -0,0 +1,23 @@
+package com.baeldung.interfaceVsAbstractClass;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class CircleClass {
+
+ private String color;
+ private List allowedColors = Arrays.asList("RED", "GREEN", "BLUE");
+
+ public boolean isValid() {
+ return allowedColors.contains(getColor());
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+}
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java
new file mode 100644
index 0000000000..a0c3a6d9c6
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/interfaceVsAbstractClass/CircleInterface.java
@@ -0,0 +1,14 @@
+package com.baeldung.interfaceVsAbstractClass;
+
+import java.util.Arrays;
+import java.util.List;
+
+public interface CircleInterface {
+ List allowedColors = Arrays.asList("RED", "GREEN", "BLUE");
+
+ String getColor();
+
+ public default boolean isValid() {
+ return allowedColors.contains(getColor());
+ }
+}
diff --git a/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java
new file mode 100644
index 0000000000..8cba13ddeb
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/interfaceVsAbstractClass/InterfaceVsAbstractClassUnitTest.java
@@ -0,0 +1,21 @@
+package com.baeldung.interfaceVsAbstractClass;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class InterfaceVsAbstractClassUnitTest {
+ @Test
+ public void givenAbstractClass_whenValidCircleUsed_thenPass() {
+ CircleClass redCircle = new ChildCircleClass();
+ redCircle.setColor("RED");
+ assertTrue(redCircle.isValid());
+ }
+
+ @Test
+ public void givenInterface_whenValidCircleWithoutStateUsed_thenPass() {
+ ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl();
+ redCircleWithoutState.setColor("RED");
+ assertTrue(redCircleWithoutState.isValid());
+ }
+}
diff --git a/core-java-modules/core-java-9/README.md b/core-java-modules/core-java-9/README.md
index cab02369cc..8203755417 100644
--- a/core-java-modules/core-java-9/README.md
+++ b/core-java-modules/core-java-9/README.md
@@ -10,3 +10,4 @@ This module contains articles about Java 9 core features
- [Initialize a HashMap in Java](https://www.baeldung.com/java-initialize-hashmap)
- [Immutable ArrayList in Java](https://www.baeldung.com/java-immutable-list)
- [Easy Ways to Write a Java InputStream to an OutputStream](https://www.baeldung.com/java-inputstream-to-outputstream)
+- [Private Methods in Java Interfaces](https://www.baeldung.com/java-interface-private-methods)
diff --git a/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java
new file mode 100644
index 0000000000..388927fbfe
--- /dev/null
+++ b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/CustomFoo.java
@@ -0,0 +1,10 @@
+package com.baeldung.java9.interfaces;
+
+public class CustomFoo implements Foo {
+
+ public static void main(String... args) {
+ Foo customFoo = new CustomFoo();
+ customFoo.bar(); // 'Hello world!'
+ Foo.buzz(); // 'Hello static world!'
+ }
+}
diff --git a/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java
new file mode 100644
index 0000000000..8ccee83b7e
--- /dev/null
+++ b/core-java-modules/core-java-9/src/main/java/com/baeldung/java9/interfaces/Foo.java
@@ -0,0 +1,22 @@
+package com.baeldung.java9.interfaces;
+
+public interface Foo {
+
+ public default void bar() {
+ System.out.print("Hello");
+ baz();
+ }
+
+ public static void buzz() {
+ System.out.print("Hello");
+ staticBaz();
+ }
+
+ private void baz() {
+ System.out.print(" world!");
+ }
+
+ private static void staticBaz() {
+ System.out.print(" static world!");
+ }
+}
diff --git a/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java b/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java
new file mode 100644
index 0000000000..d107c091fb
--- /dev/null
+++ b/core-java-modules/core-java-9/src/test/java/com/baeldung/java9/interfaces/CustomFooUnitTest.java
@@ -0,0 +1,38 @@
+package com.baeldung.java9.interfaces;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CustomFooUnitTest {
+ private ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ private PrintStream originalOut = System.out;
+
+ @BeforeEach
+ void setup() {
+ System.setOut(new PrintStream(outContent));
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setOut(originalOut);
+ }
+
+ @Test
+ void givenACustomFooObject_whenCallingDefaultMethodBar_thenExpectedStringIsWrittenToSystemOut() {
+ CustomFoo customFoo = new CustomFoo();
+ customFoo.bar();
+ assertThat(outContent.toString()).isEqualTo("Hello world!");
+ }
+
+ @Test
+ void givenAFooInterface_whenCallingStaticMethodBuzz_thenExpectedStringIsWrittenToSystemOut() {
+ Foo.buzz();
+ assertThat(outContent.toString()).isEqualTo("Hello static world!");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-annotations/README.md b/core-java-modules/core-java-annotations/README.md
index 93da3aea62..18f5589771 100644
--- a/core-java-modules/core-java-annotations/README.md
+++ b/core-java-modules/core-java-annotations/README.md
@@ -10,3 +10,5 @@
- [Overview of Java Built-in Annotations](https://www.baeldung.com/java-default-annotations)
- [Creating a Custom Annotation in Java](https://www.baeldung.com/java-custom-annotation)
- [Efficient Word Frequency Calculator in Java](https://www.baeldung.com/java-word-frequency)
+- [Why Missing Annotations Don’t Cause ClassNotFoundException](https://www.baeldung.com/classnotfoundexception-missing-annotation)
+- [Valid @SuppressWarnings Warning Names](https://www.baeldung.com/java-suppresswarnings-valid-names)
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java
new file mode 100644
index 0000000000..aad7cf49eb
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java
@@ -0,0 +1,46 @@
+package com.baeldung.annotations;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings({"serial", "unchecked"})
+public class ClassWithSuppressWarningsNames implements Serializable {
+
+// private static final long serialVersionUID = -1166032307853492833L;
+
+ @SuppressWarnings("unused")
+ public static void suppressBoxingWarning() {
+ int value = 5;
+ int unusedVar = 10; // no warning here
+ List list = new ArrayList<>();
+ list.add(Integer.valueOf(value));
+ }
+
+ @SuppressWarnings("deprecated")
+ void suppressDeprecatedWarning() {
+ ClassWithSuppressWarningsNames cls = new ClassWithSuppressWarningsNames();
+ cls.deprecatedMethod(); // no warning here
+ }
+
+ @SuppressWarnings("fallthrough")
+ String suppressFallthroughWarning() {
+ int day = 5;
+ switch (day) {
+ case 5:
+ return "This is day 5";
+// break; // no warning here
+ case 10:
+ return "This is day 10";
+// break;
+ default:
+ return "This default day";
+ }
+ }
+
+ @Deprecated
+ String deprecatedMethod() {
+ return "deprecated method";
+ }
+
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java
new file mode 100644
index 0000000000..daf9f60b96
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java
@@ -0,0 +1,8 @@
+package com.baeldung.missingannotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface A {
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java
new file mode 100644
index 0000000000..a86b2f72ec
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java
@@ -0,0 +1,10 @@
+package com.baeldung.missingannotation;
+
+@A
+@C(D.class)
+public class B {
+
+ public static void main(String[] args) {
+ System.out.println("It worked");
+ }
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java
new file mode 100644
index 0000000000..2adf2b42fa
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java
@@ -0,0 +1,9 @@
+package com.baeldung.missingannotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface C {
+ Class> value();
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java
new file mode 100644
index 0000000000..7c534fa682
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java
@@ -0,0 +1,4 @@
+package com.baeldung.missingannotation;
+
+public class D {
+}
diff --git a/core-java-modules/core-java-collections-4/README.md b/core-java-modules/core-java-collections-4/README.md
index 4fd77473d4..0e14f407c4 100644
--- a/core-java-modules/core-java-collections-4/README.md
+++ b/core-java-modules/core-java-collections-4/README.md
@@ -6,3 +6,4 @@
- [ArrayList vs. LinkedList vs. HashMap in Java](https://www.baeldung.com/java-arraylist-vs-linkedlist-vs-hashmap)
- [Java Deque vs. Stack](https://www.baeldung.com/java-deque-vs-stack)
+- [Collection.toArray(new T[0]) or .toArray(new T[size])](https://www.baeldung.com/java-collection-toarray-methods)
diff --git a/core-java-modules/core-java-collections-4/pom.xml b/core-java-modules/core-java-collections-4/pom.xml
index daf2588ece..7eb8222211 100644
--- a/core-java-modules/core-java-collections-4/pom.xml
+++ b/core-java-modules/core-java-collections-4/pom.xml
@@ -1,31 +1,31 @@
- 4.0.0
- core-java-collections-4
- 0.1.0-SNAPSHOT
- core-java-collections-4
- jar
-
-
- com.baeldung.core-java-modules
- core-java-modules
- 0.0.1-SNAPSHOT
- ../pom.xml
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ core-java-collections-4
+ 0.1.0-SNAPSHOT
+ core-java-collections-4
+ jar
-
-
- org.assertj
- assertj-core
- ${assertj.version}
- test
-
-
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../pom.xml
+
-
- 3.19.0
-
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ 3.19.0
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java
new file mode 100644
index 0000000000..fe76a9ab3d
--- /dev/null
+++ b/core-java-modules/core-java-collections-4/src/main/java/com/baeldung/collections/toarraycomparison/ToArrayBenchmark.java
@@ -0,0 +1,72 @@
+package com.baeldung.collections.toarraycomparison;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeSet;
+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.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(value = 3, jvmArgsAppend = { "-XX:+UseParallelGC", "-Xms4g", "-Xmx4g" })
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+public class ToArrayBenchmark {
+
+ @Param({ "10", "10000", "10000000" })
+ private int size;
+
+ @Param({ "array-list", "tree-set" })
+ private String type;
+
+ private Collection collection;
+
+ @Setup
+ public void setup() {
+ switch (type) {
+ case "array-list":
+ collection = new ArrayList();
+ break;
+ case "tree-set":
+ collection = new TreeSet();
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ for (int i = 0; i < size; i++) {
+ collection.add(String.valueOf(i));
+ }
+ }
+
+ @Benchmark
+ public String[] zero_sized() {
+ return collection.toArray(new String[0]);
+ }
+
+ @Benchmark
+ public String[] pre_sized() {
+ return collection.toArray(new String[collection.size()]);
+ }
+
+
+ public static void main(String[] args) {
+ try {
+ org.openjdk.jmh.Main.main(args);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-2/pom.xml b/core-java-modules/core-java-concurrency-2/pom.xml
index 3a29101d0f..71c73f5dff 100644
--- a/core-java-modules/core-java-concurrency-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-2/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-2
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-concurrency-advanced-2/pom.xml b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
index 0571598684..f975dd41a4 100644
--- a/core-java-modules/core-java-concurrency-advanced-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
@@ -31,7 +31,6 @@
jmh-generator-annprocess
${jmh-generator.version}
-
org.assertj
assertj-core
diff --git a/core-java-modules/core-java-concurrency-advanced-4/README.md b/core-java-modules/core-java-concurrency-advanced-4/README.md
index 5b93cec0dd..db856a2cd6 100644
--- a/core-java-modules/core-java-concurrency-advanced-4/README.md
+++ b/core-java-modules/core-java-concurrency-advanced-4/README.md
@@ -2,3 +2,4 @@
- [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock)
- [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices)
+- [Start Two Threads at the Exact Same Time in Java](https://www.baeldung.com/java-start-two-threads-at-same-time)
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java
new file mode 100644
index 0000000000..3e90bd4399
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java
@@ -0,0 +1,86 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Phaser;
+
+public class ThreadsStartAtSameTime {
+
+ public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
+ usingCountDownLatch();
+
+ Thread.sleep(30);
+
+ usingCyclicBarrier();
+
+ Thread.sleep(30);
+
+ usingPhaser();
+
+ }
+
+ private static void usingCountDownLatch() throws InterruptedException {
+ System.out.println("===============================================");
+ System.out.println(" >>> Using CountDownLatch <<<<");
+ System.out.println("===============================================");
+
+ CountDownLatch latch = new CountDownLatch(1);
+
+ WorkerWithCountDownLatch worker1 = new WorkerWithCountDownLatch("Worker with latch 1", latch);
+ WorkerWithCountDownLatch worker2 = new WorkerWithCountDownLatch("Worker with latch 2", latch);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now release the latch:");
+ System.out.println("-----------------------------------------------");
+ latch.countDown();
+ }
+
+ private static void usingCyclicBarrier() throws BrokenBarrierException, InterruptedException {
+ System.out.println("\n===============================================");
+ System.out.println(" >>> Using CyclicBarrier <<<<");
+ System.out.println("===============================================");
+
+ CyclicBarrier barrier = new CyclicBarrier(3);
+
+ WorkerWithCyclicBarrier worker1 = new WorkerWithCyclicBarrier("Worker with barrier 1", barrier);
+ WorkerWithCyclicBarrier worker2 = new WorkerWithCyclicBarrier("Worker with barrier 2", barrier);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now open the barrier:");
+ System.out.println("-----------------------------------------------");
+ barrier.await();
+ }
+
+ private static void usingPhaser() throws InterruptedException {
+ System.out.println("\n===============================================");
+ System.out.println(" >>> Using Phaser <<<");
+ System.out.println("===============================================");
+
+ Phaser phaser = new Phaser();
+ phaser.register();
+
+ WorkerWithPhaser worker1 = new WorkerWithPhaser("Worker with phaser 1", phaser);
+ WorkerWithPhaser worker2 = new WorkerWithPhaser("Worker with phaser 2", phaser);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now open the phaser barrier:");
+ System.out.println("-----------------------------------------------");
+ phaser.arriveAndAwaitAdvance();
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java
new file mode 100644
index 0000000000..44b95ec042
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java
@@ -0,0 +1,24 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+public class WorkerWithCountDownLatch extends Thread {
+ private CountDownLatch latch;
+
+ public WorkerWithCountDownLatch(String name, CountDownLatch latch) {
+ this.latch = latch;
+ setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the latch\n", getName());
+ latch.await();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java
new file mode 100644
index 0000000000..99059e0725
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java
@@ -0,0 +1,25 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+public class WorkerWithCyclicBarrier extends Thread {
+ private CyclicBarrier barrier;
+
+ public WorkerWithCyclicBarrier(String name, CyclicBarrier barrier) {
+ this.barrier = barrier;
+ this.setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the barrier\n", getName());
+ barrier.await();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (InterruptedException | BrokenBarrierException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java
new file mode 100644
index 0000000000..44994d1cd0
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java
@@ -0,0 +1,26 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Phaser;
+
+public class WorkerWithPhaser extends Thread {
+ private Phaser phaser;
+
+ public WorkerWithPhaser(String name, Phaser phaser) {
+ this.phaser = phaser;
+ phaser.register();
+ setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the phaser\n", getName());
+ phaser.arriveAndAwaitAdvance();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced/pom.xml b/core-java-modules/core-java-concurrency-advanced/pom.xml
index 7dae0b17aa..52a2e67e70 100644
--- a/core-java-modules/core-java-concurrency-advanced/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-concurrency-basic-2/README.md b/core-java-modules/core-java-concurrency-basic-2/README.md
index c4a6105c56..d2ff75ca95 100644
--- a/core-java-modules/core-java-concurrency-basic-2/README.md
+++ b/core-java-modules/core-java-concurrency-basic-2/README.md
@@ -13,4 +13,5 @@ This module contains articles about basic Java concurrency
- [Why are Local Variables Thread-Safe in Java](https://www.baeldung.com/java-local-variables-thread-safe)
- [How to Stop Execution After a Certain Time in Java](https://www.baeldung.com/java-stop-execution-after-certain-time)
- [How to Handle InterruptedException in Java](https://www.baeldung.com/java-interrupted-exception)
+- [How to Get the Number of Threads in a Java Process](https://www.baeldung.com/java-get-number-of-threads)
- [[<-- Prev]](/core-java-modules/core-java-concurrency-basic)
diff --git a/core-java-modules/core-java-concurrency-basic-2/pom.xml b/core-java-modules/core-java-concurrency-basic-2/pom.xml
index 38c9c26c80..99c8cee0f2 100644
--- a/core-java-modules/core-java-concurrency-basic-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic-2/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic-2
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java b/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java
new file mode 100644
index 0000000000..8af2724fb6
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-basic-2/src/main/java/com/baeldung/concurrent/threads/number/FindNumberOfThreads.java
@@ -0,0 +1,14 @@
+package com.baeldung.concurrent.threads.number;
+
+import java.lang.management.ManagementFactory;
+
+public class FindNumberOfThreads {
+
+ public static void main(String[] args) {
+ System.out.println("Number of threads " + Thread.activeCount());
+ System.out.println("Current Thread Group - "
+ + Thread.currentThread().getThreadGroup().getName());
+ System.out.println("Total Number of threads "
+ + ManagementFactory.getThreadMXBean().getThreadCount());
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-basic/pom.xml b/core-java-modules/core-java-concurrency-basic/pom.xml
index 3f69d1a73b..5df379efb2 100644
--- a/core-java-modules/core-java-concurrency-basic/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-concurrency-collections-2/pom.xml b/core-java-modules/core-java-concurrency-collections-2/pom.xml
index 020586e892..0a31c64728 100644
--- a/core-java-modules/core-java-concurrency-collections-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-collections-2/pom.xml
@@ -6,7 +6,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-collections-2
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-concurrency-collections/pom.xml b/core-java-modules/core-java-concurrency-collections/pom.xml
index 8a7fc58bfd..edb5a71e39 100644
--- a/core-java-modules/core-java-concurrency-collections/pom.xml
+++ b/core-java-modules/core-java-concurrency-collections/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-concurrency-collections
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-date-operations-1/pom.xml b/core-java-modules/core-java-date-operations-1/pom.xml
index ded6f062cf..955cd910e9 100644
--- a/core-java-modules/core-java-date-operations-1/pom.xml
+++ b/core-java-modules/core-java-date-operations-1/pom.xml
@@ -49,7 +49,6 @@
true
-
org.apache.maven.plugins
diff --git a/core-java-modules/core-java-date-operations-2/pom.xml b/core-java-modules/core-java-date-operations-2/pom.xml
index 5766f39739..bb0435a92a 100644
--- a/core-java-modules/core-java-date-operations-2/pom.xml
+++ b/core-java-modules/core-java-date-operations-2/pom.xml
@@ -7,7 +7,7 @@
${project.parent.version}
core-java-date-operations-2
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-datetime-conversion/pom.xml b/core-java-modules/core-java-datetime-conversion/pom.xml
index b494b307d0..8f082e2793 100644
--- a/core-java-modules/core-java-datetime-conversion/pom.xml
+++ b/core-java-modules/core-java-datetime-conversion/pom.xml
@@ -48,7 +48,6 @@
true
-
org.apache.maven.plugins
diff --git a/core-java-modules/core-java-function/pom.xml b/core-java-modules/core-java-function/pom.xml
index 5db73d8796..1cdcfe47db 100644
--- a/core-java-modules/core-java-function/pom.xml
+++ b/core-java-modules/core-java-function/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-function
jar
-
+
com.baeldung.core-java-modules
core-java-modules
@@ -40,4 +40,4 @@
3.6.1
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-functional/pom.xml b/core-java-modules/core-java-functional/pom.xml
index 87a08e1e3f..3eeec71a5a 100644
--- a/core-java-modules/core-java-functional/pom.xml
+++ b/core-java-modules/core-java-functional/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-functional
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-io-2/pom.xml b/core-java-modules/core-java-io-2/pom.xml
index a4168a8f1f..19f34d6da1 100644
--- a/core-java-modules/core-java-io-2/pom.xml
+++ b/core-java-modules/core-java-io-2/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-io-2
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-io-apis/pom.xml b/core-java-modules/core-java-io-apis/pom.xml
index 0984296ea1..9d0f3196ab 100644
--- a/core-java-modules/core-java-io-apis/pom.xml
+++ b/core-java-modules/core-java-io-apis/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-io-apis
jar
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml
index e594666b55..dcb9d494dc 100644
--- a/core-java-modules/core-java-io-conversions-2/pom.xml
+++ b/core-java-modules/core-java-io-conversions-2/pom.xml
@@ -36,6 +36,16 @@
core-java-io-conversions-2
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${source.version}
+ ${target.version}
+
+
+
src/main/resources
@@ -45,6 +55,8 @@
+ 11
+ 11
20200518
4.1
diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
index c34c32891f..03f528766b 100644
--- a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
+++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
@@ -21,11 +21,11 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
-import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;
import java.util.UUID;
@@ -70,6 +70,16 @@ public class JavaInputStreamToXUnitTest {
assertThat(text, equalTo(originalString));
}
+ @Test
+ public void givenUsingJava9_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException {
+ final String originalString = randomAlphabetic(DEFAULT_SIZE);
+ final InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
+
+ final String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+
+ assertThat(text, equalTo(originalString));
+ }
+
@Test
public final void givenUsingJava7_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException {
final String originalString = randomAlphabetic(DEFAULT_SIZE);
@@ -155,15 +165,13 @@ public class JavaInputStreamToXUnitTest {
@Test
public final void whenConvertingToFile_thenCorrect() throws IOException {
- final InputStream initialStream = new FileInputStream(new File("src/test/resources/sample.txt"));
- final byte[] buffer = new byte[initialStream.available()];
- initialStream.read(buffer);
+ final Path path = Paths.get("src/test/resources/sample.txt");
+ final byte[] buffer = java.nio.file.Files.readAllBytes(path);
final File targetFile = new File("src/test/resources/targetFile.tmp");
final OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
- IOUtils.closeQuietly(initialStream);
IOUtils.closeQuietly(outStream);
}
diff --git a/core-java-modules/core-java-io-conversions/pom.xml b/core-java-modules/core-java-io-conversions/pom.xml
index 14320f46b9..2dd21a80e8 100644
--- a/core-java-modules/core-java-io-conversions/pom.xml
+++ b/core-java-modules/core-java-io-conversions/pom.xml
@@ -7,6 +7,7 @@
0.1.0-SNAPSHOT
core-java-io-conversions
jar
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-io/pom.xml b/core-java-modules/core-java-io/pom.xml
index 67495f84f9..0cf6d6767e 100644
--- a/core-java-modules/core-java-io/pom.xml
+++ b/core-java-modules/core-java-io/pom.xml
@@ -7,7 +7,7 @@
0.1.0-SNAPSHOT
core-java-io
jar
-
+
com.baeldung.core-java-modules
core-java-modules
@@ -97,6 +97,7 @@
+
integration
diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml
index 4e4a7ee465..b638f81b0c 100644
--- a/core-java-modules/core-java-jar/pom.xml
+++ b/core-java-modules/core-java-jar/pom.xml
@@ -7,6 +7,7 @@
0.1.0-SNAPSHOT
core-java-jar
jar
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml
index d0dce32c33..f82e72b85d 100644
--- a/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml
+++ b/core-java-modules/core-java-jpms/decoupling-pattern1/consumermodule1/pom.xml
@@ -10,7 +10,7 @@
com.baeldung.decoupling-pattern1
decoupling-pattern1
- 1.0
+ 1.0-SNAPSHOT
@@ -26,6 +26,10 @@
org.apache.maven.plugins
maven-compiler-plugin
+
+ ${source.version}
+ ${target.version}
+
diff --git a/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml
index 8e30fa9182..fc4b5854f9 100644
--- a/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml
+++ b/core-java-modules/core-java-jpms/decoupling-pattern1/servicemodule1/pom.xml
@@ -5,12 +5,13 @@
4.0.0
com.baeldung.servicemodule
servicemodule1
+ 1.0
jar
com.baeldung.decoupling-pattern1
decoupling-pattern1
- 1.0
+ 1.0-SNAPSHOT
@@ -18,6 +19,10 @@
org.apache.maven.plugins
maven-compiler-plugin
+
+ ${source.version}
+ ${target.version}
+
diff --git a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml
index 774319a067..13d0b2d201 100644
--- a/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml
+++ b/core-java-modules/core-java-jpms/decoupling-pattern2/consumermodule2/pom.xml
@@ -45,4 +45,4 @@
1.0
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java
index cf63a0ddda..a3ea77ec86 100644
--- a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java
+++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java
@@ -3,6 +3,9 @@ package com.baeldung.transientkw;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import java.io.File;
+
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
class TransientUnitTest {
@@ -31,5 +34,11 @@ class TransientUnitTest {
assertEquals("Fiction", book2.getBookCategory());
}
+
+ @AfterAll
+ public static void cleanup() {
+ File file = new File(BookSerDe.fileName);
+ file.deleteOnExit();
+ }
}
diff --git a/core-java-modules/core-java-lang-4/README.md b/core-java-modules/core-java-lang-4/README.md
index e1023513eb..562be950c0 100644
--- a/core-java-modules/core-java-lang-4/README.md
+++ b/core-java-modules/core-java-lang-4/README.md
@@ -6,3 +6,5 @@ This module contains articles about core features in the Java language
- [The package-info.java File](https://www.baeldung.com/java-package-info)
- [What are Compile-time Constants in Java?](https://www.baeldung.com/java-compile-time-constants)
- [Java Objects.hash() vs Objects.hashCode()](https://www.baeldung.com/java-objects-hash-vs-objects-hashcode)
+- [Referencing a Method in Javadoc Comments](https://www.baeldung.com/java-method-in-javadoc)
+- [Tiered Compilation in JVM](https://www.baeldung.com/jvm-tiered-compilation)
diff --git a/core-java-modules/core-java-lang-4/pom.xml b/core-java-modules/core-java-lang-4/pom.xml
index 4e692ffa55..824194cc07 100644
--- a/core-java-modules/core-java-lang-4/pom.xml
+++ b/core-java-modules/core-java-lang-4/pom.xml
@@ -21,6 +21,16 @@
jmh-core
${jmh-core.version}
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson.version}
+
org.openjdk.jmh
jmh-generator-annprocess
@@ -38,4 +48,5 @@
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java
new file mode 100644
index 0000000000..b26b869a60
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Animal.java
@@ -0,0 +1,12 @@
+package com.baeldung.javadocmemberreference;
+
+public class Animal {
+
+ public void run() {
+
+ }
+
+ public void run(String direction) {
+
+ }
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java
new file mode 100644
index 0000000000..a2792e0304
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/javadocmemberreference/Person.java
@@ -0,0 +1,49 @@
+package com.baeldung.javadocmemberreference;
+
+import com.baeldung.vehicle.Car;
+
+public class Person {
+
+ Person() {
+
+ }
+
+ /**
+ * Also, check the {@link #move() Move} method for more movement details.
+ */
+ public void walk() {
+
+ }
+
+ /**
+ * Check this {@link #move(String) Move} method for direction oriented movement.
+ */
+ public void move() {
+
+ }
+
+ public void move(String direction) {
+
+ }
+
+ /**
+ * Additionally, check this {@link Animal#run(String) Run} method for direction based run.
+ */
+ public void run() {
+
+ }
+
+ /**
+ * Also consider checking {@link com.baeldung.vehicle.Vehicle#Vehicle() Vehicle} constructor to initialize vehicle object.
+ */
+ public void goToWork() {
+
+ }
+
+ /**
+ * Have a look at {@link Car#getNumberOfSeats() SeatsAvailability} method for checking the available seats needed for driving.
+ */
+ public void drive() {
+
+ }
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java
new file mode 100644
index 0000000000..2eca40f055
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java
@@ -0,0 +1,21 @@
+package com.baeldung.tieredcompilation;
+
+public class Article {
+
+ private String name;
+ private String author;
+
+ public Article(String name, String author) {
+ this.name = name;
+ this.author = author;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java
new file mode 100644
index 0000000000..ee5d74d5fe
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java
@@ -0,0 +1,7 @@
+package com.baeldung.tieredcompilation;
+
+public interface Formatter {
+
+ String format(T object) throws Exception;
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java
new file mode 100644
index 0000000000..9951b477da
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java
@@ -0,0 +1,15 @@
+package com.baeldung.tieredcompilation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
+public class JsonFormatter implements Formatter {
+
+ private static final JsonMapper mapper = new JsonMapper();
+
+ @Override
+ public String format(T object) throws JsonProcessingException {
+ return mapper.writeValueAsString(object);
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java
new file mode 100644
index 0000000000..ea7178051e
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java
@@ -0,0 +1,17 @@
+package com.baeldung.tieredcompilation;
+
+public class TieredCompilation {
+
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 1_000_000; i++) {
+ Formatter formatter;
+ if (i < 500_000) {
+ formatter = new JsonFormatter();
+ } else {
+ formatter = new XmlFormatter();
+ }
+ formatter.format(new Article("Tiered Compilation in JVM", "Baeldung"));
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java
new file mode 100644
index 0000000000..8b9823cd88
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java
@@ -0,0 +1,15 @@
+package com.baeldung.tieredcompilation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+
+public class XmlFormatter implements Formatter {
+
+ private static final XmlMapper mapper = new XmlMapper();
+
+ @Override
+ public String format(T object) throws JsonProcessingException {
+ return mapper.writeValueAsString(object);
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java
new file mode 100644
index 0000000000..4bacab82d6
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Car.java
@@ -0,0 +1,14 @@
+package com.baeldung.vehicle;
+
+public class Car {
+
+ public Car() {
+
+ }
+
+ public static int getNumberOfSeats() {
+ int availableSeats = 0;
+ // available seats calculation logic
+ return availableSeats;
+ }
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java
new file mode 100644
index 0000000000..e52ae7fc5c
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/vehicle/Vehicle.java
@@ -0,0 +1,8 @@
+package com.baeldung.vehicle;
+
+public class Vehicle {
+
+ public Vehicle() {
+
+ }
+}
diff --git a/core-java-modules/core-java-lang-math-2/pom.xml b/core-java-modules/core-java-lang-math-2/pom.xml
index 098d77b59a..c825ecdef8 100644
--- a/core-java-modules/core-java-lang-math-2/pom.xml
+++ b/core-java-modules/core-java-lang-math-2/pom.xml
@@ -6,7 +6,7 @@
core-java-lang-math-2
0.0.1-SNAPSHOT
core-java-lang-math-2
-
+
com.baeldung.core-java-modules
core-java-modules
diff --git a/core-java-modules/core-java-lang-math-3/pom.xml b/core-java-modules/core-java-lang-math-3/pom.xml
index 6779d0ecc6..e1a650a18f 100644
--- a/core-java-modules/core-java-lang-math-3/pom.xml
+++ b/core-java-modules/core-java-lang-math-3/pom.xml
@@ -27,4 +27,4 @@
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-oop-constructors/README.md b/core-java-modules/core-java-lang-oop-constructors/README.md
index 0082969807..4bec8db256 100644
--- a/core-java-modules/core-java-lang-oop-constructors/README.md
+++ b/core-java-modules/core-java-lang-oop-constructors/README.md
@@ -5,4 +5,5 @@ This module contains article about constructors in Java
### Relevant Articles:
- [A Guide to Constructors in Java](https://www.baeldung.com/java-constructors)
- [Java Copy Constructor](https://www.baeldung.com/java-copy-constructor)
-- [Cannot Reference “X†Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error)
\ No newline at end of file
+- [Cannot Reference “X†Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error)
+- [Private Constructors in Java](https://www.baeldung.com/java-private-constructors)
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java
new file mode 100644
index 0000000000..1927bc4455
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Animal.java
@@ -0,0 +1,32 @@
+package com.baeldung.constructors.exception;
+
+import java.io.File;
+import java.io.IOException;
+
+public class Animal {
+
+ public Animal() throws InstantiationException {
+ throw new InstantiationException("Cannot be instantiated");
+ }
+
+ public Animal(String id, int age) {
+ if (id == null)
+ throw new NullPointerException("Id cannot be null");
+ if (age < 0)
+ throw new IllegalArgumentException("Age cannot be negative");
+ }
+
+ public Animal(File file) throws SecurityException, IOException {
+ // Avoiding Path traversal attacks
+ if (file.isAbsolute()) {
+ throw new SecurityException("Traversal attack - absolute path not allowed");
+ }
+ // Avoiding directory traversal
+ // a/../..b/
+ if (!file.getCanonicalPath()
+ .equals(file.getAbsolutePath())) {
+ throw new SecurityException("Directory traversal attempt?");
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java
new file mode 100644
index 0000000000..6e6e6558ba
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/constructors/exception/Bird.java
@@ -0,0 +1,13 @@
+package com.baeldung.constructors.exception;
+
+public class Bird extends Animal {
+
+ // Note that we are throwing parent exception
+ public Bird() throws ReflectiveOperationException {
+ super();
+ }
+
+ public Bird(String id, int age) {
+ super(id, age);
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java
new file mode 100644
index 0000000000..96785b0fb0
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/Employee.java
@@ -0,0 +1,39 @@
+package com.baeldung.privateconstructors;
+
+public class Employee {
+
+ private final String name;
+ private final int age;
+ private final String department;
+
+ private Employee(String name, int age, String department) {
+ this.name = name;
+ this.age = age;
+ this.department = department;
+ }
+
+ public static class Builder {
+ private String name;
+ private int age;
+ private String department;
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setAge(int age) {
+ this.age = age;
+ return this;
+ }
+
+ public Builder setDepartment(String department) {
+ this.department = department;
+ return this;
+ }
+
+ public Employee build() {
+ return new Employee(name, age, department);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java
new file mode 100644
index 0000000000..5361799b88
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/PrivateConstructorClass.java
@@ -0,0 +1,8 @@
+package com.baeldung.privateconstructors;
+
+public class PrivateConstructorClass {
+
+ private PrivateConstructorClass() {
+ // in the private constructor
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java
new file mode 100644
index 0000000000..a02ef37f7e
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/SingletonClass.java
@@ -0,0 +1,20 @@
+package com.baeldung.privateconstructors;
+
+public final class SingletonClass {
+
+ private static SingletonClass INSTANCE;
+ private String info = "Initial info class";
+
+ private SingletonClass() {
+ }
+
+ public static SingletonClass getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new SingletonClass();
+ }
+
+ return INSTANCE;
+ }
+
+ // getters and setters
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java
new file mode 100644
index 0000000000..b151b6f618
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/StringUtils.java
@@ -0,0 +1,16 @@
+package com.baeldung.privateconstructors;
+
+public class StringUtils {
+
+ private StringUtils() {
+ // this class cannot be instantiated
+ }
+
+ public static String toUpperCase(String s) {
+ return s.toUpperCase();
+ }
+
+ public static String toLowerCase(String s) {
+ return s.toLowerCase();
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java
new file mode 100644
index 0000000000..ff449ea9e9
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/main/java/com/baeldung/privateconstructors/ValueTypeClass.java
@@ -0,0 +1,22 @@
+package com.baeldung.privateconstructors;
+
+public class ValueTypeClass {
+
+ private final String value;
+ private final String type;
+
+ public ValueTypeClass(int x) {
+ this(Integer.toString(x), "int");
+ }
+
+ public ValueTypeClass(boolean x) {
+ this(Boolean.toString(x), "boolean");
+ }
+
+ private ValueTypeClass(String value, String type) {
+ this.value = value;
+ this.type = type;
+ }
+
+ // getters and setters
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java b/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java
new file mode 100644
index 0000000000..8f7ed440f6
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-constructors/src/test/java/com/baeldung/constructors/exception/AnimalUnitTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.constructors.exception;
+
+import java.io.File;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+
+public class AnimalUnitTest {
+
+ @Test
+ public void givenNoArgument_thenFails() {
+ Assertions.assertThatThrownBy(() -> {
+ new Animal();
+ })
+ .isInstanceOf(Exception.class);
+ }
+
+ @Test
+ public void givenInvalidArg_thenFails() {
+ Assertions.assertThatThrownBy(() -> {
+ new Animal(null, 30);
+ })
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test(expected = Test.None.class)
+ public void givenValidArg_thenSuccess() {
+ new Animal("1234", 30);
+ }
+
+ @Test
+ public void givenAbsolutePath_thenFails() {
+ Assertions.assertThatThrownBy(() -> {
+ new Animal(new File("temp.txt").getAbsoluteFile());
+ })
+ .isInstanceOf(SecurityException.class);
+ }
+
+ @Test
+ public void givenDirectoryTraversalPath_thenFails() {
+ Assertions.assertThatThrownBy(() -> {
+ new Animal(new File(File.separator + ".." + File.separator + "temp.txt"));
+ })
+ .isInstanceOf(SecurityException.class);
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/README.md b/core-java-modules/core-java-lang-oop-modifiers/README.md
index eef905fa0e..1f37cc903e 100644
--- a/core-java-modules/core-java-lang-oop-modifiers/README.md
+++ b/core-java-modules/core-java-lang-oop-modifiers/README.md
@@ -9,4 +9,5 @@ This module contains articles about modifiers in Java
- [The “final†Keyword in Java](https://www.baeldung.com/java-final)
- [A Guide to the Static Keyword in Java](https://www.baeldung.com/java-static)
- [Static and Default Methods in Interfaces in Java](https://www.baeldung.com/java-static-default-methods)
-- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp)
\ No newline at end of file
+- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp)
+- [Static Classes Versus the Singleton Pattern in Java](https://www.baeldung.com/java-static-class-vs-singleton)
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java
new file mode 100644
index 0000000000..d472e386eb
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java
@@ -0,0 +1,30 @@
+package com.baeldung.staticsingletondifference;
+
+public class CachingSingleton implements SingletonInterface {
+
+ private CachingSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final CachingSingleton instance = new CachingSingleton();
+ }
+
+ public static CachingSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ return "Caching Responsibilities";
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ return lock.takeLock(1);
+ }
+
+ @Override
+ public void increment() {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java
new file mode 100644
index 0000000000..84ea5d4d1a
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java
@@ -0,0 +1,36 @@
+package com.baeldung.staticsingletondifference;
+
+public class FileSystemSingleton implements SingletonInterface{
+
+ private int filesWritten;
+
+ private FileSystemSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final FileSystemSingleton instance = new FileSystemSingleton();
+ }
+
+ public static FileSystemSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ return "File System Responsibilities";
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ return lock.takeLock(2);
+ }
+
+ @Override
+ public void increment() {
+ this.filesWritten++;
+ }
+
+ public int getFilesWritten() {
+ return filesWritten;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java
new file mode 100644
index 0000000000..3a124bd678
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class MyLock {
+
+ protected String takeLock(int locks) {
+ return "Taken Specific Lock";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java
new file mode 100644
index 0000000000..64a422f9ec
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java
@@ -0,0 +1,49 @@
+package com.baeldung.staticsingletondifference;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+public class SerializableCloneableSingleton implements SingletonInterface, Serializable, Cloneable {
+
+ private static final long serialVersionUID = -1917003064592196223L;
+
+ private int state;
+
+ private SerializableCloneableSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final SerializableCloneableSingleton instance = new SerializableCloneableSingleton();
+ }
+
+ public static SerializableCloneableSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+
+ @Override
+ public void increment() {
+ this.state++;
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return SingletonHolder.instance;
+ }
+
+ public Object cloneObject() throws CloneNotSupportedException {
+ return this.clone();
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java
new file mode 100644
index 0000000000..34e14c5cac
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java
@@ -0,0 +1,10 @@
+package com.baeldung.staticsingletondifference;
+
+public interface SingletonInterface {
+
+ public String describeMe();
+
+ public String passOnLocks(MyLock lock);
+
+ public void increment();
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java
new file mode 100644
index 0000000000..35d9931597
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java
@@ -0,0 +1,20 @@
+package com.baeldung.staticsingletondifference;
+
+public class SingletonLock extends MyLock {
+
+ private SingletonLock() {
+ }
+
+ private static class SingletonHolder {
+ public static final SingletonLock instance = new SingletonLock();
+ }
+
+ public static SingletonLock getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String takeLock(int locks) {
+ return "Taken Singleton Lock";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java
new file mode 100644
index 0000000000..c9661cbf70
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class SubUtility extends SuperUtility {
+
+ public static String echoIt(String data) {
+ return data;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java
new file mode 100644
index 0000000000..00011f4697
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class SuperUtility {
+
+ public static String echoIt(String data) {
+ return "SUPER";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java
new file mode 100644
index 0000000000..4b0b23b5ec
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java
@@ -0,0 +1,77 @@
+package com.baeldung.staticsingletondifference;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.stream.IntStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ForSingletonsUnitTest {
+
+ @Test
+ public void whenStaticUtilClassInheritance_thenOverridingFails() {
+ SuperUtility superUtility = new SubUtility();
+ Assert.assertNotEquals("ECHO", superUtility.echoIt("ECHO"));
+ Assert.assertEquals("SUPER", superUtility.echoIt("ECHO"));
+ }
+
+ @Test
+ public void whenSingletonDerivesBaseClass_thenRuntimePolymorphism() {
+ MyLock myLock = new MyLock();
+ Assert.assertEquals("Taken Specific Lock", myLock.takeLock(10));
+ myLock = SingletonLock.getInstance();
+ Assert.assertEquals("Taken Singleton Lock", myLock.takeLock(10));
+ }
+
+ @Test
+ public void whenSingletonImplementsInterface_thenRuntimePolymorphism() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ Assert.assertEquals("File System Responsibilities", singleton.describeMe());
+ singleton = CachingSingleton.getInstance();
+ Assert.assertEquals("Caching Responsibilities", singleton.describeMe());
+ }
+
+ @Test
+ public void whenSingleton_thenPassAsArguments() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ Assert.assertEquals("Taken Singleton Lock", singleton.passOnLocks(SingletonLock.getInstance()));
+ }
+
+ @Test
+ public void whenSingleton_thenAllowState() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ IntStream.range(0, 5)
+ .forEach(i -> singleton.increment());
+ Assert.assertEquals(5, ((FileSystemSingleton) singleton).getFilesWritten());
+ }
+
+ @Test
+ public void whenSingleton_thenAllowSerializationDeserialization() {
+ SingletonInterface singleton = SerializableCloneableSingleton.getInstance();
+ singleton.increment();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ new ObjectOutputStream(baos).writeObject(singleton);
+ SerializableCloneableSingleton singletonNew = (SerializableCloneableSingleton) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject();
+ Assert.assertEquals(1, singletonNew.getState());
+ Assert.assertEquals(singleton.hashCode(), singletonNew.hashCode());
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void whenSingleton_thenAllowCloneable() {
+ SerializableCloneableSingleton singleton = SerializableCloneableSingleton.getInstance();
+ singleton.increment();
+ try {
+ Assert.assertEquals(2, ((SerializableCloneableSingleton) singleton.cloneObject()).getState());
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-operators-2/README.md b/core-java-modules/core-java-lang-operators-2/README.md
index 46c874f361..bc1809a4a7 100644
--- a/core-java-modules/core-java-lang-operators-2/README.md
+++ b/core-java-modules/core-java-lang-operators-2/README.md
@@ -3,3 +3,6 @@
This module contains articles about Java operators
## Relevant Articles:
+
+- [Logical vs Bitwise OR Operator](https://www.baeldung.com/java-logical-vs-bitwise-or-operator)
+- [Bitmasking in Java with Bitwise Operators](https://www.baeldung.com/java-bitmasking)
diff --git a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java
new file mode 100644
index 0000000000..9af59d7c1c
--- /dev/null
+++ b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java
@@ -0,0 +1,120 @@
+package com.baeldung.oroperators;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class BitmaskingUnitTest {
+
+ @Test
+ public void givenIntegerShouldPrintBinaryRepresentation() {
+ // given
+ int intRepresentation = 1094795523;
+
+ // expected
+ String stringRepresentation = getStringRepresentation(intRepresentation);
+ assertEquals(stringRepresentation, "01000001010000010100000100000011");
+ }
+
+ private String getStringRepresentation(int intRepresentation) {
+ String binaryString = Integer.toBinaryString(intRepresentation);
+ return padWithZeros(binaryString);
+ }
+
+ private String padWithZeros(String binaryString) {
+ return String.format("%" + 32 + "s", binaryString).replace(' ', '0');
+ }
+
+ @Test
+ public void givenBinaryRepresentationShouldReturnNumber() {
+ // given
+ String stringRepresentation = "01000001010000010100000100000011";
+
+ // expected
+ int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2);
+ assertEquals(intRepresentation, 1094795523);
+ }
+
+ @Test
+ public void givenBinaryRepresentationShouldReturnCharacter() {
+ // given
+ String stringRepresentation = "010000010100000101000001";
+
+ // expected
+ assertEquals(binaryToText(stringRepresentation), "AAA");
+ }
+
+ @Test
+ public void givenIntAndPositionNumberShouldReturnBitValue() {
+ // given
+ String stringRepresentation = "010000010100000101000001000000011";
+ int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2);
+
+ // when
+ boolean value1 = extractValueAtPosition(intRepresentation, 1);
+ boolean value2 = extractValueAtPosition(intRepresentation, 2);
+ boolean value3 = extractValueAtPosition(intRepresentation, 3);
+
+ // then
+ assertTrue(value1);
+ assertTrue(value2);
+ assertFalse(value3);
+
+ }
+
+ @Test
+ public void givenIntegerShouldExtractLastThreeBytes() {
+ // given
+ int intRepresentation = 1094795523;
+
+ // when
+ int lastThreeBites = intRepresentation >> 8;
+
+ // expected
+ String stringRepresentation = getStringRepresentation(lastThreeBites);
+ assertEquals(stringRepresentation, "00000000010000010100000101000001");
+ assertEquals(binaryToText(stringRepresentation), "AAA");
+ }
+
+ @Test
+ public void givenIntegerShouldApplyMask() {
+ // given
+ int intRepresentation = Integer.parseUnsignedInt("00000000010000010100000101000001", 2);
+ int mask = Integer.parseUnsignedInt("00000000000000000000000000000011", 2);
+ int mask2 = Integer.parseUnsignedInt("00000000000000000000000000000001", 2);
+
+ // when
+ int masked = intRepresentation & mask;
+ int masked2 = intRepresentation & mask2;
+
+ // expected
+ assertEquals(getStringRepresentation(masked), "00000000000000000000000000000001");
+ assertEquals(getStringRepresentation(masked2), "00000000000000000000000000000001");
+ assertNotEquals(masked, mask);
+ assertEquals(masked2, mask2);
+ assertFalse((intRepresentation & mask) == mask);
+ }
+
+ private boolean extractValueAtPosition(int intRepresentation, int position) {
+ String mask = getStringRepresentation(1 << (position - 1));
+ System.out.println(getStringRepresentation(intRepresentation));
+ System.out.println(mask);
+ System.out.println("-------------------------------- &");
+ System.out.println(getStringRepresentation((intRepresentation) & (1 << (position - 1))));
+ System.out.println();
+ return ((intRepresentation) & (1 << (position - 1))) != 0;
+ }
+
+ private static String binaryToText(String stringRepresentation) {
+ return Arrays.stream(stringRepresentation.split("(?<=\\G.{8})"))
+ .filter(eightBits -> !eightBits.equals("00000000"))
+ .map(eightBits -> (char)Integer.parseInt(eightBits, 2))
+ .collect(
+ StringBuilder::new,
+ StringBuilder::append,
+ StringBuilder::append
+ ).toString();
+ }
+}
diff --git a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java
index c04638991d..8b95d19753 100644
--- a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java
+++ b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitwiseAndLogicalOROperatorUnitTest.java
@@ -1,4 +1,110 @@
package com.baeldung.oroperators;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
public class BitwiseAndLogicalOROperatorUnitTest {
+
+ @Test
+ public void givenTwoTrueBooleans_whenLogicalOrOperator_thenTrue() {
+ boolean condition1 = true;
+ boolean condition2 = true;
+ boolean result = condition1 || condition2;
+ assertTrue(result);
+ }
+
+ @Test
+ public void givenOneFalseAndOneTrueBooleans_whenLogicalOrOperator_thenTrue() {
+ boolean condition1 = true;
+ boolean condition2 = false;
+ boolean result = condition1 || condition2;
+ assertTrue(result);
+ }
+
+ @Test
+ public void givenTwoFalseBooleans_whenLogicalOrOperator_thenFalse() {
+ boolean condition1 = false;
+ boolean condition2 = false;
+ boolean result = condition1 || condition2;
+ assertFalse(result);
+ }
+
+ @Test
+ public void givenTwoExpressions_whenLogicalOrOperator_TrueOrFalse_thenShortCircuitTrue() {
+
+ boolean shortCircuitResult = returnAndLog(true) || returnAndLog(false);
+ assertTrue(shortCircuitResult);
+ }
+
+ @Test
+ public void givenTwoExpressions_whenLogicalOrOperator_FalseOrTrue_thenShortCircuitTrue() {
+
+ boolean shortCircuitResult = returnAndLog(false) || returnAndLog(true);
+ assertTrue(shortCircuitResult);
+ }
+
+ @Test
+ public void givenTwoTrueBooleans_whenBitwiseOrOperator_thenTrue() {
+ boolean condition1 = true;
+ boolean condition2 = true;
+ boolean result = condition1 | condition2;
+ assertTrue(result);
+ }
+
+ @Test
+ public void givenOneFalseAndOneTrueBooleans_whenBitwiseOrOperator_thenFalse() {
+ boolean condition1 = true;
+ boolean condition2 = false;
+ boolean result = condition1 | condition2;
+ assertTrue(result);
+ }
+
+ @Test
+ public void givenTwoFalseBooleans_whenBitwiseOrOperator_thenFalse() {
+ boolean condition1 = false;
+ boolean condition2 = false;
+ boolean result = condition1 | condition2;
+ assertFalse(result);
+ }
+
+ @Test
+ public void givenTwoIntegers_whenBitwiseOrOperator_thenNewInteger() {
+ int four = 4; //0100
+ int three = 3; //0011
+ int fourORthree = four | three;
+ assertEquals(7, fourORthree);
+ }
+
+ @Test
+ public void givenFourIntegers_whenBitwiseOrOperator_thenNewInteger() {
+ int result = 1 | 2 | 3 | 4;
+ assertEquals(7, result);
+ }
+
+ @Test
+ public void givenExpression_whenLogicalORAndOtherOperators_thenEvaluateByPrecedence() {
+
+ boolean result = 2 + 4 == 5 || 3 < 5;
+ assertTrue(result);
+ }
+
+ @Test
+ public void givenExpression_whenBitwiseORAndOtherOperators_thenEvaluateByPrecedence() {
+
+ int result = 1 + 2 | 5 - 1;
+ assertEquals(7, result);
+ }
+
+ @Test
+ public void givenExpression_multipleLogicalOr_thenEvaluateByLeftToRightAssociativity() {
+
+ boolean result = true || false || false;
+ assertTrue(result);
+ }
+
+ boolean returnAndLog(boolean value) {
+ System.out.println("Returning " + value);
+ return value;
+ }
}
diff --git a/core-java-modules/core-java-lang-syntax-2/pom.xml b/core-java-modules/core-java-lang-syntax-2/pom.xml
index b725089351..9ca06e8fba 100644
--- a/core-java-modules/core-java-lang-syntax-2/pom.xml
+++ b/core-java-modules/core-java-lang-syntax-2/pom.xml
@@ -25,4 +25,4 @@
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang/pom.xml b/core-java-modules/core-java-lang/pom.xml
index 2a1a7a05b8..6a14cab242 100644
--- a/core-java-modules/core-java-lang/pom.xml
+++ b/core-java-modules/core-java-lang/pom.xml
@@ -25,4 +25,4 @@
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-networking-3/README.md b/core-java-modules/core-java-networking-3/README.md
index 730231525f..2e76ab5d51 100644
--- a/core-java-modules/core-java-networking-3/README.md
+++ b/core-java-modules/core-java-networking-3/README.md
@@ -6,4 +6,6 @@ This module contains articles about networking in Java
- [Finding a Free Port in Java](https://www.baeldung.com/java-free-port)
- [Downloading Email Attachments in Java](https://www.baeldung.com/java-download-email-attachments)
+- [Connection Timeout vs. Read Timeout for Java Sockets](https://www.baeldung.com/java-socket-connection-read-timeout)
+- [Find Whether an IP Address Is in the Specified Range or Not in Java](https://www.baeldung.com/java-check-ip-address-range)
- [[<-- Prev]](/core-java-modules/core-java-networking-2)
diff --git a/core-java-modules/core-java-networking-3/pom.xml b/core-java-modules/core-java-networking-3/pom.xml
index 0ad800e173..d3b2398b75 100644
--- a/core-java-modules/core-java-networking-3/pom.xml
+++ b/core-java-modules/core-java-networking-3/pom.xml
@@ -47,6 +47,24 @@
javax.mail
1.6.2
+
+
+ com.github.seancfoley
+ ipaddress
+ ${seancfoley.ipaddress.version}
+
+
+
+ com.github.jgonian
+ commons-ip-math
+ ${jgonian.commons-ip-math.version}
+
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ ${googlecode.ipv6.version}
+
@@ -66,6 +84,9 @@
9.4.31.v20200723
10.0.0-M7
3.11.1
+ 5.3.3
+ 1.32
+ 0.17
\ No newline at end of file
diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java
new file mode 100644
index 0000000000..634c88ccba
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java
@@ -0,0 +1,81 @@
+package com.baeldung.ipingivenrange;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressSeqRange;
+import inet.ipaddr.IPAddressString;
+import inet.ipaddr.AddressStringException;
+
+import com.github.jgonian.ipmath.Ipv4;
+import com.github.jgonian.ipmath.Ipv4Range;
+import com.github.jgonian.ipmath.Ipv6;
+import com.github.jgonian.ipmath.Ipv6Range;
+import com.googlecode.ipv6.IPv6Address;
+import com.googlecode.ipv6.IPv6AddressRange;
+
+public class IPWithGivenRangeCheck {
+
+ // using IPAddress library
+ public static boolean checkIPIsInGivenRange(String inputIP, String rangeStartIP, String rangeEndIP) throws AddressStringException {
+ IPAddress startIPAddress = new IPAddressString(rangeStartIP).getAddress();
+ IPAddress endIPAddress = new IPAddressString(rangeEndIP).getAddress();
+ IPAddressSeqRange ipRange = startIPAddress.toSequentialRange(endIPAddress);
+ IPAddress inputIPAddress = new IPAddressString(inputIP).toAddress();
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // using Commons IP Math library for IPv4
+ public static boolean checkIPv4IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) {
+ Ipv4 startIPAddress = Ipv4.of(rangeStartIP);
+ Ipv4 endIPAddress = Ipv4.of(rangeEndIP);
+ Ipv4Range ipRange = Ipv4Range.from(startIPAddress)
+ .to(endIPAddress);
+ Ipv4 inputIPAddress = Ipv4.of(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // using Commons IP Math library for IPv6
+ public static boolean checkIPv6IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) {
+ Ipv6 startIPAddress = Ipv6.of(rangeStartIP);
+ Ipv6 endIPAddress = Ipv6.of(rangeEndIP);
+ Ipv6Range ipRange = Ipv6Range.from(startIPAddress)
+ .to(endIPAddress);
+ Ipv6 inputIPAddress = Ipv6.of(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // checking IP is in range by converting it to an integer
+ public static boolean checkIPv4IsInRangeByConvertingToInt(String inputIP, String rangeStartIP, String rangeEndIP) throws UnknownHostException {
+ long startIPAddress = ipToLongInt(InetAddress.getByName(rangeStartIP));
+ long endIPAddress = ipToLongInt(InetAddress.getByName(rangeEndIP));
+ long inputIPAddress = ipToLongInt(InetAddress.getByName(inputIP));
+
+ return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
+ }
+
+ private static long ipToLongInt(InetAddress ipAddress) {
+ long resultIP = 0;
+ byte[] ipAddressOctets = ipAddress.getAddress();
+
+ for (byte octet : ipAddressOctets) {
+ resultIP <<= 8;
+ resultIP |= octet & 0xFF;
+ }
+ return resultIP;
+ }
+
+ // using Java IPv6 library (which internally uses two long integers to store ip address)
+ public static boolean checkIPv6IsInRangeByIPv6library(String inputIP, String rangeStartIP, String rangeEndIP) {
+ IPv6Address startIPAddress = IPv6Address.fromString(rangeStartIP);
+ IPv6Address endIPAddress = IPv6Address.fromString(rangeEndIP);
+ IPv6AddressRange ipRange = IPv6AddressRange.fromFirstAndLast(startIPAddress, endIPAddress);
+ IPv6Address inputIPAddress = IPv6Address.fromString(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+}
diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java
new file mode 100644
index 0000000000..a482db3543
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpClientSocket.java
@@ -0,0 +1,46 @@
+package com.baeldung.timeout;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+public class TcpClientSocket {
+
+ private Socket socket;
+ private PrintStream out;
+ private BufferedReader in;
+
+ public void connect(String host, int port) {
+ try {
+ socket = new Socket(host, port);
+ socket.setSoTimeout(30000);
+ System.out.println("connected to " + host + " on port " + port);
+
+ out = new PrintStream(socket.getOutputStream(), true);
+ System.out.println("Sending message ... ");
+ out.println("Hello world");
+
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ out.println(in.readLine());
+
+ System.out.println("Closing connection !!! ");
+ in.close();
+ out.close();
+ socket.close();
+
+ } catch (UnknownHostException e) {
+ System.err.println(e);
+ } catch (IOException e) {
+ System.err.println(e);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ TcpClientSocket client = new TcpClientSocket();
+ client.connect("127.0.0.1", 5000);
+ }
+
+}
diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java
new file mode 100644
index 0000000000..452aaac191
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/timeout/TcpServerSocket.java
@@ -0,0 +1,43 @@
+package com.baeldung.timeout;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class TcpServerSocket {
+
+ private Socket socket;
+ private ServerSocket serverSocket;
+ private BufferedReader in;
+
+ public TcpServerSocket(int port) {
+
+ try {
+ serverSocket = new ServerSocket(port);
+ System.out.println("Server is listening on port :: " + port);
+ System.out.println("Waiting for a client ...");
+
+ socket = serverSocket.accept();
+ socket.setSoTimeout(40000);
+ System.out.println("Client connected !! ");
+
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ String line = in.readLine();
+ System.out.println(line);
+
+ System.out.println("Closing connection !!! ");
+
+ socket.close();
+ in.close();
+ } catch (IOException i) {
+ System.out.println(i);
+ }
+ }
+
+ public static void main(String args[]) {
+ TcpServerSocket server = new TcpServerSocket(5000);
+ }
+}
diff --git a/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java
new file mode 100644
index 0000000000..a9b54834d5
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.ipingivenrange;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.ipingivenrange.IPWithGivenRangeCheck;
+
+class IPWithGivenRangeCheckUnitTest {
+
+ @Test
+ void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
+ // test for IPAddress library
+ assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for Common IP Math library
+ assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for IPv4 by converting it to an integer and checking if it falls under the specified range.
+ assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+ }
+
+ @Test
+ void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
+ // test for IPAddress library
+ assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for Common IP Math library
+ assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for IPv4 by converting it to an integer and checking if it falls under the specified range.
+ assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+ }
+
+ @Test
+ void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
+ // test for IPAddress library
+ assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Common IP Math library
+ assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Java IPv6 library
+ assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("fe80::226:2dff:fefa:dcba", "fe80::226:2dff:fefa:cd1f", "fe80::226:2dff:fefa:ffff"));
+ }
+
+ @Test
+ void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
+ // test for IPAddress library
+ assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Common IP Math library
+ assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Java IPv6 library
+ assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+ }
+}
diff --git a/core-java-modules/core-java-nio-2/README.md b/core-java-modules/core-java-nio-2/README.md
index ab54899501..52efa0330a 100644
--- a/core-java-modules/core-java-nio-2/README.md
+++ b/core-java-modules/core-java-nio-2/README.md
@@ -10,4 +10,5 @@ This module contains articles about core Java non-blocking input and output (IO)
- [Using Java MappedByteBuffer](https://www.baeldung.com/java-mapped-byte-buffer)
- [How to Lock a File in Java](https://www.baeldung.com/java-lock-files)
- [Java NIO DatagramChannel](https://www.baeldung.com/java-nio-datagramchannel)
+- [Java – Path vs File](https://www.baeldung.com/java-path-vs-file)
- [[<-- Prev]](/core-java-modules/core-java-nio)
diff --git a/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java
new file mode 100644
index 0000000000..7c1e910db8
--- /dev/null
+++ b/core-java-modules/core-java-nio-2/src/test/java/com/baeldung/pathvsfile/PathVsFileUnitTest.java
@@ -0,0 +1,98 @@
+package com.baeldung.pathvsfile;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.catchThrowable;
+
+public class PathVsFileUnitTest {
+
+ @Test
+ public void whenCreateFileAndPath_thenShouldPointTheSame() {
+ File file = new File("baeldung/tutorial.txt");
+ Path path = Paths.get("baeldung/tutorial.txt");
+
+ assertThat(file.toString())
+ .isEqualTo(path.toString());
+ assertThat(file.toPath())
+ .isEqualTo(path);
+ assertThat(path.toFile())
+ .isEqualTo(file);
+ assertThat(file)
+ .isEqualTo(new File("baeldung", "tutorial.txt"));
+ assertThat(path)
+ .isEqualTo(Paths.get("baeldung", "tutorial.txt" ))
+ .isEqualTo(Paths.get("baeldung").resolve("tutorial.txt"));
+ }
+
+ @Test
+ public void givenNoFile_whenDelete_thenReportError() {
+ File file = new File("tutorial.txt");
+ Path path = Paths.get("tutorial.txt");
+
+ assertThat(file.delete()).isFalse();
+ assertThatThrownBy(() -> Files.delete(path))
+ .isInstanceOf(IOException.class)
+ .isInstanceOf(NoSuchFileException.class);
+ }
+
+ @Test
+ public void givenExistedFile_whenReadMetadata_thenShouldBeSame() throws IOException {
+ File file = new File("tutorial.txt");
+ Path path = Paths.get("tutorial.txt");
+
+ Throwable throwable = catchThrowable(() -> Files.createFile(path));
+ assertThat(throwable)
+ .isNull();
+
+ assertThat(file.exists())
+ .isEqualTo(Files.exists(path));
+ assertThat(file.isFile())
+ .isEqualTo(Files.isRegularFile(path));
+ assertThat(file.isDirectory())
+ .isEqualTo(Files.isDirectory(path));
+ assertThat(file.canRead())
+ .isEqualTo(Files.isReadable(path));
+ assertThat(file.canWrite())
+ .isEqualTo(Files.isWritable(path));
+ assertThat(file.canExecute())
+ .isEqualTo(Files.isExecutable(path));
+ assertThat(file.isHidden())
+ .isEqualTo(Files.isHidden(path));
+ }
+
+ @Test
+ public void givenExistedFile_whenUriPathOperations_thenShouldBeSame() throws IOException {
+ File file = new File("tutorial.txt");
+ Path path = Paths.get("tutorial.txt");
+
+ Throwable throwable = catchThrowable(() -> Files.createFile(path));
+ assertThat(throwable)
+ .isNull();
+
+ assertThat(file.toURI())
+ .isEqualTo(path.toUri());
+ assertThat(file.getAbsolutePath())
+ .isEqualTo(path.toAbsolutePath().toString());
+ assertThat(file.getCanonicalPath())
+ .isEqualTo(path.toRealPath().toString())
+ .isEqualTo(path.toRealPath().normalize().toString());
+ }
+
+ @AfterEach
+ public void afterEach() throws IOException {
+ Path path = Paths.get("tutorial.txt");
+ if (Files.exists(path)) {
+ Files.delete(path);
+ }
+ }
+}
diff --git a/core-java-modules/core-java-perf/pom.xml b/core-java-modules/core-java-perf/pom.xml
index bc82e92e34..636de00d8f 100644
--- a/core-java-modules/core-java-perf/pom.xml
+++ b/core-java-modules/core-java-perf/pom.xml
@@ -15,4 +15,4 @@
../
-
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-reflection-2/pom.xml b/core-java-modules/core-java-reflection-2/pom.xml
index 347f986275..5b04b8e8db 100644
--- a/core-java-modules/core-java-reflection-2/pom.xml
+++ b/core-java-modules/core-java-reflection-2/pom.xml
@@ -62,4 +62,5 @@
1.8
5.3.4
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-regex-2/README.md b/core-java-modules/core-java-regex-2/README.md
new file mode 100644
index 0000000000..25656c3cb1
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/README.md
@@ -0,0 +1,5 @@
+### Relevant Articles:
+
+- [Non-Capturing Regex Groups in Java](https://www.baeldung.com/java-regex-non-capturing-groups)
+- [Lookahead and Lookbehind in Java Regex](https://www.baeldung.com/java-regex-lookahead-lookbehind)
+- [Converting Camel Case and Title Case to Words in Java](https://www.baeldung.com/java-camel-case-title-case-to-words)
diff --git a/core-java-modules/core-java-regex-2/pom.xml b/core-java-modules/core-java-regex-2/pom.xml
new file mode 100644
index 0000000000..9e081c2228
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+ core-java-regex-2
+ 0.1.0-SNAPSHOT
+ core-java-regex-2
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj-core.version}
+ test
+
+
+
+
+ 3.15.0
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java
new file mode 100644
index 0000000000..ea6593495f
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java
@@ -0,0 +1,27 @@
+package com.baeldung.regex.camelcasetowords;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Convert a string in camelCase or TitleCase into a list of words
+ */
+public class CamelCaseToWords {
+ private static final Pattern WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))");
+
+ /**
+ * Find the words in mixed case string like ThisIsText or HereIsSomeText
+ * @param text the text to parse
+ * @return the list of words to process
+ */
+ public static List findWordsInMixedCase(String text) {
+ Matcher matcher = WORD_FINDER.matcher(text);
+ List words = new ArrayList<>();
+ while (matcher.find()) {
+ words.add(matcher.group(0));
+ }
+ return words;
+ }
+}
diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java
new file mode 100644
index 0000000000..f87e59c6cc
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java
@@ -0,0 +1,43 @@
+package com.baeldung.regex.camelcasetowords;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class Recapitalize {
+ private static final Set STOP_WORDS = Stream.of("a", "an", "the", "and",
+ "but", "for", "at", "by", "to", "or")
+ .collect(Collectors.toSet());
+
+ public static String sentenceCase(List words) {
+ List capitalized = new ArrayList<>();
+ for (int i = 0; i < words.size(); i++) {
+ String currentWord = words.get(i);
+ if (i == 0) {
+ capitalized.add(capitalizeFirst(currentWord));
+ } else {
+ capitalized.add(currentWord.toLowerCase());
+ }
+ }
+ return String.join(" ", capitalized) + ".";
+ }
+
+ public static String capitalizeMyTitle(List words) {
+ List capitalized = new ArrayList<>();
+ for (int i = 0; i < words.size(); i++) {
+ String currentWord = words.get(i);
+ if (i == 0 || !STOP_WORDS.contains(currentWord.toLowerCase())) {
+ capitalized.add(capitalizeFirst(currentWord));
+ } else {
+ capitalized.add(currentWord.toLowerCase());
+ }
+ }
+ return String.join(" ", capitalized);
+ }
+
+ private static String capitalizeFirst(String word) {
+ return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
+ }
+}
diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java
new file mode 100644
index 0000000000..a843945bba
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.regex.camelcasetowords;
+
+import org.junit.jupiter.api.Test;
+
+import static com.baeldung.regex.camelcasetowords.CamelCaseToWords.findWordsInMixedCase;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CamelCaseToWordsUnitTest {
+
+ @Test
+ void givenPlainStringWithNonLetters_thenFindsWords() {
+ assertThat(findWordsInMixedCase("some words"))
+ .containsExactly("some", "words");
+ }
+
+ @Test
+ void givenWordsInCamelCase_thenFindsWords() {
+ assertThat(findWordsInMixedCase("thisIsCamelCaseText"))
+ .containsExactly("this", "Is", "Camel", "Case", "Text");
+ }
+
+ @Test
+ void givenWordsInTitleCase_thenFindsWords() {
+ assertThat(findWordsInMixedCase("ThisIsTitleCaseText"))
+ .containsExactly("This", "Is", "Title", "Case", "Text");
+ }
+
+ @Test
+ void givenWordsAcrossMultipleTexts_thenFindsWords() {
+ assertThat(findWordsInMixedCase("ThisIsTitleCaseText --- andSoIsThis"))
+ .containsExactly("This", "Is", "Title", "Case", "Text", "and", "So", "Is", "This");
+ }
+
+ @Test
+ void givenCamelCaseHasASingleLetterWord_thenItCanBeSplit() {
+ assertThat(findWordsInMixedCase("thisHasASingleLetterWord"))
+ .containsExactly("this", "Has", "A", "Single", "Letter", "Word");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java
new file mode 100644
index 0000000000..32c8f39372
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java
@@ -0,0 +1,35 @@
+package com.baeldung.regex.camelcasetowords;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static com.baeldung.regex.camelcasetowords.Recapitalize.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RecapitalizeUnitTest {
+
+ @Test
+ void givenWords_thenCanComposeSentence() {
+ assertThat(sentenceCase(Arrays.asList("these", "Words", "Form", "A", "Sentence")))
+ .isEqualTo("These words form a sentence.");
+ }
+
+ @Test
+ void givenNonStopWords_thenTitleIsComposed() {
+ assertThat(capitalizeMyTitle(Arrays.asList("title", "words", "capitalize")))
+ .isEqualTo("Title Words Capitalize");
+ }
+
+ @Test
+ void givenStopWords_thenTitleHasThemInLowerCase() {
+ assertThat(capitalizeMyTitle(Arrays.asList("this", "is", "A", "title", "with", "a", "stop", "word", "or", "two")))
+ .isEqualTo("This Is a Title With a Stop Word or Two");
+ }
+
+ @Test
+ void givenStopWordIsFirstWord_thenTitleHasItCapitalized() {
+ assertThat(capitalizeMyTitle(Arrays.asList("a", "stop", "word", "first")))
+ .isEqualTo("A Stop Word First");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java
new file mode 100644
index 0000000000..eab3793931
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/lookaround/LookaroundUnitTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.regex.lookaround;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.jupiter.api.Test;
+
+public class LookaroundUnitTest {
+
+ @Test
+ void givenPositiveLookahead_thenSuccess() {
+ Pattern pattern = Pattern.compile("import (?=static.+).+");
+
+ Matcher matcher = pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;");
+ assertTrue(matcher.find());
+ assertEquals("import static org.junit.jupiter.api.Assertions.assertEquals;", matcher.group());
+
+ assertFalse(pattern.matcher("import java.util.regex.Matcher;")
+ .find());
+ }
+
+ @Test
+ void givenNegativeLookahead_thenSuccess() {
+ Pattern pattern = Pattern.compile("import (?!static.+).+");
+
+ Matcher matcher = pattern.matcher("import java.util.regex.Matcher;");
+ assertTrue(matcher.find());
+ assertEquals("import java.util.regex.Matcher;", matcher.group());
+
+ assertFalse(pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;")
+ .find());
+ }
+
+ @Test
+ void givenPositiveLookbehind_thenSuccess() {
+ Pattern pattern = Pattern.compile(".*(?<=jupiter).*assertEquals;");
+
+ Matcher matcher = pattern.matcher("import static org.junit.jupiter.api.Assertions.assertEquals;");
+ assertTrue(matcher.find());
+ assertEquals("import static org.junit.jupiter.api.Assertions.assertEquals;", matcher.group());
+
+ assertFalse(pattern.matcher("import static org.junit.Assert.assertEquals;")
+ .find());
+ }
+
+ @Test
+ void givenNegativeLookbehind_thenSuccess() {
+ Pattern pattern = Pattern.compile(".*(?[.a-z]+/?)+/ending-path");
+ private static final Pattern INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH_AND_BACKTRACKING = Pattern.compile("[^:]+://(?>(?:[.a-z]+/?)+/)ending-path");
+
+ @Test
+ void givenSimpleUrlPattern_whenValidUrlProvided_thenMatches() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ Assertions.assertThatThrownBy(() -> urlMatcher.group(1)).isInstanceOf(IndexOutOfBoundsException.class);
+ }
+
+ @Test
+ void whenSimpleUrlProvidedWithPathProvided_thenMatches() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/live");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void whenSimpleUrlProvidedWithPathEndingWithSlashProvided_thenMatches() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/live/");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenSimpleUrlPattern_whenUrlWithMultiplePathSegmentsProvided_thenMatches() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.microsoft.com/some/other/url/path");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void whenUrlWithUppercaseCharactersProvided_thenDoesNotMatch() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN.matcher("http://www.Microsoft.com/");
+
+ Assertions.assertThat(urlMatcher.matches()).isFalse();
+ }
+
+ @Test
+ void givenPatternWithCaseInsensitiveGroup_whenUrlHasUppercaseCharactersInsideOfScope_thenMatches() {
+ Matcher urlMatcher = SCOPED_CASE_INSENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenCaseInsensitivePattern_whenUrlHasUppercaseCharacters_thenMatches() {
+ Matcher urlMatcher = CASE_INSENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/Ending-path");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ Assertions.assertThat(urlMatcher.group(1)).isEqualTo("/Ending-path");
+ }
+
+ @Test
+ void givenPatternWithCaseInsensitiveGroup_whenUrlHasUppercaseCharactersOutsideOfScope_thenMatchFails() {
+ Matcher urlMatcher = SCOPED_CASE_INSENSTIIVE_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.Microsoft.com/Ending-path");
+
+ Assertions.assertThat(urlMatcher.matches()).isFalse();
+ }
+
+ @Test
+ void givenPatternAllowingBacktracking_whenUrlWithEndingPathCausingBacktrackingProvided_thenMatches() {
+ Matcher urlMatcher = SIMPLE_URL_PATTERN_WITH_SPECIFIC_ENDING_PATH.matcher("http://www.microsoft.com/ending-path");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingOccurs_thenDoesNotMatch() {
+ Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.microsoft.com/ending-path");
+
+ Assertions.assertThat(independentMatcher.matches()).isFalse();
+ }
+
+ @Test
+ void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingDoesNotOccur_thenMatches() {
+ Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH.matcher("http://www.microsoft.com//ending-path");
+
+ Assertions.assertThat(independentMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenPatternWithIndependentNonCapturingGroup_whenBacktrackingOccursInsideGroup_thenMatches() {
+ Matcher independentMatcher = INDEPENDENT_URL_PATTERN_WITH_ENDING_PATH_AND_BACKTRACKING.matcher("http://www.microsoft.com/ending-path");
+
+ Assertions.assertThat(independentMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenCaseInsensitivePatternWithCaseSensitivieSubPattern_whenUrlWithUppercaseCharactersOutsideOfScopeProvided_thenMatches() {
+ Matcher urlMatcher = SCOPED_CASE_SENSITIVE_URL_PATTERN.matcher("http://www.microsoft.com/ENDING-PATH");
+
+ Assertions.assertThat(urlMatcher.matches()).isTrue();
+ }
+
+ @Test
+ void givenCaseInsensitivePatternWithCaseSensitivieSubPattern_whenUrlWithUppercaseCharactersInsideOfScopeProvided_thenDoesNotMatch() {
+ Matcher urlMatcher = SCOPED_CASE_SENSITIVE_URL_PATTERN.matcher("http://www.Microsoft.com/ending-path");
+
+ Assertions.assertThat(urlMatcher.matches()).isFalse();
+ }
+}
diff --git a/core-java-modules/core-java-regex/README.md b/core-java-modules/core-java-regex/README.md
index bc28f4b732..4c78f64d75 100644
--- a/core-java-modules/core-java-regex/README.md
+++ b/core-java-modules/core-java-regex/README.md
@@ -14,3 +14,4 @@
- [Validate Phone Numbers With Java Regex](https://www.baeldung.com/java-regex-validate-phone-numbers)
- [How to Count the Number of Matches for a Regex?](https://www.baeldung.com/java-count-regex-matches)
- [Find All Numbers in a String in Java](https://www.baeldung.com/java-find-numbers-in-string)
+- [Understanding the Pattern.quote Method](https://www.baeldung.com/java-pattern-quote)
diff --git a/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java b/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java
index 921876c0d5..8c07282716 100644
--- a/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java
+++ b/core-java-modules/core-java-regex/src/test/java/com/baeldung/ignore/pattern/metacharacters/IgnoringPatternMetacharactersUnitTest.java
@@ -12,7 +12,7 @@ public class IgnoringPatternMetacharactersUnitTest {
private static final String patternStr = "$100.50";
@Test
- public void givenPatternStringHasMetacharacters_whenPatternMatchedWithoutEscapingMetacharacters_thenNoMatchesFound() {
+ public void whenMetacharactersNotEscaped_thenNoMatchesFound() {
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(dollarAmounts);
@@ -25,7 +25,7 @@ public class IgnoringPatternMetacharactersUnitTest {
}
@Test
- public void givenPatternStringHasMetacharacters_whenPatternCompiledUsingManuallyMetaEscapedPattern_thenMatchingSuccessful() {
+ public void whenMetacharactersManuallyEscaped_thenMatchingSuccessful() {
String metaEscapedPatternStr = "\\Q" + patternStr + "\\E";
Pattern pattern = Pattern.compile(metaEscapedPatternStr);
Matcher matcher = pattern.matcher(dollarAmounts);
@@ -39,7 +39,7 @@ public class IgnoringPatternMetacharactersUnitTest {
}
@Test
- public void givenPatternStringHasMetacharacters_whenPatternCompiledUsingLiteralPatternFromQuote_thenMatchingSuccessful() {
+ public void whenMetacharactersEscapedUsingPatternQuote_thenMatchingSuccessful() {
String literalPatternStr = Pattern.quote(patternStr);
Pattern pattern = Pattern.compile(literalPatternStr);
Matcher matcher = pattern.matcher(dollarAmounts);
diff --git a/core-java-modules/core-java-security-2/README.md b/core-java-modules/core-java-security-2/README.md
index 684f2504cc..680a7de925 100644
--- a/core-java-modules/core-java-security-2/README.md
+++ b/core-java-modules/core-java-security-2/README.md
@@ -17,4 +17,5 @@ This module contains articles about core Java Security
- [InvalidAlgorithmParameterException: Wrong IV Length](https://www.baeldung.com/java-invalidalgorithmparameter-exception)
- [The java.security.egd JVM Option](https://www.baeldung.com/java-security-egd)
- [RSA in Java](https://www.baeldung.com/java-rsa)
+- [3DES in Java](https://www.baeldung.com/java-3des)
- More articles: [[<-- prev]](/core-java-modules/core-java-security)
diff --git a/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java
new file mode 100644
index 0000000000..6e5df8e619
--- /dev/null
+++ b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java
@@ -0,0 +1,90 @@
+package com.baeldung.des;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class TripleDESUnitTest {
+
+ @Test
+ public void given3DesKey_whenEncryptAndDecryptString_thenCompareResults() throws Exception {
+ byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes();
+ byte[] iv = "a76nb5h9".getBytes();
+
+ String secretMessage = "Baeldung secret message";
+
+ SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede");
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] secretMessagesBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
+ byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessagesBytes);
+
+ Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
+ String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
+
+ Assertions.assertEquals(secretMessage, decryptedMessage);
+ }
+
+ @Test
+ public void given3DesKey_whenEncryptAndDecryptFile_thenCompareResults() throws Exception {
+ byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes();
+ byte[] iv = "a76nb5h9".getBytes();
+
+ SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede");
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+
+ String originalContent = "some secret message";
+ Path tempFile = Files.createTempFile("temp", "txt");
+ writeString(tempFile, originalContent);
+
+ byte[] fileBytes = Files.readAllBytes(tempFile);
+ Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes);
+ try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {
+ stream.write(encryptedFileBytes);
+ }
+
+ encryptedFileBytes = Files.readAllBytes(tempFile);
+ Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes);
+ try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {
+ stream.write(decryptedFileBytes);
+ }
+
+ String fileContent = readString(tempFile);
+
+ Assertions.assertEquals(originalContent, fileContent);
+ }
+
+ private void writeString(Path path, String content) throws Exception {
+ try (BufferedWriter writer = Files.newBufferedWriter(path)) {
+ writer.write(content);
+ }
+ }
+
+ private String readString(Path path) throws Exception {
+ StringBuilder resultStringBuilder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ resultStringBuilder.append(line);
+ }
+ }
+ return resultStringBuilder.toString();
+ }
+}
diff --git a/core-java-modules/core-java-security-3/pom.xml b/core-java-modules/core-java-security-3/pom.xml
index 2520cee7f8..69d0ffb917 100644
--- a/core-java-modules/core-java-security-3/pom.xml
+++ b/core-java-modules/core-java-security-3/pom.xml
@@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- core-java-security-2
+ core-java-security-3
0.1.0-SNAPSHOT
- core-java-security-2
+ core-java-security-3
jar
diff --git a/core-java-modules/core-java-streams-4/.gitignore b/core-java-modules/core-java-streams-4/.gitignore
new file mode 100644
index 0000000000..3de4cc647e
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/.gitignore
@@ -0,0 +1,26 @@
+*.class
+
+0.*
+
+#folders#
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+.resourceCache
+
+# Packaged files #
+*.jar
+*.war
+*.ear
+
+# Files generated by integration tests
+*.txt
+backup-pom.xml
+/bin/
+/temp
+
+#IntelliJ specific
+.idea/
+*.iml
\ No newline at end of file
diff --git a/core-java-modules/core-java-streams-4/README.md b/core-java-modules/core-java-streams-4/README.md
new file mode 100644
index 0000000000..6eeee943aa
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Count Occurrences Using Java groupingBy Collector](https://www.baeldung.com/java-groupingby-count)
diff --git a/core-java-modules/core-java-streams-4/pom.xml b/core-java-modules/core-java-streams-4/pom.xml
new file mode 100644
index 0000000000..8cb3786776
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/pom.xml
@@ -0,0 +1,62 @@
+
+
+ 4.0.0
+ core-java-streams-4
+ 0.1.0-SNAPSHOT
+ core-java-streams-4
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
+
+
+
+
+ log4j
+ log4j
+ ${log4j.version}
+
+
+ org.junit
+ junit-bom
+ ${junit-jupiter.version}
+ pom
+ import
+
+
+
+
+ core-java-streams-4
+
+
+ src/main
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ -parameters
+
+
+
+
+
+
+
+ 3.1
+ 1.8
+ 1.8
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java
new file mode 100644
index 0000000000..c50d73a638
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java
@@ -0,0 +1,84 @@
+package com.baeldung.streamcollectors;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StreamGroupingByCollectorUnitTest {
+ @Test
+ public void givenListOfStrings_whenGroupingEqualStrings_thenUseCollectorsGroupingByToGroupEqualStringsAndCountOfOccurrences() {
+
+ List list = new ArrayList<>(Arrays.asList("Foo", "Bar", "Bar", "Foo", "Bar"));
+
+ Map result = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get("Foo"));
+ Assert.assertEquals(new Long(3), result.get("Bar"));
+
+ }
+
+ @Test
+ public void givenListOfStrings_whenGroupingEqualLengthStrings_thenUseCollectorsGroupingByConcurrentToGroupEqualLengthStringsAndCountOfOccurrences() {
+
+ List list = new ArrayList<>(Arrays.asList("Adam", "Bill", "Jack", "Joe", "Ian"));
+
+ Map result = list.stream().collect(Collectors.groupingByConcurrent(String::length, Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get(3));
+ Assert.assertEquals(new Long(3), result.get(4));
+
+ }
+
+ @Test
+ public void givenListOfEmployees_whenGroupingDepartmentId_thenUseCollectorsGroupingByDepartmentIdAndCountNumberOfEmployeesWithinEveryDepartment() {
+
+ List list = new ArrayList<>(Arrays.asList(new Employee(1, "Joe", 1), new Employee(2, "Josh", 1), new Employee(3, "Jamie", 2), new Employee(4, "Jim", 2), new Employee(5, "Jack", 2)));
+
+ Map result = list.stream().collect(Collectors.groupingBy(Employee::getDepartmentId, Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get(1));
+ Assert.assertEquals(new Long(3), result.get(2));
+
+ }
+
+ static class Employee {
+
+ Integer employeeId;
+ String employeeName;
+ Integer departmentId;
+
+ Employee(Integer employeeId, String employeeName, Integer departmentId) {
+ this.employeeId = employeeId;
+ this.employeeName = employeeName;
+ this.departmentId = departmentId;
+ }
+
+ public Integer getEmployeeId() {
+ return employeeId;
+ }
+
+ public void setEmployeeId(Integer employeeId) {
+ this.employeeId = employeeId;
+ }
+
+ public String getEmployeeName() {
+ return employeeName;
+ }
+
+ public void setEmployeeName(String employeeName) {
+ this.employeeName = employeeName;
+ }
+
+ public Integer getDepartmentId() {
+ return departmentId;
+ }
+
+ public void setDepartmentId(Integer departmentId) {
+ this.departmentId = departmentId;
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-streams/pom.xml b/core-java-modules/core-java-streams/pom.xml
index f02ba1c69a..3d5de7cebe 100644
--- a/core-java-modules/core-java-streams/pom.xml
+++ b/core-java-modules/core-java-streams/pom.xml
@@ -91,7 +91,6 @@
true
-
org.apache.maven.plugins
diff --git a/core-java-modules/core-java-string-algorithms-3/pom.xml b/core-java-modules/core-java-string-algorithms-3/pom.xml
index 1048b796bc..c8243258c1 100644
--- a/core-java-modules/core-java-string-algorithms-3/pom.xml
+++ b/core-java-modules/core-java-string-algorithms-3/pom.xml
@@ -27,7 +27,6 @@
guava
${guava.version}
-
org.junit.jupiter
junit-jupiter
diff --git a/core-java-modules/core-java-string-conversions-2/README.md b/core-java-modules/core-java-string-conversions-2/README.md
index 3bd3ba927e..46eb783a27 100644
--- a/core-java-modules/core-java-string-conversions-2/README.md
+++ b/core-java-modules/core-java-string-conversions-2/README.md
@@ -7,4 +7,6 @@ This module contains articles about string conversions from/to another type.
- [Convert String to Byte Array and Reverse in Java](https://www.baeldung.com/java-string-to-byte-array)
- [Convert Character Array to String in Java](https://www.baeldung.com/java-char-array-to-string)
- [Converting String to BigDecimal in Java](https://www.baeldung.com/java-string-to-bigdecimal)
+- [Converting String to BigInteger in Java](https://www.baeldung.com/java-string-to-biginteger)
+- [Convert a String to Camel Case](https://www.baeldung.com/java-string-to-camel-case)
- More articles: [[<-- prev]](/core-java-string-conversions)
diff --git a/core-java-modules/core-java-string-conversions-2/pom.xml b/core-java-modules/core-java-string-conversions-2/pom.xml
index 8a222a6b5d..700b4394c6 100644
--- a/core-java-modules/core-java-string-conversions-2/pom.xml
+++ b/core-java-modules/core-java-string-conversions-2/pom.xml
@@ -33,8 +33,36 @@
${hamcrest.version}
test
+
+ com.ibm.icu
+ icu4j
+ ${icu.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+ org.apache.commons
+ commons-text
+ ${commons-text.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ 64.2
+ 3.12.2
+ 1.9
+ 30.1.1-jre
+
+
core-java-string-conversions-2
diff --git a/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java
new file mode 100644
index 0000000000..7a4369ac4a
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java
@@ -0,0 +1,109 @@
+package com.baeldung.stringtocamelcase;
+
+import com.google.common.base.CaseFormat;
+import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.text.BreakIterator;
+import org.apache.commons.text.CaseUtils;
+import org.apache.commons.text.WordUtils;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class StringToCamelCase {
+
+ public static String toCamelCaseByIteration(String text, char delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ boolean shouldConvertNextCharToLower = true;
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char currentChar = text.charAt(i);
+ if (currentChar == delimiter) {
+ shouldConvertNextCharToLower = false;
+ } else if (shouldConvertNextCharToLower) {
+ builder.append(Character.toLowerCase(currentChar));
+ } else {
+ builder.append(Character.toUpperCase(currentChar));
+ shouldConvertNextCharToLower = true;
+ }
+ }
+ return builder.toString();
+ }
+
+ public static String toCamelCaseBySplitting(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ String[] words = text.split(delimiter);
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0, wordsLength = words.length; i < wordsLength; i++) {
+ String word = words[i];
+ if (i == 0) {
+ //Make the first word all lowercase
+ word = word.isEmpty() ? word : word.toLowerCase();
+ } else {
+ //Convert the first character to Uppercase and others to lowercase
+ // e.g sTRING =====> String
+ word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
+ }
+ builder.append(word);
+ }
+ return builder.toString();
+ }
+
+ public static String toCamelCaseBySplittingUsingStreams(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ String[] words = text.split(delimiter);
+ //Convert the first word to lowercase and then every
+ //other word to Title Case.
+ String firstWord = words[0].toLowerCase();
+ String otherWords = Arrays.stream(words, 1, words.length)
+ .filter(word -> !word.isEmpty())
+ .map(word -> Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(""));
+
+ return firstWord + otherWords;
+ }
+
+ public static String toCamelCaseByRegex(String text) {
+ StringBuilder builder = new StringBuilder();
+ String[] words = text.split("[\\W_]+");
+ for (int i = 0; i < words.length; i++) {
+ String word = words[i];
+ if (i == 0) {
+ word = word.isEmpty() ? word : word.toLowerCase();
+ } else {
+ word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
+ }
+ builder.append(word);
+ }
+ return builder.toString();
+ }
+
+ //Third-Party Libraries
+ public static String toCamelCaseUsingICU4J(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ text = UCharacter.toTitleCase(text, BreakIterator.getTitleInstance()).replaceAll(delimiter, "");
+ StringBuilder builder = new StringBuilder(text);
+ builder.setCharAt(0, Character.toLowerCase(text.charAt(0)));
+ return builder.toString();
+ }
+
+ public static String toCamelCaseUsingGuava(String text, String delimiter) {
+ String toUpperUnderscore = text.toUpperCase().replaceAll(delimiter, "_");
+ return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, toUpperUnderscore);
+ }
+
+ public static String toCamelCaseUsingApacheCommons(String text, char delimiter) {
+ text = WordUtils.capitalizeFully(text, delimiter).replaceAll(String.valueOf(delimiter), "");
+ StringBuilder builder = new StringBuilder(text);
+ builder.setCharAt(0, Character.toLowerCase(text.charAt(0)));
+ return builder.toString();
+ }
+
+}
diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java
new file mode 100644
index 0000000000..82ffe96d84
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.stringtobybiginteger;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+public class StringToBigIntegerUnitTest {
+
+ @Test
+ public void whenGetStringWithOutRadix_thenOK() {
+ String inputString = "878";
+ BigInteger result = new BigInteger(inputString);
+ assertEquals("878", result.toString());
+ }
+
+ @Test
+ public void whenGetStringWithRadix_thenOK() {
+ String inputString = "290f98";
+ BigInteger result = new BigInteger(inputString, 16);
+ assertEquals("2690968", result.toString());
+ }
+
+ @Test(expected = NumberFormatException.class)
+ public void whenGetStringWithOutRadix_thenThrowError() {
+ String inputString = "290f98";
+ new BigInteger(inputString);
+ }
+
+ @Test(expected = NumberFormatException.class)
+ public void whenGetStringWithRadix_thenThrowError() {
+ String inputString = "290f98";
+ new BigInteger(inputString, 7);
+ }
+
+ @Test
+ public void whenGetStringUsingByte_thenOk() {
+ String inputString = "290f98";
+ byte[] inputStringBytes = inputString.getBytes();
+ BigInteger result = new BigInteger(inputStringBytes);
+ assertEquals("290f98", new String(result.toByteArray()));
+ }
+}
diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java
new file mode 100644
index 0000000000..3adc4434a9
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java
@@ -0,0 +1,111 @@
+package com.baeldung.stringtocamelcase;
+
+
+import com.google.common.base.CaseFormat;
+import org.apache.commons.text.CaseUtils;
+import org.junit.Test;
+
+import static com.baeldung.stringtocamelcase.StringToCamelCase.*;
+import static org.assertj.core.api.Assertions.*;
+
+
+public class StringToCamelCaseUnitTest {
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseByIteration_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByIteration("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseByIteration_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByIteration("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplitting_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplitting("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseBySplitting_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplitting("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplittingUsingStreams("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplittingUsingStreams("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() {
+ assertThat(CaseUtils.toCamelCase("THIS STRING SHOULD BE IN CAMEL CASE", false, ' '))
+ .isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() {
+ assertThat(CaseUtils.toCamelCase("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", false, '_'))
+ .isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingICU4J("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingICU4J("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingGuava("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingGuava("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingApacheCommons("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingApacheCommons("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("THIS STRING SHOULD BE IN CAMEL CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteUnderscores_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenRandomString_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case")).isEqualTo("pleaseTurnThis56738ToCamelCase");
+ }
+
+ @Test
+ public void givenRandomStringWithDifferentDelimiters_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case This should-be_in;camel-case")).isEqualTo("pleaseTurnThis56738ToCamelCaseThisShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenUppercaseWordWithUnderscores_WhenCaseFormatToLowerCamel_ThenReturnCamelCase() {
+ assertThat(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+}
diff --git a/core-java-modules/core-java-string-operations-3/pom.xml b/core-java-modules/core-java-string-operations-3/pom.xml
index aa9fe38cca..b73851f90c 100644
--- a/core-java-modules/core-java-string-operations-3/pom.xml
+++ b/core-java-modules/core-java-string-operations-3/pom.xml
@@ -44,13 +44,18 @@
-
- 3.6.1
- 3.6.3
- 6.1.1
- 2.11.1
- 3.1.0
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
@@ -58,4 +63,15 @@
https://repo.gradle.org/gradle/libs-releases-local/
+
+
+ 11
+ 11
+ 3.6.1
+ 3.6.3
+ 6.1.1
+ 2.11.1
+ 3.1.0
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java b/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java
new file mode 100644
index 0000000000..1a86edd45a
--- /dev/null
+++ b/core-java-modules/core-java-string-operations-3/src/main/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUtils.java
@@ -0,0 +1,61 @@
+package com.baeldung.stringfilenamevalidaiton;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class StringFilenameValidationUtils {
+
+ public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '*', ':', '<', '>', '?', '\\', '|', 0x7F};
+ public static final Character[] INVALID_UNIX_SPECIFIC_CHARS = {'\000'};
+
+ public static final String REGEX_PATTERN = "^[A-za-z0-9.]{1,255}$";
+
+ private StringFilenameValidationUtils() {
+ }
+
+ public static boolean validateStringFilenameUsingIO(String filename) throws IOException {
+ File file = new File(filename);
+ boolean created = false;
+ try {
+ created = file.createNewFile();
+ return created;
+ } finally {
+ if (created) {
+ file.delete();
+ }
+ }
+ }
+
+ public static boolean validateStringFilenameUsingNIO2(String filename) {
+ Paths.get(filename);
+ return true;
+ }
+
+ public static boolean validateStringFilenameUsingContains(String filename) {
+ if (filename == null || filename.isEmpty() || filename.length() > 255) {
+ return false;
+ }
+ return Arrays.stream(getInvalidCharsByOS())
+ .noneMatch(ch -> filename.contains(ch.toString()));
+ }
+
+ public static boolean validateStringFilenameUsingRegex(String filename) {
+ if (filename == null) {
+ return false;
+ }
+ return filename.matches(REGEX_PATTERN);
+ }
+
+ private static Character[] getInvalidCharsByOS() {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.contains("win")) {
+ return INVALID_WINDOWS_SPECIFIC_CHARS;
+ } else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
+ return INVALID_UNIX_SPECIFIC_CHARS;
+ } else {
+ return new Character[]{};
+ }
+ }
+}
diff --git a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java
index 9afba62947..2e6613b0be 100644
--- a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java
+++ b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/splitstringbynewline/SplitStringByNewLineUnitTest.java
@@ -1,9 +1,13 @@
package com.baeldung.splitstringbynewline;
import org.junit.Test;
+
+import java.util.regex.Pattern;
+
import static org.assertj.core.api.Assertions.assertThat;
public class SplitStringByNewLineUnitTest {
+
@Test
public void givenString_whenSplitByNewLineUsingSystemLineSeparator_thenReturnsArray() {
assertThat("Line1\nLine2\nLine3".split(System.lineSeparator())).containsExactly("Line1", "Line2", "Line3");
@@ -26,4 +30,20 @@ public class SplitStringByNewLineUnitTest {
assertThat("Line1\r\nLine2\r\nLine3".split("\\R")).containsExactly("Line1", "Line2", "Line3");
}
+
+ @Test
+ public void givenString_whenSplitByNewLineUsingJava8PatternClass_thenReturnsStream() {
+ Pattern pattern = Pattern.compile("\\R");
+
+ assertThat(pattern.splitAsStream("Line1\nLine2\nLine3")).containsExactly("Line1", "Line2", "Line3");
+
+ assertThat(pattern.splitAsStream("Line1\rLine2\rLine3")).containsExactly("Line1", "Line2", "Line3");
+
+ assertThat(pattern.splitAsStream("Line1\r\nLine2\r\nLine3")).containsExactly("Line1", "Line2", "Line3");
+ }
+
+ @Test
+ public void givenString_whenSplitByNewLineUsingJava11Lines_thenReturnsStream() {
+ assertThat("Line1\nLine2\rLine3\r\nLine4".lines()).containsExactly("Line1", "Line2", "Line3", "Line4");
+ }
}
diff --git a/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java
new file mode 100644
index 0000000000..3e787f08be
--- /dev/null
+++ b/core-java-modules/core-java-string-operations-3/src/test/java/com/baeldung/stringfilenamevalidaiton/StringFilenameValidationUnitTest.java
@@ -0,0 +1,130 @@
+package com.baeldung.stringfilenamevalidaiton;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EmptySource;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.NullSource;
+
+import java.io.IOException;
+import java.nio.file.InvalidPathException;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingContains;
+import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingIO;
+import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingNIO2;
+import static com.baeldung.stringfilenamevalidaiton.StringFilenameValidationUtils.validateStringFilenameUsingRegex;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class StringFilenameValidationUnitTest {
+
+ private static final String CORRECT_FILENAME_PATTERN = "baeldung.txt";
+
+ @ParameterizedTest
+ @MethodSource("correctAlphanumericFilenamesProvider")
+ public void givenCorrectAlphanumericRandomFilenameString_whenValidateUsingIO_thenReturnTrue(String filename) throws IOException {
+ assertThat(validateStringFilenameUsingIO(filename)).isTrue();
+ assertThat(validateStringFilenameUsingNIO2(filename)).isTrue();
+ assertThat(validateStringFilenameUsingContains(filename)).isTrue();
+ assertThat(validateStringFilenameUsingRegex(filename)).isTrue();
+ }
+
+ @Test
+ public void givenTooLongFileNameString_whenValidate_thenIOAndCustomFailsNIO2Succeed() {
+ String filename = RandomStringUtils.randomAlphabetic(500);
+ assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
+ .isInstanceOf(IOException.class)
+ .hasMessageContaining("File name too long");
+ assertThat(validateStringFilenameUsingNIO2(filename)).isTrue();
+ assertThat(validateStringFilenameUsingContains(filename)).isFalse();
+ assertThat(validateStringFilenameUsingRegex(filename)).isFalse();
+ }
+
+ @ParameterizedTest
+ @NullSource
+ public void givenNullString_whenValidate_thenFails(String filename) {
+ assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
+ .isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename))
+ .isInstanceOf(NullPointerException.class);
+ assertThat(validateStringFilenameUsingContains(filename)).isFalse();
+ assertThat(validateStringFilenameUsingRegex(filename)).isFalse();
+ }
+
+ @ParameterizedTest
+ @EmptySource
+ public void givenEmptyString_whenValidate_thenIOAndCustomFailsNIO2Succeed(String filename) {
+ assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
+ .isInstanceOf(IOException.class);
+ assertThat(validateStringFilenameUsingNIO2(filename)).isTrue();
+ assertThat(validateStringFilenameUsingContains(filename)).isFalse();
+ assertThat(validateStringFilenameUsingRegex(filename)).isFalse();
+ }
+
+ @ParameterizedTest
+ @EnabledOnOs({OS.LINUX, OS.MAC})
+ @MethodSource("filenamesWithInvalidWindowsChars")
+ public void givenFilenameStringWithInvalidWindowsCharAndIsUnix_whenValidateUsingIO_thenReturnTrue(String filename) throws IOException {
+ assertThat(validateStringFilenameUsingIO(filename)).isTrue();
+ assertThat(validateStringFilenameUsingNIO2(filename)).isTrue();
+ assertThat(validateStringFilenameUsingContains(filename)).isTrue();
+ }
+
+ @ParameterizedTest
+ @EnabledOnOs(OS.WINDOWS)
+ @MethodSource("filenamesWithInvalidWindowsChars")
+ public void givenFilenameStringWithInvalidWindowsCharAndIsWindows_whenValidateUsingIO_thenRaiseException(String filename) {
+ assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
+ .isInstanceOf(IOException.class)
+ .hasMessageContaining("Invalid file path");
+
+ assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename))
+ .isInstanceOf(InvalidPathException.class)
+ .hasMessage("character not allowed");
+
+ assertThat(validateStringFilenameUsingContains(filename)).isFalse();
+ }
+
+ @ParameterizedTest
+ @EnabledOnOs({OS.LINUX, OS.MAC})
+ @MethodSource("filenamesWithInvalidUnixChars")
+ public void givenFilenameStringWithInvalidUnixCharAndIsUnix_whenValidate_thenRaiseException(String filename) {
+ assertThatThrownBy(() -> validateStringFilenameUsingIO(filename))
+ .isInstanceOf(IOException.class)
+ .hasMessageContaining("Invalid file path");
+
+ assertThatThrownBy(() -> validateStringFilenameUsingNIO2(filename))
+ .isInstanceOf(InvalidPathException.class)
+ .hasMessageContaining("character not allowed");
+
+ assertThat(validateStringFilenameUsingContains(filename)).isFalse();
+ }
+
+
+ private static Stream correctAlphanumericFilenamesProvider() {
+ return Stream.generate(() -> RandomStringUtils.randomAlphanumeric(1, 10) + "." + RandomStringUtils.randomAlphabetic(3, 5)).limit(10);
+ }
+
+ private static Stream filenamesWithInvalidWindowsChars() {
+ return Arrays.stream(StringFilenameValidationUtils.INVALID_WINDOWS_SPECIFIC_CHARS)
+ .map(character -> {
+ int idx = RandomUtils.nextInt(0, CORRECT_FILENAME_PATTERN.length());
+ return CORRECT_FILENAME_PATTERN.substring(0, idx) + character + CORRECT_FILENAME_PATTERN.substring(idx);
+ });
+ }
+
+ private static Stream filenamesWithInvalidUnixChars() {
+ return Arrays.stream(StringFilenameValidationUtils.INVALID_UNIX_SPECIFIC_CHARS)
+ .map(character -> {
+ int idx = RandomUtils.nextInt(0, CORRECT_FILENAME_PATTERN.length());
+ return CORRECT_FILENAME_PATTERN.substring(0, idx) + character + CORRECT_FILENAME_PATTERN.substring(idx);
+ });
+ }
+
+}
diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md
index 14857d5d87..f1cf2d8c09 100644
--- a/core-java-modules/core-java/README.md
+++ b/core-java-modules/core-java/README.md
@@ -12,3 +12,4 @@
- [A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle)
- [Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties)
- [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities)
+- [Generating Alphanumeric UUID String in Java](https://www.baeldung.com/java-generate-alphanumeric-uuid)
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
index cb43a26929..54de1f95c6 100644
--- a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
+++ b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
@@ -1,6 +1,8 @@
package com.baeldung.uuid;
import java.io.UnsupportedEncodingException;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
@@ -132,21 +134,21 @@ public class UUIDGenerator {
}
private static byte[] bytesFromUUID(String uuidHexString) {
- String normalizedUUIDHexString = uuidHexString.replace("-","");
+ String normalizedUUIDHexString = uuidHexString.replace("-", "");
assert normalizedUUIDHexString.length() == 32;
byte[] bytes = new byte[16];
for (int i = 0; i < 16; i++) {
- byte b = hexToByte(normalizedUUIDHexString.substring(i*2, i*2+2));
+ byte b = hexToByte(normalizedUUIDHexString.substring(i * 2, i * 2 + 2));
bytes[i] = b;
}
return bytes;
}
public static byte hexToByte(String hexString) {
- int firstDigit = Character.digit(hexString.charAt(0),16);
- int secondDigit = Character.digit(hexString.charAt(1),16);
+ int firstDigit = Character.digit(hexString.charAt(0), 16);
+ int secondDigit = Character.digit(hexString.charAt(1), 16);
return (byte) ((firstDigit << 4) + secondDigit);
}
@@ -154,14 +156,47 @@ public class UUIDGenerator {
int finalLength = byteArray1.length + byteArray2.length;
byte[] result = new byte[finalLength];
- for(int i = 0; i < byteArray1.length; i++) {
+ for (int i = 0; i < byteArray1.length; i++) {
result[i] = byteArray1[i];
}
- for(int i = 0; i < byteArray2.length; i++) {
- result[byteArray1.length+i] = byteArray2[i];
+ for (int i = 0; i < byteArray2.length; i++) {
+ result[byteArray1.length + i] = byteArray2[i];
}
return result;
}
+
+ public static UUID generateType5UUID(String name) {
+
+ try {
+
+ byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+
+ byte[] hash = md.digest(bytes);
+
+ long msb = getLeastAndMostSignificantBitsVersion5(hash, 0);
+ long lsb = getLeastAndMostSignificantBitsVersion5(hash, 8);
+ // Set the version field
+ msb &= ~(0xfL << 12);
+ msb |= ((long) 5) << 12;
+ // Set the variant field to 2
+ lsb &= ~(0x3L << 62);
+ lsb |= 2L << 62;
+ return new UUID(msb, lsb);
+
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static long getLeastAndMostSignificantBitsVersion5(final byte[] src, final int offset) {
+ long ans = 0;
+ for (int i = offset + 7; i >= offset; i -= 1) {
+ ans <<= 8;
+ ans |= src[i] & 0xffL;
+ }
+ return ans;
+ }
}
\ No newline at end of file
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
index 9e08363a63..cf0d7656ff 100644
--- a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
+++ b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
@@ -61,4 +61,14 @@ class UUIDGeneratorUnitTest {
assertEquals(5, uuid.version());
assertEquals(2, uuid.variant());
}
+
+ @Test
+ public void version_5_UUID_is_correctly_generated_for_domain_baeldung_name() {
+
+ UUID uuid = UUIDGenerator.generateType5UUID("baeldung.com");
+
+ assertEquals("efd5462b-b07a-52a3-94ea-bf575c0e0e75", uuid.toString());
+ assertEquals(5, uuid.version());
+ assertEquals(2, uuid.variant());
+ }
}
\ No newline at end of file
diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml
index b801d44a08..13dd20b5da 100644
--- a/core-java-modules/pom.xml
+++ b/core-java-modules/pom.xml
@@ -48,7 +48,6 @@
core-java-concurrency-collections
core-java-concurrency-collections-2
core-java-console
-
core-java-8-datetime-2
core-java-date-operations-2
core-java-8-datetime
@@ -63,7 +62,6 @@
core-java-io-4
core-java-io-apis
core-java-io-conversions
- core-java-io-conversions-2
core-java-jar
core-java-jndi
core-java-jvm
@@ -103,6 +101,7 @@
core-java-streams
core-java-streams-2
core-java-streams-3
+ core-java-streams-4
core-java-string-algorithms
core-java-string-algorithms-2
core-java-string-algorithms-3
@@ -111,9 +110,9 @@
core-java-string-conversions-2
core-java-string-operations
core-java-string-operations-2
- core-java-string-operations-3
core-java-sun
core-java-regex
+ core-java-regex-2
pre-jpms
diff --git a/data-structures/README.md b/data-structures/README.md
index 0a29bd8964..cfcfbc6a0a 100644
--- a/data-structures/README.md
+++ b/data-structures/README.md
@@ -12,3 +12,5 @@ This module contains articles about data structures in Java
- [Guide to AVL Trees in Java](https://www.baeldung.com/java-avl-trees)
- [Graphs in Java](https://www.baeldung.com/java-graphs)
- [Implementing a Ring Buffer in Java](https://www.baeldung.com/java-ring-buffer)
+- [How to Implement Min-Max Heap In Java](https://www.baeldung.com/java-min-max-heap)
+- [How to Implement LRU Cache in Java](https://www.baeldung.com/java-lru-cache)
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Cache.java b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java
new file mode 100644
index 0000000000..f070f66191
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java
@@ -0,0 +1,15 @@
+package com.baeldung.lrucache;
+
+import java.util.Optional;
+
+public interface Cache {
+ boolean put(K key, V value);
+
+ Optional get(K key);
+
+ int size();
+
+ boolean isEmpty();
+
+ void clear();
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java
new file mode 100644
index 0000000000..7d260c4531
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java
@@ -0,0 +1,31 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+
+public class CacheElement {
+ private K key;
+ private V value;
+
+ public CacheElement(K key, V value) {
+ this.value = value;
+ this.key = key;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public void setKey(K key) {
+ this.key = key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public void setValue(V value) {
+ this.value = value;
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java
new file mode 100644
index 0000000000..5850d6c13d
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java
@@ -0,0 +1,163 @@
+package com.baeldung.lrucache;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class DoublyLinkedList {
+
+ private DummyNode dummyNode;
+ private LinkedListNode head;
+ private LinkedListNode tail;
+ private AtomicInteger size;
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public DoublyLinkedList() {
+ this.dummyNode = new DummyNode(this);
+ clear();
+ }
+
+ public void clear() {
+ this.lock.writeLock().lock();
+ try {
+ head = dummyNode;
+ tail = dummyNode;
+ size = new AtomicInteger(0);
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public int size() {
+ this.lock.readLock().lock();
+ try {
+ return size.get();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public boolean isEmpty() {
+ this.lock.readLock().lock();
+ try {
+ return head.isEmpty();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public boolean contains(T value) {
+ this.lock.readLock().lock();
+ try {
+ return search(value).hasElement();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public LinkedListNode search(T value) {
+ this.lock.readLock().lock();
+ try {
+ return head.search(value);
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public LinkedListNode add(T value) {
+ this.lock.writeLock().lock();
+ try {
+ head = new Node(value, head, this);
+ if (tail.isEmpty()) {
+ tail = head;
+ }
+ size.incrementAndGet();
+ return head;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public boolean addAll(Collection values) {
+ this.lock.writeLock().lock();
+ try {
+ for (T value : values) {
+ if (add(value).isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode remove(T value) {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode linkedListNode = head.search(value);
+ if (!linkedListNode.isEmpty()) {
+ if (linkedListNode == tail) {
+ tail = tail.getPrev();
+ }
+ if (linkedListNode == head) {
+ head = head.getNext();
+ }
+ linkedListNode.detach();
+ size.decrementAndGet();
+ }
+ return linkedListNode;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode removeTail() {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode oldTail = tail;
+ if (oldTail == head) {
+ tail = head = dummyNode;
+ } else {
+ tail = tail.getPrev();
+ oldTail.detach();
+ }
+ if (!oldTail.isEmpty()) {
+ size.decrementAndGet();
+ }
+ return oldTail;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode moveToFront(LinkedListNode node) {
+ return node.isEmpty() ? dummyNode : updateAndMoveToFront(node, node.getElement());
+ }
+
+ public LinkedListNode updateAndMoveToFront(LinkedListNode node, T newValue) {
+ this.lock.writeLock().lock();
+ try {
+ if (node.isEmpty() || (this != (node.getListReference()))) {
+ return dummyNode;
+ }
+ detach(node);
+ add(newValue);
+ return head;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ private void detach(LinkedListNode node) {
+ if (node != tail) {
+ node.detach();
+ if (node == head) {
+ head = head.getNext();
+ }
+ size.decrementAndGet();
+ } else {
+ removeTail();
+ }
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java
new file mode 100644
index 0000000000..4965b70bcf
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java
@@ -0,0 +1,62 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+public class DummyNode implements LinkedListNode {
+ private DoublyLinkedList list;
+
+ public DummyNode(DoublyLinkedList list) {
+ this.list = list;
+ }
+
+ @Override
+ public boolean hasElement() {
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public T getElement() throws NullPointerException {
+ throw new NullPointerException();
+ }
+
+ @Override
+ public void detach() {
+ return;
+ }
+
+ @Override
+ public DoublyLinkedList getListReference() {
+ return list;
+ }
+
+ @Override
+ public LinkedListNode setPrev(LinkedListNode next) {
+ return next;
+ }
+
+ @Override
+ public LinkedListNode setNext(LinkedListNode prev) {
+ return prev;
+ }
+
+ @Override
+ public LinkedListNode getPrev() {
+ return this;
+ }
+
+ @Override
+ public LinkedListNode getNext() {
+ return this;
+ }
+
+ @Override
+ public LinkedListNode search(T value) {
+ return this;
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java
new file mode 100644
index 0000000000..3032a90d8e
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java
@@ -0,0 +1,100 @@
+package com.baeldung.lrucache;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class LRUCache implements Cache {
+ private int size;
+ private Map>> linkedListNodeMap;
+ private DoublyLinkedList> doublyLinkedList;
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public LRUCache(int size) {
+ this.size = size;
+ this.linkedListNodeMap = new ConcurrentHashMap<>(size);
+ this.doublyLinkedList = new DoublyLinkedList<>();
+ }
+
+ @Override
+ public boolean put(K key, V value) {
+ this.lock.writeLock().lock();
+ try {
+ CacheElement item = new CacheElement(key, value);
+ LinkedListNode> newNode;
+ if (this.linkedListNodeMap.containsKey(key)) {
+ LinkedListNode> node = this.linkedListNodeMap.get(key);
+ newNode = doublyLinkedList.updateAndMoveToFront(node, item);
+ } else {
+ if (this.size() >= this.size) {
+ this.evictElement();
+ }
+ newNode = this.doublyLinkedList.add(item);
+ }
+ if (newNode.isEmpty()) {
+ return false;
+ }
+ this.linkedListNodeMap.put(key, newNode);
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public Optional get(K key) {
+ this.lock.readLock().lock();
+ try {
+ LinkedListNode> linkedListNode = this.linkedListNodeMap.get(key);
+ if (linkedListNode != null && !linkedListNode.isEmpty()) {
+ linkedListNodeMap.put(key, this.doublyLinkedList.moveToFront(linkedListNode));
+ return Optional.of(linkedListNode.getElement().getValue());
+ }
+ return Optional.empty();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public int size() {
+ this.lock.readLock().lock();
+ try {
+ return doublyLinkedList.size();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public void clear() {
+ this.lock.writeLock().lock();
+ try {
+ linkedListNodeMap.clear();
+ doublyLinkedList.clear();
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+
+ private boolean evictElement() {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode> linkedListNode = doublyLinkedList.removeTail();
+ if (linkedListNode.isEmpty()) {
+ return false;
+ }
+ linkedListNodeMap.remove(linkedListNode.getElement().getKey());
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java
new file mode 100644
index 0000000000..219ee13496
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java
@@ -0,0 +1,23 @@
+package com.baeldung.lrucache;
+
+public interface LinkedListNode {
+ boolean hasElement();
+
+ boolean isEmpty();
+
+ V getElement() throws NullPointerException;
+
+ void detach();
+
+ DoublyLinkedList getListReference();
+
+ LinkedListNode setPrev(LinkedListNode prev);
+
+ LinkedListNode setNext(LinkedListNode next);
+
+ LinkedListNode getPrev();
+
+ LinkedListNode getNext();
+
+ LinkedListNode search(V value);
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Node.java b/data-structures/src/main/java/com/baeldung/lrucache/Node.java
new file mode 100644
index 0000000000..340bb1dd82
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/Node.java
@@ -0,0 +1,71 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+public class Node implements LinkedListNode {
+ private T value;
+ private DoublyLinkedList list;
+ private LinkedListNode next;
+ private LinkedListNode prev;
+
+ public Node(T value, LinkedListNode next, DoublyLinkedList list) {
+ this.value = value;
+ this.next = next;
+ this.setPrev(next.getPrev());
+ this.prev.setNext(this);
+ this.next.setPrev(this);
+ this.list = list;
+ }
+
+ @Override
+ public boolean hasElement() {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public T getElement() {
+ return value;
+ }
+
+ public void detach() {
+ this.prev.setNext(this.getNext());
+ this.next.setPrev(this.getPrev());
+ }
+
+ @Override
+ public DoublyLinkedList getListReference() {
+ return this.list;
+ }
+
+ @Override
+ public LinkedListNode setPrev(LinkedListNode prev) {
+ this.prev = prev;
+ return this;
+ }
+
+ @Override
+ public LinkedListNode setNext(LinkedListNode next) {
+ this.next = next;
+ return this;
+ }
+
+ @Override
+ public LinkedListNode getPrev() {
+ return this.prev;
+ }
+
+ @Override
+ public LinkedListNode getNext() {
+ return this.next;
+ }
+
+ @Override
+ public LinkedListNode search(T value) {
+ return this.getElement() == value ? this : this.getNext().search(value);
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java
new file mode 100644
index 0000000000..124183afe6
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java
@@ -0,0 +1,344 @@
+package com.baeldung.minmaxheap;
+
+import java.util.*;
+
+/**
+ * Created by arash on 15.06.21.
+ */
+
+public class MinMaxHeap> {
+ private List array;
+ private int capacity;
+ private int indicator;
+
+ MinMaxHeap(int capacity) {
+ array = new ArrayList<>();
+ this.capacity = capacity;
+ indicator = 1;
+ }
+
+ MinMaxHeap(List array) {
+ this.array = array;
+ this.capacity = array.size();
+ this.indicator = array.size() + 1;
+ }
+
+ public List getMinMaxHeap() {
+ return array;
+ }
+
+ public List create() {
+ for (int i = Math.floorDiv(array.size(), 2); i >= 1; i--) {
+ pushDown(array, i);
+ }
+ return array;
+ }
+
+ private void pushDown(List array, int i) {
+ if (isEvenLevel(i)) {
+ pushDownMin(array, i);
+ } else {
+ pushDownMax(array, i);
+ }
+ }
+
+ private void pushDownMin(List h, int i) {
+ while (getLeftChildIndex(i) < indicator) {
+ int indexOfSmallest = getIndexOfSmallestChildOrGrandChild(h, i);
+ if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) {
+ if (getParentIndex(getParentIndex(indexOfSmallest)) == i) {
+ if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) {
+ swap(indexOfSmallest - 1, i - 1, h);
+ if (h.get(indexOfSmallest - 1).compareTo(h.get(getParentIndex(indexOfSmallest) - 1)) > 0) {
+ swap(indexOfSmallest - 1, getParentIndex(indexOfSmallest) - 1, h);
+ }
+ }
+ } else if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) {
+ swap(indexOfSmallest - 1, i - 1, h);
+ }
+ } else {
+ break;
+ }
+ i = indexOfSmallest;
+ }
+ }
+
+ private void pushDownMax(List h, int i) {
+ while (getLeftChildIndex(i) < indicator) {
+ int indexOfGreatest = getIndexOfGreatestChildOrGrandChild(h, i);
+ if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) {
+ if (getParentIndex(getParentIndex(indexOfGreatest)) == i) {
+ if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) {
+ swap(indexOfGreatest - 1, i - 1, h);
+ if (h.get(indexOfGreatest - 1).compareTo(h.get(getParentIndex(indexOfGreatest) - 1)) < 0) {
+ swap(indexOfGreatest - 1, getParentIndex(indexOfGreatest) - 1, h);
+ }
+ }
+ } else if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) {
+ swap(indexOfGreatest - 1, i - 1, h);
+ }
+ } else {
+ break;
+ }
+ i = indexOfGreatest;
+ }
+ }
+
+ private void swap(int i, int j, List h) { //switch data at x with data at y
+ T temp = h.get(i);
+ h.set(i, h.get(j));
+ h.set(j, temp);
+ }
+
+ private int getLeftChildIndex(int i) {
+ return 2 * i;
+ }
+
+ private int getRightChildIndex(int i) {
+ return ((2 * i) + 1);
+ }
+
+ private int getParentIndex(int i) {
+ return i / 2;
+ }
+
+ private int getGrandparentIndex(int i) {
+ return i / 4;
+ }
+
+ private boolean isEvenLevel(int i) {
+ return logBase2(i) % 2 == 0;
+ }
+
+ private int logBase2(int num) {
+ return (int) (Math.log(num) / Math.log(2));
+ }
+
+ private int getMinChildIndex(int i) {
+ return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) < 0 ? getLeftChildIndex(i) : getRightChildIndex(i);
+ }
+
+ private int getMaxChildIndex(int i) {
+ return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) > 0 ? getLeftChildIndex(i) : getRightChildIndex(i);
+ }
+
+ private int getIndexOfSmallestChildOrGrandChild(List h, int i) {
+ int minIndex = getLeftChildIndex(i);
+ T minValue = h.get(minIndex - 1);
+
+ if (getRightChildIndex(i) < indicator) {
+ if (h.get(getRightChildIndex(i) - 1).compareTo(minValue) < 0) {
+ minValue = h.get(getRightChildIndex(i));
+ minIndex = getRightChildIndex(i);
+ }
+ } else {
+ return minIndex;
+ }
+
+ if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) {
+ if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) {
+ minValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1);
+ minIndex = getLeftChildIndex(getLeftChildIndex(i));
+ }
+ } else {
+ return minIndex; //if no leftmost grandchild
+ }
+
+ if (getRightChildIndex(getLeftChildIndex(i)) < indicator) {
+ if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) {
+ minValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1);
+ minIndex = getRightChildIndex(getLeftChildIndex(i));
+ }
+ } else {
+ return minIndex; //if no left-right grandchild
+ }
+
+ if (getLeftChildIndex(getRightChildIndex(i)) < indicator) {
+ if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) {
+ minValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1);
+ minIndex = getLeftChildIndex(getRightChildIndex(i));
+ }
+ } else {
+ return minIndex; //if no right-left grandchild
+ }
+
+ if (getRightChildIndex(getRightChildIndex(i)) < indicator) {
+ if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) {
+ minValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1);
+ minIndex = getRightChildIndex(getRightChildIndex(i));
+ }
+ } else {
+ return minIndex;
+ }
+
+ return minIndex;
+ }
+
+ private int getIndexOfGreatestChildOrGrandChild(List h, int i) {
+ int maxIndex = getLeftChildIndex(i); //we know left child exists
+ T maxValue = h.get(maxIndex - 1);
+
+ if (getRightChildIndex(i) < indicator) {
+ if (h.get(getRightChildIndex(i) - 1).compareTo(maxValue) > 0) {
+ maxValue = h.get(getRightChildIndex(i) - 1);
+ maxIndex = getRightChildIndex(i);
+ }
+ } else {
+ return maxIndex;
+ }
+
+ if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) {
+ if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) {
+ maxValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1);
+ maxIndex = getLeftChildIndex(getLeftChildIndex(i));
+ }
+ } else {
+ return maxIndex; //if no leftmost grandchild
+ }
+
+ if (getRightChildIndex(getLeftChildIndex(i)) < indicator) {
+ if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) {
+ maxValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1);
+ maxIndex = getRightChildIndex(getLeftChildIndex(i));
+ }
+ } else {
+ return maxIndex; //if no left-right grandchild
+ }
+
+ if (getLeftChildIndex(getRightChildIndex(i)) < indicator) {
+ if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) {
+ maxValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1);
+ maxIndex = getLeftChildIndex(getRightChildIndex(i));
+ }
+ } else {
+ return maxIndex;
+ }
+
+ if (getRightChildIndex(getRightChildIndex(i)) < indicator) {
+ if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) {
+ maxValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1);
+ maxIndex = getRightChildIndex(getRightChildIndex(i));
+ }
+ } else {
+ return maxIndex;
+ }
+
+ return maxIndex;
+ }
+
+
+ public boolean isFull() {
+ return indicator == this.capacity + 1;
+ }
+
+ public boolean isEmpty() {
+ return indicator == 1;
+ }
+
+ public void insert(T item) {
+ if (isEmpty()) {
+ array.add(item);
+ indicator++;
+ } else if (!isFull()) {
+ array.add(item);
+ pushUp(array, indicator);
+ indicator++;
+ } else {
+ throw new RuntimeException("invalid operation !!!");
+ }
+ }
+
+ private void pushUpMin(List h, int i) {
+ while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) < 0) {
+ swap(i - 1, getGrandparentIndex(i) - 1, h);
+ i = getGrandparentIndex(i);
+ }
+ }
+
+ private void pushUpMax(List h, int i) {
+ while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) > 0) {
+ swap(i - 1, getGrandparentIndex(i) - 1, h);
+ i = getGrandparentIndex(i);
+ }
+ }
+
+ private boolean hasGrandparent(int i) {
+ return getParentIndex(i) > 1;
+ }
+
+ private void pushUp(List h, int i) {
+ if (i != 1) {
+ if (isEvenLevel(i)) {
+ if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) < 0) {
+ pushUpMin(h, i);
+ } else {
+ swap(i - 1, getParentIndex(i) - 1, h);
+ i = getParentIndex(i);
+ pushUpMax(h, i);
+ }
+ } else if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) > 0) {
+ pushUpMax(h, i);
+ } else {
+ swap(i - 1, getParentIndex(i) - 1, h);
+ i = getParentIndex(i);
+ pushUpMin(h, i);
+ }
+ }
+ }
+
+ public T min() {
+ if (!isEmpty()) {
+ return array.get(0);
+ }
+ return null;
+ }
+
+ public T max() {
+ if (!isEmpty()) {
+ if (indicator == 2) {
+ return array.get(0);
+ }
+ if (indicator == 3) {
+ return array.get(1);
+ }
+ return array.get(1).compareTo(array.get(2)) < 0 ? array.get(2) : array.get(1);
+ }
+ return null;
+ }
+
+ public T removeMin() {
+ T min = min();
+ if (min != null) {
+ if (indicator == 2) {
+ array.remove(indicator--);
+ return min;
+ }
+ array.set(0, array.get(--indicator - 1));
+ array.remove(indicator - 1);
+ pushDown(array, 1);
+ }
+ return min;
+ }
+
+ public T removeMax() {
+ T max = max();
+ if (max != null) {
+ int maxIndex;
+ if (indicator == 2) {
+ maxIndex = 0;
+ array.remove(--indicator - 1);
+ return max;
+ } else if (indicator == 3) {
+ maxIndex = 1;
+ array.remove(--indicator - 1);
+ return max;
+ } else {
+ maxIndex = array.get(1).compareTo(array.get(2)) < 0 ? 2 : 1;
+ }
+ array.set(maxIndex, array.get(--indicator - 1));
+ array.remove(indicator - 1);
+ pushDown(array, maxIndex + 1);
+ }
+ return max;
+ }
+}
diff --git a/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java
new file mode 100644
index 0000000000..e64085ae4c
--- /dev/null
+++ b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.lrucache;
+
+import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.IntStream;
+import static org.junit.Assert.*;
+
+public class LRUCacheUnitTest {
+
+ @Test
+ public void addSomeDataToCache_WhenGetData_ThenIsEqualWithCacheElement() {
+ LRUCache lruCache = new LRUCache<>(3);
+ lruCache.put("1", "test1");
+ lruCache.put("2", "test2");
+ lruCache.put("3", "test3");
+ assertEquals("test1", lruCache.get("1").get());
+ assertEquals("test2", lruCache.get("2").get());
+ assertEquals("test3", lruCache.get("3").get());
+ }
+
+ @Test
+ public void addDataToCacheToTheNumberOfSize_WhenAddOneMoreData_ThenLeastRecentlyDataWillEvict() {
+ LRUCache lruCache = new LRUCache<>(3);
+ lruCache.put("1", "test1");
+ lruCache.put("2", "test2");
+ lruCache.put("3", "test3");
+ lruCache.put("4", "test4");
+ assertFalse(lruCache.get("1").isPresent());
+ }
+
+ @Test
+ public void runMultiThreadTask_WhenPutDataInConcurrentToCache_ThenNoDataLost() throws Exception {
+ final int size = 50;
+ final ExecutorService executorService = Executors.newFixedThreadPool(5);
+ Cache cache = new LRUCache<>(size);
+ CountDownLatch countDownLatch = new CountDownLatch(size);
+ try {
+ IntStream.range(0, size).mapToObj(key -> () -> {
+ cache.put(key, "value" + key);
+ countDownLatch.countDown();
+ }).forEach(executorService::submit);
+ countDownLatch.await();
+ } finally {
+ executorService.shutdown();
+ }
+ assertEquals(cache.size(), size);
+ IntStream.range(0, size).forEach(i -> assertEquals("value" + i, cache.get(i).get()));
+ }
+}
diff --git a/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java
new file mode 100644
index 0000000000..1682db63be
--- /dev/null
+++ b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.minmaxheap;
+
+import org.junit.Assert;
+import org.junit.Test;
+import java.util.Arrays;
+import java.util.List;
+
+
+public class MinMaxHeapUnitTest {
+
+ @Test
+ public void givenUnOrderedArray_WhenCreateMinMaxHeap_ThenIsEqualWithMinMaxHeapOrdered() {
+ List list = Arrays.asList(34, 12, 28, 9, 30, 19, 1, 40);
+ MinMaxHeap minMaxHeap = new MinMaxHeap<>(list);
+ minMaxHeap.create();
+ Assert.assertEquals(Arrays.asList(1, 40, 34, 9, 30, 19, 28, 12), list);
+ }
+
+ @Test
+ public void givenNewElement_WhenInserted_ThenIsEqualWithMinMaxHeapOrdered() {
+ MinMaxHeap minMaxHeap = new MinMaxHeap(8);
+ minMaxHeap.insert(34);
+ minMaxHeap.insert(12);
+ minMaxHeap.insert(28);
+ minMaxHeap.insert(9);
+ minMaxHeap.insert(30);
+ minMaxHeap.insert(19);
+ minMaxHeap.insert(1);
+ minMaxHeap.insert(40);
+ Assert.assertEquals(Arrays.asList(1, 40, 28, 12, 30, 19, 9, 34), minMaxHeap.getMinMaxHeap());
+ }
+}
diff --git a/ddd/pom.xml b/ddd/pom.xml
index 77cf1f5341..cc5a173e85 100644
--- a/ddd/pom.xml
+++ b/ddd/pom.xml
@@ -101,4 +101,5 @@
1.0.1
+
\ No newline at end of file
diff --git a/docker/heap-sizing/pom.xml b/docker/heap-sizing/pom.xml
index a86a67fdcd..2cc354f6cf 100644
--- a/docker/heap-sizing/pom.xml
+++ b/docker/heap-sizing/pom.xml
@@ -1,21 +1,21 @@
-
+
4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.4.2
-
-
com.baeldung.docker
heap-sizing
0.0.1-SNAPSHOT
heap-sizing
Demo project for Spring Boot
-
- 11
-
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
org.springframework.boot
@@ -58,4 +58,8 @@
+
+ 11
+
+
diff --git a/flyway-cdi-extension/pom.xml b/flyway-cdi-extension/pom.xml
index 757d1e0a7f..dbd21374e2 100644
--- a/flyway-cdi-extension/pom.xml
+++ b/flyway-cdi-extension/pom.xml
@@ -56,4 +56,5 @@
8.5.33
1.3.2
+
\ No newline at end of file
diff --git a/geotools/pom.xml b/geotools/pom.xml
index 46913daa69..b9a6a7c91f 100644
--- a/geotools/pom.xml
+++ b/geotools/pom.xml
@@ -1,6 +1,5 @@
-
4.0.0
@@ -36,9 +35,9 @@
- osgeo
- Open Source Geospatial Foundation Repository
- http://download.osgeo.org/webdav/geotools/
+ osgeo-release
+ OSGeo Repository
+ https://repo.osgeo.org/repository/release/
@@ -48,4 +47,4 @@
15.2
-
+
\ No newline at end of file
diff --git a/grpc/pom.xml b/grpc/pom.xml
index c3e4996c29..915777f3bd 100644
--- a/grpc/pom.xml
+++ b/grpc/pom.xml
@@ -77,4 +77,5 @@
1.6.1
0.6.1
+
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/README.md b/guava-modules/guava-concurrency/README.md
new file mode 100644
index 0000000000..12fca9a1a5
--- /dev/null
+++ b/guava-modules/guava-concurrency/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Guava’s Futures and ListenableFuture](https://www.baeldung.com/guava-futures-listenablefuture)
diff --git a/guava-modules/guava-concurrency/pom.xml b/guava-modules/guava-concurrency/pom.xml
new file mode 100644
index 0000000000..a9ff19e6bd
--- /dev/null
+++ b/guava-modules/guava-concurrency/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ guava-concurrency
+
+
+ guava-modules
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java
new file mode 100644
index 0000000000..b6620bd1e2
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java
@@ -0,0 +1,105 @@
+package com.baeldung.guava.future;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureService {
+
+ private final ListeningExecutorService lExecService;
+
+ public ListenableFutureService() {
+ this.lExecService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ }
+
+ public ListenableFutureService(ListeningExecutorService lExecService) {
+ this.lExecService = lExecService;
+ }
+
+ public ListenableFuture fetchConfig(String configKey) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public FutureTask fetchConfigTask(String configKey) {
+ return new FutureTask<>(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public ListenableFutureTask fetchConfigListenableTask(String configKey) {
+ return ListenableFutureTask.create(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public ListenableFuture succeedingTask() {
+ return Futures.immediateFuture(new Random().nextInt(Integer.MAX_VALUE));
+ }
+
+ public ListenableFuture failingTask() {
+ return Futures.immediateFailedFuture(new ListenableFutureException());
+ }
+
+ public ListenableFuture getCartId() {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return new Random().nextInt(Integer.MAX_VALUE);
+ });
+ }
+
+ public ListenableFuture getCustomerName() {
+ String[] names = new String[] { "Mark", "Jane", "June" };
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return names[new Random().nextInt(names.length)];
+ });
+ }
+
+ public ListenableFuture> getCartItems() {
+ String[] items = new String[] { "Apple", "Orange", "Mango", "Pineapple" };
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+
+ int noOfItems = new Random().nextInt(items.length);
+ if (noOfItems == 0) ++noOfItems;
+
+ return Arrays.stream(items, 0, noOfItems).collect(Collectors.toList());
+ });
+ }
+
+ public ListenableFuture generateUsername(String firstName) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return firstName.replaceAll("[^a-zA-Z]+","")
+ .concat("@service.com");
+ });
+ }
+
+ public ListenableFuture generatePassword(String username) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ if (username.contains("@")) {
+ String[] parts = username.split("@");
+ return parts[0] + "123@" + parts[1];
+ } else {
+ return username + "123";
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java
new file mode 100644
index 0000000000..921c02b54a
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java
@@ -0,0 +1,4 @@
+package com.baeldung.guava.future.exception;
+
+public class ListenableFutureException extends Exception {
+}
diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java
new file mode 100644
index 0000000000..27a1cc6592
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java
@@ -0,0 +1,277 @@
+package com.baeldung.guava.future;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.function.UnaryOperator;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncCallable;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureComplexUnitTest {
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllAsList_thenAllSuccess() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.fetchConfig("config.1");
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3);
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ assertNotNull(configResults);
+ assertEquals(3, configResults.size());
+ for (int i = 0; i < 3; i++) {
+ assertTrue(configResults.get(i)
+ .contains("config." + i));
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenOneFailingTask_whenAllAsList_thenFailure() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.failingTask();
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3);
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ fail("Expected a failed future");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertTrue(t instanceof ListenableFutureException);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenOneFailingTask_whenSuccessfulAsList_thenSomeSuccess() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.failingTask();
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.successfulAsList(task1, task2, task3);
+
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ assertNotNull(configResults);
+ assertTrue(configResults.get(0).contains("config.0"));
+ assertNull(configResults.get(1));
+ assertTrue(configResults.get(2).contains("config.2"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllSucceed_thenSuccess() {
+ ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture cartIdTask = service.getCartId();
+ ListenableFuture customerNameTask = service.getCustomerName();
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ ListenableFuture cartInfoTask = Futures.whenAllSucceed(cartIdTask, customerNameTask, cartItemsTask)
+ .call(() -> {
+ int cartId = Futures.getDone(cartIdTask);
+ String customerName = Futures.getDone(customerNameTask);
+ List cartItems = Futures.getDone(cartItemsTask);
+ return new CartInfo(cartId, customerName, cartItems);
+ }, listeningExecService);
+
+ Futures.addCallback(cartInfoTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable CartInfo result) {
+ assertNotNull(result);
+ assertTrue(result.cartId >= 0);
+ assertFalse(result.customerName.isEmpty());
+ assertFalse(result.cartItems.isEmpty());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllComplete_thenSomeSuccess() {
+ ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture cartIdTask = service.getCartId();
+ ListenableFuture customerNameTask = service.failingTask();
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ ListenableFuture cartInfoTask = Futures.whenAllComplete(cartIdTask, customerNameTask, cartItemsTask)
+ .call(() -> {
+ Integer cartId = getOrNull(cartIdTask);
+ String customerName = getOrNull(customerNameTask);
+ List cartItems = getOrNull(cartItemsTask);
+ return new CartInfo(cartId, customerName, cartItems);
+ }, listeningExecService);
+
+ Futures.addCallback(cartInfoTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable CartInfo result) {
+ assertNotNull(result);
+ assertTrue(result.cartId >= 0);
+ assertNull(result.customerName);
+ assertFalse(result.cartItems.isEmpty());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void whenTransform_thenTransformSuccess() {
+ ListeningExecutorService listenExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listenExecService);
+
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ Function, Integer> itemCountFunc = cartItems -> {
+ assertNotNull(cartItems);
+ return cartItems.size();
+ };
+
+ ListenableFuture itemCountTask = Futures.transform(cartItemsTask, itemCountFunc, listenExecService);
+
+ Futures.addCallback(itemCountTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Integer cartItemCount) {
+ assertNotNull(cartItemCount);
+ assertTrue(cartItemCount > 0);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listenExecService);
+ }
+
+ @Test
+ public void whenSubmitAsync_thenSuccess() {
+ ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(executor);
+
+ AsyncCallable asyncConfigTask = () -> {
+ ListenableFuture configTask = service.fetchConfig("config.a");
+ TimeUnit.MILLISECONDS.sleep(500); //some long running task
+ return configTask;
+ };
+
+ ListenableFuture configTask = Futures.submitAsync(asyncConfigTask, executor);
+
+ Futures.addCallback(configTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable String result) {
+ assertNotNull(result);
+ assertTrue(result.contains("config.a"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, executor);
+ }
+
+ @Test
+ public void whenAsyncTransform_thenSuccess() {
+ ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(executor);
+
+ ListenableFuture usernameTask = service.generateUsername("john");
+ AsyncFunction passwordFunc = username -> {
+ ListenableFuture generatePasswordTask = service.generatePassword(username);
+ TimeUnit.MILLISECONDS.sleep(500); // some long running task
+ return generatePasswordTask;
+ };
+
+ ListenableFuture passwordTask = Futures.transformAsync(usernameTask, passwordFunc, executor);
+
+ Futures.addCallback(passwordTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable String password) {
+ assertNotNull(password);
+ assertTrue(password.contains("john"));
+ assertTrue(password.contains("@"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, executor);
+ }
+
+ private static T getOrNull(ListenableFuture future) {
+ try {
+ return Futures.getDone(future);
+ } catch (ExecutionException e) {
+ return null;
+ }
+ }
+
+ static class CartInfo {
+ Integer cartId;
+ String customerName;
+ List cartItems;
+
+ public CartInfo(Integer cartId, String customerName, List cartItems) {
+ this.cartId = cartId;
+ this.customerName = customerName;
+ this.cartItems = cartItems;
+ }
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java
new file mode 100644
index 0000000000..7dce11a33f
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java
@@ -0,0 +1,122 @@
+package com.baeldung.guava.future;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureSimpleUnitTest {
+
+ @Test
+ public void whenSubmitToListeningExecutor_thenSuccess() throws ExecutionException, InterruptedException {
+ ExecutorService execService = Executors.newSingleThreadExecutor();
+ ListeningExecutorService listeningExecService = MoreExecutors.listeningDecorator(execService);
+
+ ListenableFuture asyncTask = listeningExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500); // long running task
+ return 5;
+ });
+
+ assertEquals(5, asyncTask.get());
+ }
+
+ @Test
+ public void
+ givenJavaExecutor_whenSubmitListeningTask_thenSuccess() throws ExecutionException, InterruptedException {
+ Executor executor = Executors.newSingleThreadExecutor();
+ ListenableFutureService service = new ListenableFutureService();
+
+ FutureTask configFuture = service.fetchConfigTask("future.value");
+ executor.execute(configFuture);
+ assertTrue(configFuture.get().contains("future.value"));
+
+ ListenableFutureTask configListenableFuture =
+ service.fetchConfigListenableTask("listenable.value");
+ executor.execute(configListenableFuture);
+ assertTrue(configListenableFuture.get().contains("listenable.value"));
+ }
+
+ @Test
+ public void givenNonFailingTask_whenCallbackListen_thenSuccess() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ ListenableFuture succeedingTask = new ListenableFutureService().succeedingTask();
+ Futures.addCallback(succeedingTask, new FutureCallback() {
+ @Override
+ public void onSuccess(Integer result) {
+ assertNotNull(result);
+ assertTrue(result >= 0);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Succeeding task cannot failed", t);
+ }
+ }, listeningExecutor);
+ }
+
+ @Test
+ public void givenFailingTask_whenCallbackListen_thenThrows() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ ListenableFuture failingTask = new ListenableFutureService().failingTask();
+ Futures.addCallback(failingTask, new FutureCallback() {
+ @Override
+ public void onSuccess(Integer result) {
+ fail("Failing task cannot succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertTrue(t instanceof ListenableFutureException);
+ }
+ }, listeningExecutor);
+ }
+
+ @Test
+ public void givenNonFailingTask_whenDirectListen_thenListenerExecutes() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ int nextTask = 1;
+ Set runningTasks = ConcurrentHashMap.newKeySet();
+ runningTasks.add(nextTask);
+
+ ListenableFuture nonFailingTask = new ListenableFutureService().succeedingTask();
+ nonFailingTask.addListener(() -> runningTasks.remove(nextTask), listeningExecutor);
+
+ assertTrue(runningTasks.isEmpty());
+ }
+
+ @Test
+ public void givenFailingTask_whenDirectListen_thenListenerExecutes() {
+ final Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ int nextTask = 1;
+ Set runningTasks = ConcurrentHashMap.newKeySet();
+ runningTasks.add(nextTask);
+
+ final ListenableFuture failingTask = new ListenableFutureService().failingTask();
+ failingTask.addListener(() -> runningTasks.remove(nextTask),listeningExecutor);
+
+ assertTrue(runningTasks.isEmpty());
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/pom.xml b/guava-modules/pom.xml
index 957b8ad166..8ffac98b51 100644
--- a/guava-modules/pom.xml
+++ b/guava-modules/pom.xml
@@ -24,6 +24,7 @@
guava-collections-list
guava-collections-map
guava-collections-set
+ guava-concurrency
guava-io
diff --git a/httpclient-2/README.md b/httpclient-2/README.md
index 9d7a9683cd..7c2d5862bd 100644
--- a/httpclient-2/README.md
+++ b/httpclient-2/README.md
@@ -11,4 +11,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [How to Set TLS Version in Apache HttpClient](https://www.baeldung.com/apache-httpclient-tls)
- [Reading an HTTP Response Body as a String in Java](https://www.baeldung.com/java-http-response-body-as-string)
- [How To Get Cookies From the Apache HttpClient Response](https://www.baeldung.com/java-apache-httpclient-cookies)
+- [Enabling Logging for Apache HttpClient](https://www.baeldung.com/java-httpclient-enable-logging)
- More articles: [[<-- prev]](../httpclient)
diff --git a/httpclient-2/pom.xml b/httpclient-2/pom.xml
index 881b0407c3..85fc1d87e7 100644
--- a/httpclient-2/pom.xml
+++ b/httpclient-2/pom.xml
@@ -26,6 +26,11 @@
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${httpclient5.version}
+
org.springframework.boot
@@ -38,6 +43,12 @@
${spring-boot.version}
test
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ ${spring-boot.version}
+
@@ -63,6 +74,7 @@
4.5.8
+ 5.1
11
11
2.1.7.RELEASE
diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java
new file mode 100644
index 0000000000..35b21789ef
--- /dev/null
+++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java
@@ -0,0 +1,29 @@
+package com.baeldung.httpclient.readresponsebodystring;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class ApacheHttpClient5UnitTest {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ public static final String DUMMY_URL = "https://postman-echo.com/get";
+
+ @Test
+ public void whenUseApacheHttpClient_thenCorrect() throws IOException, ParseException {
+ HttpGet request = new HttpGet(DUMMY_URL);
+
+ try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) {
+ HttpEntity entity = response.getEntity();
+ logger.debug("Response -> {}", EntityUtils.toString(entity));
+ }
+ }
+}
\ No newline at end of file
diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
index 5a638b2bd5..7625b415f2 100644
--- a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
+++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
@@ -7,10 +7,13 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ApacheHttpClientUnitTest {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String DUMMY_URL = "https://postman-echo.com/get";
@Test
@@ -19,8 +22,7 @@ public class ApacheHttpClientUnitTest {
try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) {
HttpEntity entity = response.getEntity();
- String result = EntityUtils.toString(entity);
- System.out.println("Response -> " + result);
+ logger.debug("Response -> {}", EntityUtils.toString(entity));
}
}
}
diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java
new file mode 100644
index 0000000000..9bd2f825ad
--- /dev/null
+++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/SpringWebClientUnitTest.java
@@ -0,0 +1,17 @@
+package com.baeldung.httpclient.readresponsebodystring;
+
+import org.junit.Test;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+public class SpringWebClientUnitTest {
+ public static final String DUMMY_URL = "https://postman-echo.com/get";
+
+ @Test
+ public void whenUseWebClientRetrieve_thenCorrect() {
+ WebClient webClient = WebClient.create(DUMMY_URL);
+ Mono body = webClient.get().retrieve().bodyToMono(String.class);
+ String s = body.block();
+ System.out.println(s);
+ }
+}
diff --git a/httpclient-2/src/test/resources/logback.xml b/httpclient-2/src/test/resources/logback.xml
new file mode 100644
index 0000000000..366a94e86e
--- /dev/null
+++ b/httpclient-2/src/test/resources/logback.xml
@@ -0,0 +1,15 @@
+
+
+
+ %date [%level] %logger - %msg %n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/image-processing/README.md b/image-processing/README.md
index eba23f83eb..12b061bf41 100644
--- a/image-processing/README.md
+++ b/image-processing/README.md
@@ -8,3 +8,4 @@ This module contains articles about image processing.
- [Optical Character Recognition with Tesseract](https://www.baeldung.com/java-ocr-tesseract)
- [How Can I Resize an Image Using Java?](https://www.baeldung.com/java-resize-image)
- [Adding Text to an Image in Java](https://www.baeldung.com/java-add-text-to-image)
+- [Capturing Image From Webcam In Java](https://www.baeldung.com/java-capture-image-from-webcam)
diff --git a/image-processing/pom.xml b/image-processing/pom.xml
index ea218356ce..3780ecfd33 100644
--- a/image-processing/pom.xml
+++ b/image-processing/pom.xml
@@ -82,6 +82,16 @@
MarvinPlugins
${marvin-version}
+
+ org.bytedeco
+ javacv-platform
+ ${javacv-platform.version}
+
+
+ com.github.sarxos
+ webcam-capture
+ ${webcam-capture.version}
+
@@ -91,6 +101,8 @@
4.5.1
4.1.0-1.5.2
3.4.2-0
+ 1.5.5
+ 0.3.12
4.2
0.4.11
1.5.5
diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java
new file mode 100644
index 0000000000..67a46d9d59
--- /dev/null
+++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/MarvinExample.java
@@ -0,0 +1,31 @@
+package com.baeldung.imagefromwebcam;
+
+import marvin.gui.MarvinImagePanel;
+import marvin.image.MarvinImage;
+import marvin.io.MarvinImageIO;
+import marvin.video.MarvinJavaCVAdapter;
+import marvin.video.MarvinVideoInterface;
+import marvin.video.MarvinVideoInterfaceException;
+
+public class MarvinExample {
+
+ public static void main(String[] args) throws MarvinVideoInterfaceException {
+ MarvinVideoInterface videoAdapter = new MarvinJavaCVAdapter();
+ videoAdapter.connect(0);
+ MarvinImage image = videoAdapter.getFrame();
+ MarvinImageIO.saveImage(image, "selfie.jpg");
+ }
+
+ public void captureWithPanel() throws MarvinVideoInterfaceException {
+ MarvinVideoInterface videoAdapter = new MarvinJavaCVAdapter();
+ videoAdapter.connect(0);
+ MarvinImage image = videoAdapter.getFrame();
+
+ MarvinImagePanel imagePanel = new MarvinImagePanel();
+ imagePanel.setImage(image);
+
+ imagePanel.setSize(800,600);
+ imagePanel.setVisible(true);
+ }
+
+}
diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java
new file mode 100644
index 0000000000..3d9d1bcb00
--- /dev/null
+++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/OpenCVExample.java
@@ -0,0 +1,31 @@
+package com.baeldung.imagefromwebcam;
+
+import org.bytedeco.javacv.*;
+import org.bytedeco.opencv.opencv_core.IplImage;
+import java.awt.event.WindowEvent;
+import javax.swing.JFrame;
+import static org.bytedeco.opencv.helper.opencv_imgcodecs.cvSaveImage;
+
+public class OpenCVExample {
+
+ public static void main(String[] args) throws Exception {
+ CanvasFrame canvas = new CanvasFrame("Web Cam");
+ canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ FrameGrabber grabber = new OpenCVFrameGrabber(0);
+ OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
+
+ grabber.start();
+ Frame frame = grabber.grab();
+
+ IplImage img = converter.convert(frame);
+ cvSaveImage("selfie.jpg", img);
+
+ canvas.showImage(frame);
+
+ Thread.sleep(2000);
+
+ canvas.dispatchEvent(new WindowEvent(canvas, WindowEvent.WINDOW_CLOSING));
+ }
+
+}
diff --git a/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java b/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java
new file mode 100644
index 0000000000..8edde2a637
--- /dev/null
+++ b/image-processing/src/main/java/com/baeldung/imagefromwebcam/WebcamCaptureExample.java
@@ -0,0 +1,41 @@
+package com.baeldung.imagefromwebcam;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.JFrame;
+
+import com.github.sarxos.webcam.Webcam;
+import com.github.sarxos.webcam.WebcamPanel;
+import com.github.sarxos.webcam.WebcamResolution;
+import com.github.sarxos.webcam.util.ImageUtils;
+
+public class WebcamCaptureExample {
+
+ public static void main(String[] args) throws IOException, Exception {
+ Webcam webcam = Webcam.getDefault();
+ webcam.open();
+
+ BufferedImage image = webcam.getImage();
+
+ ImageIO.write(image, ImageUtils.FORMAT_JPG, new File("selfie.jpg"));
+ }
+
+ public void captureWithPanel() {
+ Webcam webcam = Webcam.getDefault();
+ webcam.setViewSize(WebcamResolution.VGA.getSize());
+
+ WebcamPanel panel = new WebcamPanel(webcam);
+ panel.setImageSizeDisplayed(true);
+
+ JFrame window = new JFrame("Webcam");
+ window.add(panel);
+ window.setResizable(true);
+ window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ window.pack();
+ window.setVisible(true);
+ }
+
+}
diff --git a/java-collections-conversions-2/README.md b/java-collections-conversions-2/README.md
index 628421b0e2..83b3ec8786 100644
--- a/java-collections-conversions-2/README.md
+++ b/java-collections-conversions-2/README.md
@@ -8,4 +8,5 @@ This module contains articles about conversions among Collection types and array
- [Mapping Lists with ModelMapper](https://www.baeldung.com/java-modelmapper-lists)
- [Converting List to Map With a Custom Supplier](https://www.baeldung.com/list-to-map-supplier)
- [Arrays.asList vs new ArrayList(Arrays.asList())](https://www.baeldung.com/java-arrays-aslist-vs-new-arraylist)
+- [Iterate Over a Set in Java](https://www.baeldung.com/java-iterate-set)
- More articles: [[<-- prev]](../java-collections-conversions)
diff --git a/java-collections-conversions-2/pom.xml b/java-collections-conversions-2/pom.xml
index 7845e0d934..a81b245b33 100644
--- a/java-collections-conversions-2/pom.xml
+++ b/java-collections-conversions-2/pom.xml
@@ -44,6 +44,11 @@
${hamcrest.version}
test
+
+ io.vavr
+ vavr
+ 0.10.3
+
diff --git a/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java b/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java
new file mode 100644
index 0000000000..ee0943ec1c
--- /dev/null
+++ b/java-collections-conversions-2/src/test/java/com/baeldung/setiteration/SetIteration.java
@@ -0,0 +1,74 @@
+package com.baeldung.setiteration;
+
+import com.google.common.collect.Sets;
+import io.vavr.collection.Stream;
+import org.junit.jupiter.api.Test;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+class SetIteration {
+
+ @Test
+ void givenSet_whenIteratorUsed_shouldIterateOverElements() {
+ // given
+ Set names = Sets.newHashSet("Tom", "Jane", "Karen");
+
+ // when
+ Iterator namesIterator1 = names.iterator();
+ Iterator namesIterator2 = names.iterator();
+
+ // then
+ namesIterator1.forEachRemaining(System.out::println);
+ while(namesIterator2.hasNext()) {
+ System.out.println(namesIterator2.next());
+ }
+ }
+
+ @Test
+ void givenSet_whenStreamUsed_shouldIterateOverElements() {
+ // given
+ Set names = Sets.newHashSet("Tom", "Jane", "Karen");
+
+ // when & then
+ String namesJoined = names.stream()
+ .map(String::toUpperCase)
+ .peek(System.out::println)
+ .collect(Collectors.joining());
+ }
+
+ @Test
+ void givenSet_whenEnhancedLoopUsed_shouldIterateOverElements() {
+ // given
+ Set names = Sets.newHashSet("Tom", "Jane", "Karen");
+
+ // when & then
+ for (String name : names) {
+ System.out.println(name);
+ }
+ }
+
+ @Test
+ void givenSet_whenMappedToArray_shouldIterateOverElements() {
+ // given
+ Set names = Sets.newHashSet("Tom", "Jane", "Karen");
+
+ // when & then
+ Object[] namesArray = names.toArray();
+ for (int i = 0; i < namesArray.length; i++) {
+ System.out.println(i + ": " + namesArray[i]);
+ }
+ }
+
+ @Test
+ void givenSet_whenZippedWithIndex_shouldIterateOverElements() {
+ // given
+ Set names = Sets.newHashSet("Tom", "Jane", "Karen");
+
+ // when & then
+ Stream.ofAll(names)
+ .zipWithIndex()
+ .forEach(t -> System.out.println(t._2() + ": " + t._1()));
+ }
+}
diff --git a/java-collections-conversions/pom.xml b/java-collections-conversions/pom.xml
index 92fc66b480..e76181c9af 100644
--- a/java-collections-conversions/pom.xml
+++ b/java-collections-conversions/pom.xml
@@ -41,5 +41,5 @@
4.1
-
+
\ No newline at end of file
diff --git a/java-collections-maps-3/pom.xml b/java-collections-maps-3/pom.xml
index 37a0f617d0..0cdf24e31b 100644
--- a/java-collections-maps-3/pom.xml
+++ b/java-collections-maps-3/pom.xml
@@ -40,4 +40,5 @@
3.6.1
5.2.5.RELEASE
+
\ No newline at end of file
diff --git a/java-jdi/pom.xml b/java-jdi/pom.xml
index 978ede604b..a8716de4ee 100644
--- a/java-jdi/pom.xml
+++ b/java-jdi/pom.xml
@@ -1,7 +1,7 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
java-jdi
0.1.0-SNAPSHOT
@@ -39,4 +39,4 @@
1.8
-
+
\ No newline at end of file
diff --git a/java-lite/pom.xml b/java-lite/pom.xml
index 7ff50a8fd3..c422e9a421 100644
--- a/java-lite/pom.xml
+++ b/java-lite/pom.xml
@@ -1,6 +1,7 @@
-
+
4.0.0
org.baeldung
java-lite
diff --git a/java-numbers-4/README.md b/java-numbers-4/README.md
index cdd53692e0..f053a82b80 100644
--- a/java-numbers-4/README.md
+++ b/java-numbers-4/README.md
@@ -3,3 +3,4 @@
- [Probability in Java](https://www.baeldung.com/java-probability)
- [Understanding the & 0xff Value in Java](https://www.baeldung.com/java-and-0xff)
- [Determine if an Integer’s Square Root Is an Integer in Java](https://www.baeldung.com/java-find-if-square-root-is-integer)
+- [Guide to Java BigInteger](https://www.baeldung.com/java-biginteger)
diff --git a/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java
new file mode 100644
index 0000000000..4ac185984d
--- /dev/null
+++ b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.biginteger;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BigIntegerUnitTest {
+
+ @Test
+ void givenPositiveAndNegativeAndZeroBigInteger_whenGetSigNumValue_shouldReturnOneAndMinusOneAndZero() {
+ assertEquals(1, BigInteger.TEN.signum());
+ assertEquals(-1, BigInteger.TEN.negate().signum());
+ assertEquals(0, BigInteger.ZERO.signum());
+ }
+
+ @Test
+ void givenByteArrays_whenCreateBigInteger_shouldTranslateToTwosComplementBinary() {
+ assertEquals(new BigInteger("1"), new BigInteger(new byte[]{0b1}));
+ assertEquals(new BigInteger("2"), new BigInteger(new byte[]{0b10}));
+ assertEquals(new BigInteger("4"), new BigInteger(new byte[]{0b100}));
+ }
+
+ @Test
+ void givenSingleByte_whenCreateBigIntegerAndDifferentSigNum_thenOppositeValues() {
+ byte[] bytes = { (byte) -128 }; // 0b1000_0000
+
+ BigInteger positive = new BigInteger(1, bytes);
+ BigInteger negative = new BigInteger(-1, bytes);
+
+ assertEquals(new BigInteger("128"), positive);
+ assertEquals("10000000", positive.toString(2));
+
+ assertEquals(new BigInteger("-128"), negative);
+ assertEquals("-10000000", negative.toString(2));
+ }
+
+ @Test
+ void givenZeroBigInteger_whenCheckMagnitude_thenIsEmpty() {
+ assertEquals(0, BigInteger.ZERO.bitCount());
+ assertEquals(BigInteger.ZERO, new BigInteger(0, new byte[]{}));
+ }
+
+ @Test
+ void given63rdBitSet_whenCreateBigInteger_thenIsLargerThanLongByOne() {
+ // first
+ BigInteger bi1 = BigInteger.ZERO.setBit(63);
+ String str1 = bi1.toString(2);
+
+ // second
+ byte[] bytes = ByteBuffer.allocate(Long.BYTES).putLong(Long.MIN_VALUE).array();
+ BigInteger bi2 = new BigInteger(1, bytes);
+ String str2 = bi2.toString(2);
+
+ largerThanLongAssertionSet(bi1, str1);
+
+ assertEquals(bi1, bi2);
+
+ largerThanLongAssertionSet(bi2, str2);
+
+ }
+
+ private static void largerThanLongAssertionSet(BigInteger bi, String str)
+ {
+ assertEquals(64, bi.bitLength());
+ assertEquals(1, bi.signum());
+ assertEquals("9223372036854775808", bi.toString());
+ assertEquals(BigInteger.ONE, bi.subtract(BigInteger.valueOf(Long.MAX_VALUE)));
+
+ assertEquals(64, str.length());
+ assertTrue(str.matches("^10{63}$")); // 1000 0000
+ }
+}
diff --git a/jee-7/pom.xml b/jee-7/pom.xml
index b68c8b9801..eb23a3e150 100644
--- a/jee-7/pom.xml
+++ b/jee-7/pom.xml
@@ -485,23 +485,9 @@
-
-
- bintray-mvc-spec-maven
- bintray
- http://dl.bintray.com/mvc-spec/maven
-
- true
-
-
- false
-
-
-
-
1.0.0
- 20160715
+ 1.0-edr2
1.8
3.0.0
7.0
@@ -523,7 +509,7 @@
2.1.0.Final
2.8
2.2
- 20160715
+ 1.0.0-m02
1.0.0.Final
1.0.2.Final
1.0.0.Final
diff --git a/jenkins/plugins/pom.xml b/jenkins/plugins/pom.xml
index c4caf5567f..3662d59980 100644
--- a/jenkins/plugins/pom.xml
+++ b/jenkins/plugins/pom.xml
@@ -1,6 +1,7 @@
-
+
4.0.0
plugins
1.0-SNAPSHOT
@@ -12,7 +13,7 @@
org.jenkins-ci.plugins
plugin
2.33
-
+
@@ -80,9 +81,9 @@
-
+
2.7.3
-
1.7
2.12
2.39
@@ -93,4 +94,4 @@
2.14
-
+
\ No newline at end of file
diff --git a/json-2/pom.xml b/json-2/pom.xml
index fbae40b6a3..733fbc6668 100644
--- a/json-2/pom.xml
+++ b/json-2/pom.xml
@@ -14,6 +14,11 @@
+
+ org.jsonschema2pojo
+ jsonschema2pojo-core
+ 1.1.1
+
com.jsoniter
jsoniter
@@ -46,7 +51,6 @@
commons-lang3
${commons-lang3.version}
-
com.fasterxml.jackson.core
jackson-annotations
diff --git a/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java b/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java
new file mode 100644
index 0000000000..65eecd0d38
--- /dev/null
+++ b/json-2/src/main/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversion.java
@@ -0,0 +1,42 @@
+package com.baeldung.jsontojavaclass;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import org.jsonschema2pojo.DefaultGenerationConfig;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.Jackson2Annotator;
+import org.jsonschema2pojo.SchemaGenerator;
+import org.jsonschema2pojo.SchemaMapper;
+import org.jsonschema2pojo.SchemaStore;
+import org.jsonschema2pojo.SourceType;
+import org.jsonschema2pojo.rules.RuleFactory;
+
+import com.sun.codemodel.JCodeModel;
+
+public class JsonToJavaClassConversion {
+
+ public Object convertJsonToJavaClass(URL inputJson, File outputJavaClassDirectory, String packageName, String className) throws IOException {
+ JCodeModel jcodeModel = new JCodeModel();
+
+ GenerationConfig config = new DefaultGenerationConfig() {
+ @Override
+ public boolean isGenerateBuilders() {
+ return true;
+ }
+
+ @Override
+ public SourceType getSourceType() {
+ return SourceType.JSON;
+ }
+ };
+
+ SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator());
+ mapper.generate(jcodeModel, className, packageName, inputJson);
+
+ jcodeModel.build(outputJavaClassDirectory);
+ return mapper;
+ }
+
+}
diff --git a/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java b/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java
new file mode 100644
index 0000000000..4c8a719de0
--- /dev/null
+++ b/json-2/src/main/java/com/baeldung/jsontojavaclass/pojo/SamplePojo.java
@@ -0,0 +1,214 @@
+
+package com.baeldung.jsontojavaclass.pojo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Generated;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonPropertyOrder({ "name", "area", "author", "id", "salary", "topics" })
+@Generated("jsonschema2pojo")
+public class SamplePojo {
+
+ @JsonProperty("name")
+ private String name;
+ @JsonProperty("area")
+ private String area;
+ @JsonProperty("author")
+ private String author;
+ @JsonProperty("id")
+ private Integer id;
+ @JsonProperty("salary")
+ private Integer salary;
+ @JsonProperty("topics")
+ private List topics = new ArrayList();
+ @JsonIgnore
+ private Map additionalProperties = new HashMap();
+
+ @JsonProperty("name")
+ public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public SamplePojo withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ @JsonProperty("area")
+ public String getArea() {
+ return area;
+ }
+
+ @JsonProperty("area")
+ public void setArea(String area) {
+ this.area = area;
+ }
+
+ public SamplePojo withArea(String area) {
+ this.area = area;
+ return this;
+ }
+
+ @JsonProperty("author")
+ public String getAuthor() {
+ return author;
+ }
+
+ @JsonProperty("author")
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public SamplePojo withAuthor(String author) {
+ this.author = author;
+ return this;
+ }
+
+ @JsonProperty("id")
+ public Integer getId() {
+ return id;
+ }
+
+ @JsonProperty("id")
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public SamplePojo withId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ @JsonProperty("salary")
+ public Integer getSalary() {
+ return salary;
+ }
+
+ @JsonProperty("salary")
+ public void setSalary(Integer salary) {
+ this.salary = salary;
+ }
+
+ public SamplePojo withSalary(Integer salary) {
+ this.salary = salary;
+ return this;
+ }
+
+ @JsonProperty("topics")
+ public List getTopics() {
+ return topics;
+ }
+
+ @JsonProperty("topics")
+ public void setTopics(List topics) {
+ this.topics = topics;
+ }
+
+ public SamplePojo withTopics(List topics) {
+ this.topics = topics;
+ return this;
+ }
+
+ @JsonAnyGetter
+ public Map getAdditionalProperties() {
+ return this.additionalProperties;
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ }
+
+ public SamplePojo withAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(SamplePojo.class.getName())
+ .append('@')
+ .append(Integer.toHexString(System.identityHashCode(this)))
+ .append('[');
+ sb.append("name");
+ sb.append('=');
+ sb.append(((this.name == null) ? "" : this.name));
+ sb.append(',');
+ sb.append("area");
+ sb.append('=');
+ sb.append(((this.area == null) ? "" : this.area));
+ sb.append(',');
+ sb.append("author");
+ sb.append('=');
+ sb.append(((this.author == null) ? "" : this.author));
+ sb.append(',');
+ sb.append("id");
+ sb.append('=');
+ sb.append(((this.id == null) ? "" : this.id));
+ sb.append(',');
+ sb.append("salary");
+ sb.append('=');
+ sb.append(((this.salary == null) ? "" : this.salary));
+ sb.append(',');
+ sb.append("topics");
+ sb.append('=');
+ sb.append(((this.topics == null) ? "" : this.topics));
+ sb.append(',');
+ sb.append("additionalProperties");
+ sb.append('=');
+ sb.append(((this.additionalProperties == null) ? "" : this.additionalProperties));
+ sb.append(',');
+ if (sb.charAt((sb.length() - 1)) == ',') {
+ sb.setCharAt((sb.length() - 1), ']');
+ } else {
+ sb.append(']');
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = ((result * 31) + ((this.area == null) ? 0 : this.area.hashCode()));
+ result = ((result * 31) + ((this.author == null) ? 0 : this.author.hashCode()));
+ result = ((result * 31) + ((this.topics == null) ? 0 : this.topics.hashCode()));
+ result = ((result * 31) + ((this.name == null) ? 0 : this.name.hashCode()));
+ result = ((result * 31) + ((this.id == null) ? 0 : this.id.hashCode()));
+ result = ((result * 31) + ((this.additionalProperties == null) ? 0 : this.additionalProperties.hashCode()));
+ result = ((result * 31) + ((this.salary == null) ? 0 : this.salary.hashCode()));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if ((other instanceof SamplePojo) == false) {
+ return false;
+ }
+ SamplePojo rhs = ((SamplePojo) other);
+ return ((((((((this.area == rhs.area) || ((this.area != null) && this.area.equals(rhs.area))) && ((this.author == rhs.author) || ((this.author != null) && this.author.equals(rhs.author))))
+ && ((this.topics == rhs.topics) || ((this.topics != null) && this.topics.equals(rhs.topics)))) && ((this.name == rhs.name) || ((this.name != null) && this.name.equals(rhs.name))))
+ && ((this.id == rhs.id) || ((this.id != null) && this.id.equals(rhs.id)))) && ((this.additionalProperties == rhs.additionalProperties) || ((this.additionalProperties != null) && this.additionalProperties.equals(rhs.additionalProperties))))
+ && ((this.salary == rhs.salary) || ((this.salary != null) && this.salary.equals(rhs.salary))));
+ }
+
+}
diff --git a/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java b/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java
new file mode 100644
index 0000000000..8dbfb14b45
--- /dev/null
+++ b/json-2/src/test/java/com/baeldung/jsontojavaclass/JsonToJavaClassConversionUnitTest.java
@@ -0,0 +1,38 @@
+package com.baeldung.jsontojavaclass;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class JsonToJavaClassConversionUnitTest {
+
+ private JsonToJavaClassConversion jsonToJavaConversion = new JsonToJavaClassConversion();
+
+ @Test
+ void whenProvideInputJSON_thenGenerateJavaClass() throws MalformedURLException, IOException {
+
+ String packageName = "com.baeldung.jsontojavaclass.pojo";
+
+ // load input JSON file
+ String jsonPath = "src/test/resources/";
+ File inputJson = new File(jsonPath + "sample_input.json");
+
+ // create the local directory for generating the Java Class file
+ String outputPath = "src/main/java/";
+ File outputJavaClassDirectory = new File(outputPath);
+ outputJavaClassDirectory.mkdirs();
+
+ String className = "SamplePojo";
+
+ Object object = jsonToJavaConversion.convertJsonToJavaClass(inputJson.toURI()
+ .toURL(), outputJavaClassDirectory, packageName, className);
+ System.out.println(object);
+
+ Assertions.assertNotNull(object);
+
+ }
+
+}
diff --git a/json-2/src/test/resources/sample_input.json b/json-2/src/test/resources/sample_input.json
new file mode 100644
index 0000000000..b6e90da357
--- /dev/null
+++ b/json-2/src/test/resources/sample_input.json
@@ -0,0 +1,13 @@
+{
+ "name": "Baeldung",
+ "area": "tech blogs",
+ "author": "Eugen",
+ "id": 32134,
+ "salary": 70000,
+ "topics": [
+ "java",
+ "kotlin",
+ "cs",
+ "linux"
+ ]
+}
\ No newline at end of file
diff --git a/jta/pom.xml b/jta/pom.xml
index b8b5570226..1937f55a20 100644
--- a/jta/pom.xml
+++ b/jta/pom.xml
@@ -36,4 +36,8 @@
+
+ 2.4.7
+
+
\ No newline at end of file
diff --git a/ksqldb/pom.xml b/ksqldb/pom.xml
new file mode 100644
index 0000000000..13867b16e3
--- /dev/null
+++ b/ksqldb/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+ ksqldb-app
+ 0.0.1-SNAPSHOT
+ ksqldb
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+ confluent
+ confluent-repo
+ http://packages.confluent.io/maven/
+
+
+
+
+
+ io.confluent.ksql
+ ksqldb-api-client
+ ${ksqldb.version}
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+ org.testcontainers
+ testcontainers
+ ${testcontainers.version}
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers.version}
+ test
+
+
+
+
+ 6.2.0
+ 3.20.2
+ 4.1.0
+ 1.15.3
+
+
+
diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java b/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java
new file mode 100644
index 0000000000..badb00f114
--- /dev/null
+++ b/ksqldb/src/main/java/com/baeldung/ksqldb/Alert.java
@@ -0,0 +1,29 @@
+package com.baeldung.ksqldb;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Alert {
+
+ @JsonProperty(value = "SENSOR_ID")
+ private String sensorId;
+
+ @JsonProperty(value = "START_PERIOD")
+ private String startPeriod;
+
+ @JsonProperty(value = "END_PERIOD")
+ private String endPeriod;
+
+ @JsonProperty(value = "AVERAGE_READING")
+ private double averageReading;
+
+}
diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java b/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java
new file mode 100644
index 0000000000..35ad3ebbb0
--- /dev/null
+++ b/ksqldb/src/main/java/com/baeldung/ksqldb/KsqlDBApplication.java
@@ -0,0 +1,75 @@
+package com.baeldung.ksqldb;
+
+import io.confluent.ksql.api.client.Client;
+import io.confluent.ksql.api.client.ExecuteStatementResult;
+import io.confluent.ksql.api.client.KsqlObject;
+import io.confluent.ksql.api.client.Row;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Subscriber;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+@AllArgsConstructor
+@Slf4j
+public class KsqlDBApplication {
+
+ private static final String CREATE_READINGS_STREAM = ""
+ + " CREATE STREAM readings (sensor_id VARCHAR KEY, timestamp VARCHAR, reading INT)"
+ + " WITH (KAFKA_TOPIC = 'readings',"
+ + " VALUE_FORMAT = 'JSON',"
+ + " TIMESTAMP = 'timestamp',"
+ + " TIMESTAMP_FORMAT = 'yyyy-MM-dd HH:mm:ss',"
+ + " PARTITIONS = 1);";
+
+ private static final String CREATE_ALERTS_TABLE = ""
+ + " CREATE TABLE alerts AS"
+ + " SELECT"
+ + " sensor_id,"
+ + " TIMESTAMPTOSTRING(WINDOWSTART, 'yyyy-MM-dd HH:mm:ss', 'UTC') AS start_period,"
+ + " TIMESTAMPTOSTRING(WINDOWEND, 'yyyy-MM-dd HH:mm:ss', 'UTC') AS end_period,"
+ + " AVG(reading) AS average_reading"
+ + " FROM readings"
+ + " WINDOW TUMBLING (SIZE 30 MINUTES)"
+ + " GROUP BY sensor_id"
+ + " HAVING AVG(reading) > 25"
+ + " EMIT CHANGES;";
+
+ private static final String ALERTS_QUERY = "SELECT * FROM alerts EMIT CHANGES;";
+
+ private static final String READINGS_STREAM = "readings";
+
+ private static final Map PROPERTIES = Collections.singletonMap("auto.offset.reset", "earliest");
+
+ private final Client client;
+
+ public CompletableFuture createReadingsStream() {
+ return client.executeStatement(CREATE_READINGS_STREAM, PROPERTIES);
+ }
+
+ public CompletableFuture createAlertsTable() {
+ return client.executeStatement(CREATE_ALERTS_TABLE, PROPERTIES);
+ }
+
+ public CompletableFuture insert(Collection rows) {
+ return CompletableFuture.allOf(
+ rows.stream()
+ .map(row -> client.insertInto(READINGS_STREAM, row))
+ .toArray(CompletableFuture[]::new)
+ );
+ }
+
+ public CompletableFuture subscribeOnAlerts(Subscriber subscriber) {
+ return client.streamQuery(ALERTS_QUERY, PROPERTIES)
+ .thenAccept(streamedQueryResult -> streamedQueryResult.subscribe(subscriber))
+ .whenComplete((result, ex) -> {
+ if (ex != null) {
+ log.error("Alerts push query failed", ex);
+ }
+ });
+ }
+
+}
diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java b/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java
new file mode 100644
index 0000000000..8964ff5801
--- /dev/null
+++ b/ksqldb/src/main/java/com/baeldung/ksqldb/Reading.java
@@ -0,0 +1,10 @@
+package com.baeldung.ksqldb;
+
+import lombok.Data;
+
+@Data
+public class Reading {
+ private String id;
+ private String timestamp;
+ private int reading;
+}
diff --git a/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java b/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java
new file mode 100644
index 0000000000..7d09583a39
--- /dev/null
+++ b/ksqldb/src/main/java/com/baeldung/ksqldb/RowSubscriber.java
@@ -0,0 +1,60 @@
+package com.baeldung.ksqldb;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.confluent.ksql.api.client.Row;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+public class RowSubscriber implements Subscriber {
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private final Class clazz;
+
+ private Subscription subscription;
+
+ public List consumedItems = new ArrayList<>();
+
+ public RowSubscriber(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public synchronized void onSubscribe(Subscription subscription) {
+ log.info("Subscriber is subscribed.");
+ this.subscription = subscription;
+ subscription.request(1);
+ }
+
+ @Override
+ public synchronized void onNext(Row row) {
+ String jsonString = row.asObject().toJsonString();
+ log.info("Row JSON: {}", jsonString);
+ try {
+ T item = OBJECT_MAPPER.readValue(jsonString, this.clazz);
+ log.info("Item: {}", item);
+ consumedItems.add(item);
+ } catch (JsonProcessingException e) {
+ log.error("Unable to parse json", e);
+ }
+
+ // Request the next row
+ subscription.request(1);
+ }
+
+ @Override
+ public synchronized void onError(Throwable t) {
+ log.error("Received an error", t);
+ }
+
+ @Override
+ public synchronized void onComplete() {
+ log.info("Query has ended.");
+ }
+}
diff --git a/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java b/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java
new file mode 100644
index 0000000000..f13f418048
--- /dev/null
+++ b/ksqldb/src/test/java/com/baeldung/ksqldb/KsqlDBApplicationLiveTest.java
@@ -0,0 +1,160 @@
+package com.baeldung.ksqldb;
+
+import io.confluent.ksql.api.client.Client;
+import io.confluent.ksql.api.client.ClientOptions;
+import io.confluent.ksql.api.client.KsqlObject;
+import io.confluent.ksql.api.client.QueryInfo;
+import io.confluent.ksql.api.client.QueryInfo.QueryType;
+import io.confluent.ksql.api.client.Row;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.DockerComposeContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+import static org.awaitility.Awaitility.given;
+
+@Testcontainers
+class KsqlDBApplicationLiveTest {
+
+ private static final File KSQLDB_COMPOSE_FILE = new File("src/test/resources/docker/docker-compose.yml");
+
+ private static final Map PROPERTIES = Collections.singletonMap("auto.offset.reset", "earliest");
+
+ private static final String KSQLDB_SERVER_HOST = "localhost";
+ private static final int KSQLDB_SERVER_PORT = 8088;
+
+ @Container
+ public static DockerComposeContainer dockerComposeContainer =
+ new DockerComposeContainer<>(KSQLDB_COMPOSE_FILE)
+ .withServices("zookeeper", "broker", "ksqldb-server")
+ .withExposedService("ksqldb-server", 8088,
+ Wait.forHealthcheck().withStartupTimeout(Duration.ofMinutes(5)))
+ .withLocalCompose(true);
+
+ private KsqlDBApplication ksqlDBApplication;
+
+ private Client client;
+
+ @BeforeEach
+ void setup() {
+ ClientOptions options = ClientOptions.create()
+ .setHost(KSQLDB_SERVER_HOST)
+ .setPort(KSQLDB_SERVER_PORT);
+ client = Client.create(options);
+
+ ksqlDBApplication = new KsqlDBApplication(client);
+ }
+
+ @AfterEach
+ void tearDown() {
+ deleteAlerts();
+ }
+
+ @Test
+ void givenSensorReadings_whenSubscribedToAlerts_thenAlertsAreConsumed() {
+ createAlertsMaterializedView();
+ RowSubscriber alertSubscriber = new RowSubscriber<>(Alert.class);
+
+ CompletableFuture result = ksqlDBApplication.subscribeOnAlerts(alertSubscriber);
+ insertSampleData();
+
+ assertThat(result).isNotNull();
+ await().atMost(Duration.ofMinutes(3)).untilAsserted(() ->
+ assertThat(alertSubscriber.consumedItems)
+ .containsOnly(
+ expectedAlert("sensor-1", "2021-08-01 09:30:00", "2021-08-01 10:00:00", 28.0),
+ expectedAlert("sensor-2", "2021-08-01 10:00:00", "2021-08-01 10:30:00", 26.0)
+ )
+ );
+ }
+
+ @Test
+ void givenSensorReadings_whenPullQueryForRow_thenRowIsReturned() {
+ createAlertsMaterializedView();
+ insertSampleData();
+
+ String pullQuery = "SELECT * FROM alerts WHERE sensor_id = 'sensor-2';";
+
+ given().ignoreExceptions()
+ .await().atMost(Duration.ofMinutes(1))
+ .untilAsserted(() -> {
+ // it may be possible that the materialized view is not updated with sample data yet
+ // so ignore TimeoutException and try again
+ List rows = client.executeQuery(pullQuery, PROPERTIES)
+ .get(10, TimeUnit.SECONDS);
+
+ assertThat(rows).hasSize(1);
+
+ Row row = rows.get(0);
+ assertThat(row.getString("SENSOR_ID")).isEqualTo("sensor-2");
+ assertThat(row.getString("START_PERIOD")).isEqualTo("2021-08-01 10:00:00");
+ assertThat(row.getString("END_PERIOD")).isEqualTo("2021-08-01 10:30:00");
+ assertThat(row.getDouble("AVERAGE_READING")).isEqualTo(26.0);
+ });
+ }
+
+ private void createAlertsMaterializedView() {
+ ksqlDBApplication.createReadingsStream().join();
+ ksqlDBApplication.createAlertsTable().join();
+ }
+
+ private void insertSampleData() {
+ ksqlDBApplication.insert(
+ Arrays.asList(
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:00:00").put("reading", 22),
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:10:00").put("reading", 20),
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:20:00").put("reading", 20),
+
+ // these reading will exceed the alert threshold (sensor-1)
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:30:00").put("reading", 24),
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:40:00").put("reading", 30),
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 09:50:00").put("reading", 30),
+
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 10:00:00").put("reading", 24),
+
+ // these reading will exceed the alert threshold (sensor-2)
+ new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:00:00").put("reading", 26),
+ new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:10:00").put("reading", 26),
+ new KsqlObject().put("sensor_id", "sensor-2").put("timestamp", "2021-08-01 10:20:00").put("reading", 26),
+
+ new KsqlObject().put("sensor_id", "sensor-1").put("timestamp", "2021-08-01 10:30:00").put("reading", 24)
+ )
+ ).join();
+ }
+
+ private void deleteAlerts() {
+ client.listQueries()
+ .thenApply(queryInfos -> queryInfos.stream()
+ .filter(queryInfo -> queryInfo.getQueryType() == QueryType.PERSISTENT)
+ .map(QueryInfo::getId)
+ .findFirst()
+ .orElseThrow(() -> new RuntimeException("Persistent query not found")))
+ .thenCompose(id -> client.executeStatement("TERMINATE " + id + ";"))
+ .thenCompose(result -> client.executeStatement("DROP TABLE alerts DELETE TOPIC;"))
+ .thenCompose(result -> client.executeStatement("DROP STREAM readings DELETE TOPIC;"))
+ .join();
+ }
+
+ private Alert expectedAlert(String sensorId, String startPeriod, String endPeriod, double average) {
+ return Alert.builder()
+ .sensorId(sensorId)
+ .startPeriod(startPeriod)
+ .endPeriod(endPeriod)
+ .averageReading(average)
+ .build();
+ }
+}
diff --git a/ksqldb/src/test/resources/docker/docker-compose.yml b/ksqldb/src/test/resources/docker/docker-compose.yml
new file mode 100644
index 0000000000..c90fe85e45
--- /dev/null
+++ b/ksqldb/src/test/resources/docker/docker-compose.yml
@@ -0,0 +1,49 @@
+---
+version: '3'
+
+services:
+ zookeeper:
+ image: confluentinc/cp-zookeeper:6.2.0
+ hostname: zookeeper
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2181
+ ZOOKEEPER_TICK_TIME: 2000
+
+ broker:
+ image: confluentinc/cp-kafka:6.2.0
+ hostname: broker
+ depends_on:
+ - zookeeper
+ environment:
+ KAFKA_BROKER_ID: 1
+ KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:9092,PLAINTEXT_HOST://localhost:29092
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
+
+ ksqldb-server:
+ image: confluentinc/ksqldb-server:0.19.0
+ hostname: ksqldb-server
+ depends_on:
+ - broker
+ ports:
+ - "8088:8088"
+ healthcheck:
+ test: curl -f http://ksqldb-server:8088/ || exit 1
+ environment:
+ KSQL_LISTENERS: http://0.0.0.0:8088
+ KSQL_BOOTSTRAP_SERVERS: broker:9092
+ KSQL_KSQL_LOGGING_PROCESSING_STREAM_AUTO_CREATE: "true"
+ KSQL_KSQL_LOGGING_PROCESSING_TOPIC_AUTO_CREATE: "true"
+
+ ksqldb-cli:
+ image: confluentinc/ksqldb-cli:0.19.0
+ hostname: ksqldb-cli
+ depends_on:
+ - broker
+ - ksqldb-server
+ entrypoint: /bin/sh
+ tty: true
diff --git a/ksqldb/src/test/resources/log4j.properties b/ksqldb/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..31a98608fb
--- /dev/null
+++ b/ksqldb/src/test/resources/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootLogger=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n
\ No newline at end of file
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml
new file mode 100644
index 0000000000..d463dd6dee
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml
@@ -0,0 +1,54 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: web-app-deployment
+spec:
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 2
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app: web-app
+ replicas: 3
+ template:
+ metadata:
+ labels:
+ app: web-app
+ spec:
+ containers:
+ - name: web-app
+ image: hello-world:nanoserver-1809
+ volumeMounts:
+ - name: counter
+ mountPath: /app/
+ volumes:
+ - name: counter
+ persistentVolumeClaim:
+ claimName: counter
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: counter
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 50Mi
+ storageClassName: default
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: web-app-service
+spec:
+ ports:
+ - name: http
+ port: 80
+ nodePort: 30080
+ selector:
+ name: web-app
+ type: NodePort
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml
new file mode 100644
index 0000000000..99bbb5d555
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: counter
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 50Mi
+ storageClassName: default
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml
new file mode 100644
index 0000000000..eabb12c0e0
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: web-app-service
+spec:
+ ports:
+ - name: http
+ port: 80
+ nodePort: 30080
+ selector:
+ name: web-app
+ type: NodePort
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml
new file mode 100644
index 0000000000..59f137cff1
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: myclaim
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 5Gi
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml
new file mode 100644
index 0000000000..80e5b50665
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx
+ labels:
+ app: nginx
+spec:
+ ports:
+ - port: 80
+ name: web
+ clusterIP: None
+ selector:
+ app: nginx
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml
new file mode 100644
index 0000000000..4ff4baca8c
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml
@@ -0,0 +1,28 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ selector:
+ matchLabels:
+ app: nginx
+ serviceName: "nginx"
+ replicas: 3
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx
+ ports:
+ - containerPort: 80
+ name: web
+ volumeMounts:
+ - name: www
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: www
+ persistentVolumeClaim:
+ claimName: myclaim
diff --git a/kubernetes/k8s-admission-controller/Dockerfile b/kubernetes/k8s-admission-controller/Dockerfile
new file mode 100644
index 0000000000..f8939ee7c8
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/Dockerfile
@@ -0,0 +1,11 @@
+FROM adoptopenjdk:11-jre-hotspot as builder
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} application.jar
+RUN java -Djarmode=layertools -jar application.jar extract
+
+FROM adoptopenjdk:11-jre-hotspot
+COPY --from=builder dependencies/ ./
+COPY --from=builder snapshot-dependencies/ ./
+COPY --from=builder spring-boot-loader/ ./
+COPY --from=builder application/ ./
+ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/README.md b/kubernetes/k8s-admission-controller/README.md
new file mode 100644
index 0000000000..c446ab403d
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Creating a Kubertes Admission Controller in Java](https://www.baeldung.com/java-kubernetes-admission-controller)
diff --git a/kubernetes/k8s-admission-controller/pom.xml b/kubernetes/k8s-admission-controller/pom.xml
new file mode 100644
index 0000000000..18bf98a830
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+ k8s-admission-controller
+ k8s-admission-controller
+ Demo project for Spring Boot
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ./../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ io.projectreactor
+ reactor-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+ com.baeldung.kubernetes.admission.Application
+
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java
new file mode 100644
index 0000000000..1260ec0af9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java
@@ -0,0 +1,17 @@
+package com.baeldung.kubernetes.admission;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+
+@SpringBootApplication
+@EnableConfigurationProperties(AdmissionControllerProperties.class)
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java
new file mode 100644
index 0000000000..f9103d85ba
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@ConfigurationProperties(prefix = "admission-controller")
+@Data
+public class AdmissionControllerProperties {
+
+ private boolean disabled;
+ private String annotation = "com.baeldung/wait-for-it";
+ private String waitForItImage = "willwill/wait-for-it";
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java
new file mode 100644
index 0000000000..21434535f9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java
@@ -0,0 +1,27 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.controller;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.baeldung.kubernetes.admission.service.AdmissionService;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.RequiredArgsConstructor;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequiredArgsConstructor
+public class AdmissionReviewController {
+
+ private final AdmissionService admissionService;
+
+ @PostMapping(path = "/mutate")
+ public Mono processAdmissionReviewRequest(@RequestBody Mono request) {
+ return request.map((body) -> admissionService.processAdmission(body));
+ }
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java
new file mode 100644
index 0000000000..6d590ff408
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * Result sent to the API server after reviewing and, possibly
+ * modifying the incoming request
+ */
+@Builder
+@Data
+public class AdmissionReviewData {
+
+ final String uid;
+ final boolean allowed;
+
+ @JsonInclude(Include.NON_NULL)
+ final String patchType;
+
+ @JsonInclude(Include.NON_NULL)
+ final String patch;
+
+ @JsonInclude(Include.NON_NULL)
+ final AdmissionStatus status;
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java
new file mode 100644
index 0000000000..07a3fa5b7a
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java
@@ -0,0 +1,23 @@
+package com.baeldung.kubernetes.admission.dto;
+
+public class AdmissionReviewException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+ private final int code;
+
+ public AdmissionReviewException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public AdmissionReviewException(String message) {
+ super(message);
+ this.code = 400;
+
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java
new file mode 100644
index 0000000000..1780f52540
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java
@@ -0,0 +1,25 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.dto;
+
+import lombok.Builder;
+import lombok.Builder.Default;
+import lombok.Data;
+
+/**
+ * Response "envelope" sent back to the API Server
+ */
+@Builder
+@Data
+public class AdmissionReviewResponse {
+
+ @Default
+ final String apiVersion = "admission.k8s.io/v1";
+
+ @Default
+ final String kind = "AdmissionReview";
+
+ final AdmissionReviewData response;
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java
new file mode 100644
index 0000000000..ccab7ac958
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java
@@ -0,0 +1,13 @@
+package com.baeldung.kubernetes.admission.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Builder
+@Data
+public class AdmissionStatus {
+
+ int code;
+ String message;
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java
new file mode 100644
index 0000000000..814bafbae7
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java
@@ -0,0 +1,218 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.service;
+
+import java.util.Base64;
+import java.util.UUID;
+
+import org.springframework.stereotype.Component;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewData;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewException;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.baeldung.kubernetes.admission.dto.AdmissionStatus;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Process an incoming admission request and add the "wait-for-it" init container
+ * if there's an appropriate annotation
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class AdmissionService {
+
+ private final AdmissionControllerProperties admissionControllerProperties;
+ private final ObjectMapper om;
+
+ public AdmissionReviewResponse processAdmission(ObjectNode body) {
+
+ String uid = body.path("request")
+ .required("uid")
+ .asText();
+
+ log.info("[I42] processAdmission: uid={}",uid);
+ if ( log.isDebugEnabled()) {
+ log.debug("processAdmission: body={}", body.toPrettyString());
+ }
+
+ // Get request annotations
+ JsonNode annotations = body.path("request")
+ .path("object")
+ .path("metadata")
+ .path("annotations");
+ log.info("processAdmision: annotations={}", annotations.toString());
+
+ AdmissionReviewData data;
+ try {
+ if (admissionControllerProperties.isDisabled()) {
+ log.info("[I58] 'disabled' option in effect. No changes to current request will be made");
+ data = createSimpleAllowedReview(body);
+ } else if (annotations.isMissingNode()) {
+ log.info("[I68] No annotations found in request. No changes will be made");
+ data = createSimpleAllowedReview(body);
+ } else {
+ data = processAnnotations(body, annotations);
+ }
+
+ log.info("[I65] Review result: isAllowed=" + data.isAllowed());
+ log.info("[I64] AdmissionReviewData= {}", data);
+
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ } catch (AdmissionReviewException ex) {
+ log.error("[E72] Error processing AdmissionRequest: code={}, message={}", ex.getCode(), ex.getMessage());
+ data = createRejectedAdmissionReview(body, ex.getCode(), ex.getMessage());
+
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ } catch (Exception ex) {
+ log.error("[E72] Unable to process AdmissionRequest: " + ex.getMessage(), ex);
+ data = createRejectedAdmissionReview(body, 500, ex.getMessage());
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ }
+ }
+
+ /**
+ * @param body
+ * @return
+ */
+ protected AdmissionReviewData createSimpleAllowedReview(ObjectNode body) {
+ AdmissionReviewData data;
+ String requestId = body.path("request")
+ .required("uid")
+ .asText();
+
+ data = AdmissionReviewData.builder()
+ .allowed(true)
+ .uid(requestId)
+ .build();
+
+ return data;
+
+ }
+
+ /**
+ * @param body
+ * @return
+ */
+ protected AdmissionReviewData createRejectedAdmissionReview(ObjectNode body, int code, String message) {
+ AdmissionReviewData data;
+ String requestId = body.path("request")
+ .required("uid")
+ .asText();
+
+ AdmissionStatus status = AdmissionStatus.builder()
+ .code(code)
+ .message(message)
+ .build();
+
+ data = AdmissionReviewData.builder()
+ .allowed(false)
+ .uid(requestId)
+ .status(status)
+ .build();
+
+ return data;
+
+ }
+
+ /**
+ * Processa anotações incluÃdas no deployment
+ * @param annotations
+ * @return
+ */
+ protected AdmissionReviewData processAnnotations(ObjectNode body, JsonNode annotations) {
+
+ if (annotations.path(admissionControllerProperties.getAnnotation())
+ .isMissingNode()) {
+ log.info("[I78] processAnnotations: Annotation {} not found in deployment deployment.", admissionControllerProperties.getAnnotation());
+ return createSimpleAllowedReview(body);
+ }
+ else {
+ log.info("[I163] annotation found: {}", annotations.path(admissionControllerProperties.getAnnotation()));
+ }
+
+ // Get wait-for-it arguments from the annotation
+ String waitForArgs = annotations.path(admissionControllerProperties.getAnnotation())
+ .asText();
+
+ log.info("[I169] waitForArgs={}", waitForArgs);
+ // Create a PATCH object
+ String patch = injectInitContainer(body, waitForArgs);
+
+ return AdmissionReviewData.builder()
+ .allowed(true)
+ .uid(body.path("request")
+ .required("uid")
+ .asText())
+ .patch(Base64.getEncoder()
+ .encodeToString(patch.getBytes()))
+ .patchType("JSONPatch")
+ .build();
+
+ }
+
+ /**
+ * Creates the JSONPatch to be included in the admission response
+ * @param body
+ * @param waitForArgs
+ * @return JSONPatch string
+ */
+ protected String injectInitContainer(ObjectNode body, String waitForArgs) {
+
+ // Recover original init containers from the request
+ JsonNode originalSpec = body.path("request")
+ .path("object")
+ .path("spec")
+ .path("template")
+ .path("spec")
+ .require();
+
+ JsonNode maybeInitContainers = originalSpec.path("initContainers");
+ ArrayNode initContainers =
+ maybeInitContainers.isMissingNode()?
+ om.createArrayNode():(ArrayNode) maybeInitContainers;
+
+ // Create the patch array
+ ArrayNode patchArray = om.createArrayNode();
+ ObjectNode addNode = patchArray.addObject();
+
+ addNode.put("op", "add");
+ addNode.put("path", "/spec/template/spec/initContainers");
+ ArrayNode values = addNode.putArray("value");
+
+ // Preserve original init containers
+ values.addAll(initContainers);
+
+ // append the "wait-for-it" container
+ ObjectNode wfi = values.addObject();
+ wfi.put("name", "wait-for-it-" + UUID.randomUUID()); // Create an unique name, JIC
+ wfi.put("image", admissionControllerProperties.getWaitForItImage());
+
+ ArrayNode args = wfi.putArray("args");
+ for (String s : waitForArgs.split("\\s")) {
+ args.add(s);
+ }
+
+ return patchArray.toString();
+ }
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java
new file mode 100644
index 0000000000..544b48a9d4
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java
@@ -0,0 +1,65 @@
+package com.baeldung.kubernetes.admission.service;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Base64;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@SpringBootTest
+@ActiveProfiles("test")
+@EnableConfigurationProperties(AdmissionControllerProperties.class)
+class AdmissionServiceUnitTest {
+
+ @Autowired
+ private ObjectMapper mapper;
+
+ @Autowired
+ private AdmissionService admissionService;
+
+ @Test
+ void whenAnnotationPresent_thenAddContainer() throws Exception {
+
+ InputStream is = this.getClass()
+ .getClassLoader()
+ .getResourceAsStream("test1.json");
+ JsonNode body = mapper.readTree(is);
+ AdmissionReviewResponse response = admissionService.processAdmission((ObjectNode) body);
+ assertNotNull(response);
+ assertNotNull(response.getResponse());
+ assertNotNull(response.getResponse());
+ assertTrue(response.getResponse()
+ .isAllowed());
+
+ String jsonResponse = mapper.writeValueAsString(response);
+ System.out.println(jsonResponse);
+
+ // Decode Patch data
+ String b64patch = response.getResponse()
+ .getPatch();
+ assertNotNull(b64patch);
+ byte[] patch = Base64.getDecoder()
+ .decode(b64patch);
+
+ JsonNode root = mapper.reader()
+ .readTree(new ByteArrayInputStream(patch));
+ assertTrue(root instanceof ArrayNode);
+
+ assertEquals(1, ((ArrayNode) root).size());
+
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
new file mode 100644
index 0000000000..0c601660d1
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: frontend
+ labels:
+ app: nginx
+ annotations:
+ com.baeldung/wait-for-it: "www.google.com:80"
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ ports:
+ - containerPort: 80
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test1.json b/kubernetes/k8s-admission-controller/src/test/resources/test1.json
new file mode 100644
index 0000000000..0ad42ae133
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test1.json
@@ -0,0 +1,84 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test2.json b/kubernetes/k8s-admission-controller/src/test/resources/test2.json
new file mode 100644
index 0000000000..5b8a6ca243
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test2.json
@@ -0,0 +1,85 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "initContainers": [],
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test3.json b/kubernetes/k8s-admission-controller/src/test/resources/test3.json
new file mode 100644
index 0000000000..9cb4d516f9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test3.json
@@ -0,0 +1,94 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "initContainers": [
+ {
+ "name": "init1",
+ "image": "test-app-image:latest",
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ],
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test4.json b/kubernetes/k8s-admission-controller/src/test/resources/test4.json
new file mode 100644
index 0000000000..1e3be90a93
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test4.json
@@ -0,0 +1,48 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "26beb334-739a-48d2-b04d-25f6e5e7c106",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {},
+ "f:type": {}
+ },
+ "f:template": {
+ "f:metadata": {
+ "f:labels": {
+ ".": {},
+ "f:app": {}
+ }
+ },
+ "f:spec": {
+ "f:containers": {
+ "k:{\"name\":\"nginx\"}": {
+ ".": {},
+ "f:image": {},
+ "f:imagePullPolicy": {},
+ "f:name": {},
+ "f:ports": {
+ ".": {},
+ "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
+ ".": {},
+ "f:containerPort": {},
+ "f:protocol": {}
+ }
+ },
+ "f:resources": {},
+ "f:terminationMessagePath": {},
+ "f:terminationMessagePolicy": {}
+ }
+ },
+ "f:dnsPolicy": {},
+ "f:restartPolicy": {},
+ "f:schedulerName": {},
+ "f:securityContext": {},
+ "f:terminationGracePeriodSeconds": {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
new file mode 100644
index 0000000000..4c305a080a
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
@@ -0,0 +1,3 @@
+.terraform
+terraform.tfstate
+terraform.tfstate.backup
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
new file mode 100644
index 0000000000..e13c37b4ec
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
@@ -0,0 +1,57 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/kubernetes" {
+ version = "2.3.2"
+ constraints = "2.3.2"
+ hashes = [
+ "h1:D8HWX3vouTPI3Jicq43xOQyoYWtSsVua92cBVrJ3ZMs=",
+ "zh:10f71c170be13538374a4b9553fcb3d98a6036bcd1ca5901877773116c3f828e",
+ "zh:11d2230e531b7480317e988207a73cb67b332f225b0892304983b19b6014ebe0",
+ "zh:3317387a9a6cc27fd7536b8f3cad4b8a9285e9461f125c5a15d192cef3281856",
+ "zh:458a9858362900fbe97e00432ae8a5bef212a4dacf97a57ede7534c164730da4",
+ "zh:50ea297007d9fe53e5411577f87a4b13f3877ce732089b42f938430e6aadff0d",
+ "zh:56705c959e4cbea3b115782d04c62c68ac75128c5c44ee7aa4043df253ffbfe3",
+ "zh:7eb3722f7f036e224824470c3e0d941f1f268fcd5fa2f8203e0eee425d0e1484",
+ "zh:9f408a6df4d74089e6ce18f9206b06b8107ddb57e2bc9b958a6b7dc352c62980",
+ "zh:aadd25ccc3021040808feb2645779962f638766eb583f586806e59f24dde81bb",
+ "zh:b101c3456e4309b09aab129b0118561178c92cb4be5d96dec553189c3084dca1",
+ "zh:ec08478573b4953764099fbfd670fae81dc24b60e467fb3b023e6fab50b70a9e",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/null" {
+ version = "3.1.0"
+ hashes = [
+ "h1:SFT7X3zY18CLWjoH2GfQyapxsRv6GDKsy9cF1aRwncc=",
+ "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2",
+ "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515",
+ "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521",
+ "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2",
+ "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e",
+ "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53",
+ "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d",
+ "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8",
+ "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70",
+ "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b",
+ "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/tls" {
+ version = "3.1.0"
+ hashes = [
+ "h1:ekOxs6MjdIElt8h9crEVaOwWbEqtfUUfArtA13Jkk6A=",
+ "zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6",
+ "zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2",
+ "zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e",
+ "zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca",
+ "zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698",
+ "zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d",
+ "zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841",
+ "zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989",
+ "zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5",
+ "zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d",
+ "zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0",
+ ]
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars
new file mode 100644
index 0000000000..eb1cdaea53
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars
@@ -0,0 +1,10 @@
+#
+# Sample variable values.
+#
+namespace="default"
+deployment_name="wait-for-it-admission-controller"
+replicas=1
+image="psevestre/wait-for-it-admission-controller"
+image_prefix=""
+image_version="latest"
+k8s_config_context="minikube"
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/main.tf b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf
new file mode 100644
index 0000000000..a0717c4013
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf
@@ -0,0 +1,277 @@
+
+locals {
+ prefix = var.image_prefix != "" ? "${var.image_prefix}/":""
+ image = "${local.prefix}${var.image}:${var.image_version}"
+ cloud_sdk_image = "${local.prefix}frapsoft/openssl"
+ ns = data.kubernetes_namespace.ns.metadata[0].name
+
+ # Spring SSL Configuration
+ webhook_config_json = jsonencode({
+ server = {
+ port = 443
+ ssl = {
+ "key-store" = "/shared-config/webhook.p12"
+ "key-store-type" = "PKCS12"
+ "key-alias" = "webhook"
+ "key-store-password" = ""
+ }
+ }
+
+ admission-controller = {
+ disabled = false
+ image-prefix = "gcr.io/sandboxbv-01"
+ }
+ })
+
+}
+
+
+# Resource namespace
+data "kubernetes_namespace" "ns" {
+ metadata {
+ name = var.namespace
+ }
+}
+
+# TLS Key
+resource "tls_private_key" "tls" {
+ algorithm = "RSA"
+}
+
+# CSR
+resource "tls_cert_request" "tls" {
+ key_algorithm = "RSA"
+ private_key_pem = tls_private_key.tls.private_key_pem
+ subject {
+ common_name = "${var.deployment_name}.${var.namespace}.svc"
+ }
+
+ dns_names = [
+ var.deployment_name,
+ "${var.deployment_name}.${var.namespace}",
+ "${var.deployment_name}.${var.namespace}.svc",
+ "${var.deployment_name}.${var.namespace}.svc.cluster.local"
+ ]
+
+}
+
+# HTTPS Certificate
+resource "tls_self_signed_cert" "tls" {
+ key_algorithm = tls_private_key.tls.algorithm
+ private_key_pem = tls_private_key.tls.private_key_pem
+
+ subject {
+ common_name = "${var.deployment_name}.${local.ns}"
+ }
+
+ validity_period_hours = 24*365*20
+
+ dns_names = [
+ var.deployment_name,
+ "${var.deployment_name}.${var.namespace}",
+ "${var.deployment_name}.${var.namespace}.svc",
+ "${var.deployment_name}.${var.namespace}.svc.cluster.local"
+ ]
+
+ allowed_uses = [
+ "key_encipherment",
+ "digital_signature",
+ "server_auth"
+ ]
+}
+
+# Certificado
+# Obs: Desativado pois o certificado fica preso no estado "Issued"
+resource "kubernetes_certificate_signing_request" "tls" {
+ count = 0
+ metadata {
+ name = "${var.deployment_name}.${var.namespace}"
+ }
+
+ auto_approve = true
+
+ spec {
+ usages = [
+ "key encipherment",
+ "digital signature",
+ "server auth"
+ ]
+
+ signer_name = "kubernetes.io/kubelet-serving"
+
+ request = tls_cert_request.tls.cert_request_pem
+ }
+
+}
+
+# Secret to store TLS key/cert
+resource "kubernetes_secret" "tls" {
+ metadata {
+ namespace = local.ns
+ name = var.deployment_name
+ }
+
+ data = {
+ "webhook-key.pem" = tls_private_key.tls.private_key_pem
+ "webhook-cert.pem" = tls_self_signed_cert.tls.cert_pem
+ }
+
+}
+
+# Deployment
+resource "kubernetes_deployment" "main" {
+ metadata {
+ name = var.deployment_name
+ namespace = local.ns
+ }
+
+ spec {
+ replicas = var.replicas
+ selector {
+ match_labels = {
+ app = var.deployment_name
+ }
+ }
+
+ template {
+ metadata {
+ labels = {
+ app = var.deployment_name
+ }
+ }
+
+ spec {
+ container {
+ image = local.image
+ name = var.deployment_name
+ volume_mount {
+ mount_path = "/shared-config"
+ name = "shared-config"
+ }
+
+ env {
+ name = "SPRING_APPLICATION_JSON"
+ value = local.webhook_config_json
+ }
+
+ }
+
+ init_container {
+ name = "setup-keystore"
+ image = local.cloud_sdk_image
+
+ args = [
+ "pkcs12", "-export",
+ "-in", "/secret/webhook-cert.pem",
+ "-inkey", "/secret/webhook-key.pem",
+ "-name", "webhook",
+ "-out", "/shared-config/webhook.p12",
+ "-passout", "pass:"
+ ]
+
+ volume_mount {
+ mount_path = "/shared-config"
+ name = "shared-config"
+ }
+
+ volume_mount {
+ mount_path = "/secret/webhook-cert.pem"
+ name = "webhook-secret"
+ sub_path = "webhook-cert.pem"
+ }
+
+ volume_mount {
+ mount_path = "/secret/webhook-key.pem"
+ name = "webhook-secret"
+ sub_path = "webhook-key.pem"
+ }
+
+ }
+
+ volume {
+ name = "shared-config"
+ empty_dir {}
+ }
+
+ volume {
+ name = "webhook-secret"
+ secret {
+ secret_name = kubernetes_secret.tls.metadata[0].name
+ items {
+ key = "webhook-cert.pem"
+ path = "webhook-cert.pem"
+ }
+ items {
+ key = "webhook-key.pem"
+ path = "webhook-key.pem"
+ }
+ }
+ }
+
+ }
+ }
+ }
+}
+
+# Service
+resource "kubernetes_service" "svc" {
+ metadata {
+ name = var.deployment_name
+ namespace = local.ns
+ }
+
+ spec {
+ selector = {
+ "app" = var.deployment_name
+ }
+
+ port {
+ name = "https"
+ port = 443
+ protocol = "TCP"
+ target_port = 443
+ }
+
+ type = "ClusterIP"
+ }
+}
+
+# Admission Controller
+resource "kubernetes_mutating_webhook_configuration" "waitforit" {
+ metadata {
+ name = var.deployment_name
+ }
+
+ webhook {
+ name = var.admission_controller_name
+ admission_review_versions = [ "v1", "v1beta1" ]
+
+ #failure_policy = "Ignore" #
+
+ client_config {
+
+ service {
+ name = kubernetes_service.svc.metadata[0].name
+ namespace = local.ns
+ path = "/mutate"
+ port = 443
+ }
+
+ # IMPORTANT: CA_BUNDLE must be Base64-encoded
+ ca_bundle = tls_self_signed_cert.tls.cert_pem
+ }
+
+ rule {
+ api_groups = [ "*" ]
+ api_versions = [ "*" ]
+ operations = [ "CREATE", "UPDATE" ]
+ resources = [ "deployments", "statefulsets" ]
+ }
+
+ side_effects = "None" #
+ }
+
+ depends_on = [
+ kubernetes_deployment.main
+ ]
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf
new file mode 100644
index 0000000000..eb095caa0e
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf
@@ -0,0 +1,14 @@
+terraform {
+ required_providers {
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = "2.3.2"
+ }
+ }
+}
+
+# Use standard kubectl environment to get connection details
+provider "kubernetes" {
+ config_context = var.k8s_config_context
+ config_path = var.k8s_config_path
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf
new file mode 100644
index 0000000000..33ca735f0d
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf
@@ -0,0 +1,50 @@
+variable "namespace" {
+ type = string
+ description = "Namespace where the Admission Controller will be deploymed"
+}
+
+variable "deployment_name" {
+ type = string
+ description = "Admission Controller Deployment Name"
+}
+
+variable "replicas" {
+ type = number
+ description = "Number of replicas used in the deployment"
+ default = 3
+}
+
+variable "image" {
+ type = string
+ description = "Admission Controller image name"
+}
+
+variable "image_version" {
+ type = string
+ description = "Admission Controller image version name"
+ default = "latest"
+}
+
+variable "image_prefix" {
+ type = string
+ description = "Image repository prefix"
+ default = "gcr.io/baeldung"
+}
+
+variable "admission_controller_name" {
+ type = string
+ description = "Admission Controller name"
+ default = "wait-for-it.service.local"
+}
+
+variable "k8s_config_context" {
+ type = string
+ description = "Name of the K8S config context"
+}
+
+variable "k8s_config_path" {
+ type = string
+ description = "Location of the standard K8S configuration"
+ default = "~/.kube/config"
+
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-intro/README.md b/kubernetes/k8s-intro/README.md
index 6ac593452c..8c11f4d53e 100644
--- a/kubernetes/k8s-intro/README.md
+++ b/kubernetes/k8s-intro/README.md
@@ -17,3 +17,4 @@ If you get a valid response, then you're good to go.
- [Paging and Async Calls with the Kubernetes API](https://www.baeldung.com/java-kubernetes-paging-async)
- [Using Watch with the Kubernetes API](https://www.baeldung.com/java-kubernetes-watch)
- [Using Namespaces and Selectors With the Kubernetes Java API](https://www.baeldung.com/java-kubernetes-namespaces-selectors)
+- [Creating, Updating and Deleting Resources with the Java Kubernetes API](https://www.baeldung.com/java-kubernetes-api-crud)
diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml
index 72cb1577a4..2927d449d8 100644
--- a/kubernetes/pom.xml
+++ b/kubernetes/pom.xml
@@ -1,3 +1,4 @@
+
@@ -13,5 +14,7 @@
k8s-intro
+ k8s-admission-controller
+
\ No newline at end of file
diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml
index 28a7fb62fd..104d3cbabc 100644
--- a/libraries-2/pom.xml
+++ b/libraries-2/pom.xml
@@ -130,7 +130,7 @@
0.28.3
1.1.0
4.1.2
- 6.6.0
+ 6.17.0
5.1.9.RELEASE
2.5.0
diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml
index 079628ffaa..0ff89b046f 100644
--- a/libraries-3/pom.xml
+++ b/libraries-3/pom.xml
@@ -230,4 +230,5 @@
2.8
2.1.3
+
\ No newline at end of file
diff --git a/libraries-4/pom.xml b/libraries-4/pom.xml
index 756bfbd3a8..b16d1f216f 100644
--- a/libraries-4/pom.xml
+++ b/libraries-4/pom.xml
@@ -26,6 +26,17 @@
com.haulmont.yarg
yarg
${yarg.version}
+
+
+ org.olap4j
+ olap4j
+
+
+
+
+ org.olap4j
+ olap4j
+ ${olap4j.version}
net.engio
@@ -112,6 +123,7 @@
0.6.5
3.0.0
2.2.4
+ 1.2.0
\ No newline at end of file
diff --git a/libraries-6/pom.xml b/libraries-6/pom.xml
index 289597adc9..8d05d2013c 100644
--- a/libraries-6/pom.xml
+++ b/libraries-6/pom.xml
@@ -123,9 +123,9 @@
- bedatadriven
- bedatadriven public repo
- https://nexus.bedatadriven.com/content/groups/public/
+ mulesoft
+ Mulesoft Repository
+ https://repository.mulesoft.org/nexus/content/repositories/public/
diff --git a/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java b/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java
deleted file mode 100644
index e61f4158a7..0000000000
--- a/libraries-6/src/test/java/com/baeldung/kafkastreams/KafkaStreamsLiveTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.baeldung.kafkastreams;
-
-import org.apache.kafka.clients.consumer.ConsumerConfig;
-import org.apache.kafka.common.serialization.Serde;
-import org.apache.kafka.common.serialization.Serdes;
-import org.apache.kafka.streams.KafkaStreams;
-import org.apache.kafka.streams.StreamsBuilder;
-import org.apache.kafka.streams.StreamsConfig;
-import org.apache.kafka.streams.Topology;
-import org.apache.kafka.streams.kstream.KStream;
-import org.apache.kafka.streams.kstream.KTable;
-import org.apache.kafka.streams.kstream.Produced;
-import org.apache.kafka.test.TestUtils;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-public class KafkaStreamsLiveTest {
- private String bootstrapServers = "localhost:9092";
-
- @Test
- @Ignore("it needs to have kafka broker running on local")
- public void shouldTestKafkaStreams() throws InterruptedException {
- // given
- String inputTopic = "inputTopic";
-
- Properties streamsConfiguration = new Properties();
- streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-live-test");
- streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
- streamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
- streamsConfiguration.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
- streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 1000);
- streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
- // Use a temporary directory for storing state, which will be automatically removed after the test.
- streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, TestUtils.tempDirectory().getAbsolutePath());
-
- // when
- StreamsBuilder builder = new StreamsBuilder();
- KStream textLines = builder.stream(inputTopic);
- Pattern pattern = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS);
-
- KTable wordCounts = textLines.flatMapValues(value -> Arrays.asList(pattern.split(value.toLowerCase()))).groupBy((key, word) -> word).count();
-
- textLines.foreach((word, count) -> System.out.println("word: " + word + " -> " + count));
-
- String outputTopic = "outputTopic";
- final Serde stringSerde = Serdes.String();
- final Serde longSerde = Serdes.String();
- textLines.to(outputTopic, Produced.with(stringSerde,longSerde));
-
- KafkaStreams streams = new KafkaStreams(new Topology(), streamsConfiguration);
- streams.start();
-
- // then
- Thread.sleep(30000);
- streams.close();
- }
-}
diff --git a/libraries-data-3/README.md b/libraries-data-3/README.md
index fffdf65252..d3bfb2c80c 100644
--- a/libraries-data-3/README.md
+++ b/libraries-data-3/README.md
@@ -3,7 +3,8 @@
This module contains articles about libraries for data processing in Java.
### Relevant articles
-- [Kafka Streams vs Kafka Consumer]()
+- [Kafka Streams vs Kafka Consumer](https://www.baeldung.com/java-kafka-streams-vs-kafka-consumer)
+- [Kafka Topic Creation Using Java](https://www.baeldung.com/kafka-topic-creation)
- More articles: [[<-- prev]](/../libraries-data-2)
##### Building the project
diff --git a/libraries-data-3/pom.xml b/libraries-data-3/pom.xml
index bfc39e537e..37d5c7ca0d 100644
--- a/libraries-data-3/pom.xml
+++ b/libraries-data-3/pom.xml
@@ -17,8 +17,6 @@
org.apache.kafka
kafka-clients
${kafka.version}
- test
- test
org.apache.kafka
@@ -47,6 +45,12 @@
${testcontainers-kafka.version}
test
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers-jupiter.version}
+ test
+
@@ -54,6 +58,7 @@
1.7.25
2.8.0
1.15.3
+ 1.15.3
\ No newline at end of file
diff --git a/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java b/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java
new file mode 100644
index 0000000000..0d74e27d4e
--- /dev/null
+++ b/libraries-data-3/src/main/java/com/baeldung/kafka/admin/KafkaTopicApplication.java
@@ -0,0 +1,87 @@
+package com.baeldung.kafka.admin;
+
+import org.apache.kafka.clients.admin.Admin;
+import org.apache.kafka.clients.admin.AdminClientConfig;
+import org.apache.kafka.clients.admin.CreateTopicsOptions;
+import org.apache.kafka.clients.admin.CreateTopicsResult;
+import org.apache.kafka.clients.admin.NewTopic;
+import org.apache.kafka.common.KafkaFuture;
+import org.apache.kafka.common.config.TopicConfig;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class KafkaTopicApplication {
+
+ private final Properties properties;
+
+ public KafkaTopicApplication(Properties properties) {
+ this.properties = properties;
+ }
+
+ public void createTopic(String topicName) throws Exception {
+ try (Admin admin = Admin.create(properties)) {
+ int partitions = 1;
+ short replicationFactor = 1;
+ NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor);
+
+ CreateTopicsResult result = admin.createTopics(
+ Collections.singleton(newTopic));
+
+ // get the async result for the new topic creation
+ KafkaFuture future = result.values().get(topicName);
+
+ // call get() to block until topic creation has completed or failed
+ future.get();
+ }
+ }
+
+ public void createTopicWithOptions(String topicName) throws Exception {
+ Properties props = new Properties();
+ props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
+
+ try (Admin admin = Admin.create(props)) {
+ int partitions = 1;
+ short replicationFactor = 1;
+ NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor);
+
+ CreateTopicsOptions topicOptions = new CreateTopicsOptions()
+ .validateOnly(true)
+ .retryOnQuotaViolation(true);
+
+ CreateTopicsResult result = admin.createTopics(
+ Collections.singleton(newTopic), topicOptions
+ );
+
+ KafkaFuture future = result.values().get(topicName);
+ future.get();
+ }
+ }
+
+ public void createCompactedTopicWithCompression(String topicName) throws Exception {
+ Properties props = new Properties();
+ props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
+
+ try (Admin admin = Admin.create(props)) {
+ int partitions = 1;
+ short replicationFactor = 1;
+
+ // Create a compacted topic with 'lz4' compression codec
+ Map newTopicConfig = new HashMap<>();
+ newTopicConfig.put(TopicConfig.CLEANUP_POLICY_CONFIG, TopicConfig.CLEANUP_POLICY_COMPACT);
+ newTopicConfig.put(TopicConfig.COMPRESSION_TYPE_CONFIG, "lz4");
+ NewTopic newTopic = new NewTopic(topicName, partitions, replicationFactor)
+ .configs(newTopicConfig);
+
+ CreateTopicsResult result = admin.createTopics(
+ Collections.singleton(newTopic)
+ );
+
+ KafkaFuture future = result.values().get(topicName);
+ future.get();
+ }
+ }
+
+}
diff --git a/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java b/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java
new file mode 100644
index 0000000000..d79f6af7c1
--- /dev/null
+++ b/libraries-data-3/src/test/java/com/baeldung/kafka/admin/KafkaTopicApplicationIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.kafka.admin;
+
+import org.apache.kafka.clients.admin.AdminClientConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.KafkaContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Testcontainers
+class KafkaTopicApplicationIntegrationTest {
+
+ @Container
+ private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"));
+
+ private KafkaTopicApplication kafkaTopicApplication;
+
+ @BeforeEach
+ void setup() {
+ Properties properties = new Properties();
+ properties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers());
+ kafkaTopicApplication = new KafkaTopicApplication(properties);
+ }
+
+ @Test
+ void givenTopicName_whenCreateNewTopic_thenTopicIsCreated() throws Exception {
+ kafkaTopicApplication.createTopic("test-topic");
+
+ String topicCommand = "/usr/bin/kafka-topics --bootstrap-server=localhost:9092 --list";
+ String stdout = KAFKA_CONTAINER.execInContainer("/bin/sh", "-c", topicCommand)
+ .getStdout();
+
+ assertThat(stdout).contains("test-topic");
+ }
+}
diff --git a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
index 4b35a94eb5..cce8b78808 100644
--- a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
+++ b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
@@ -21,7 +21,7 @@ public class CustomerService {
final ObjectMapper mapper = new ObjectMapper();
final Customer customer = mapper.convertValue(customerData, Customer.class);
- if (Operation.DELETE.name().equals(operation.name())) {
+ if (Operation.DELETE == operation) {
customerRepository.deleteById(customer.getId());
} else {
customerRepository.save(customer);
diff --git a/libraries-data-io/README.md b/libraries-data-io/README.md
index 3e68334ec9..16c7cc66eb 100644
--- a/libraries-data-io/README.md
+++ b/libraries-data-io/README.md
@@ -10,3 +10,4 @@ This module contains articles about IO data processing libraries.
- [Interact with Google Sheets from Java](https://www.baeldung.com/google-sheets-java-client)
- [Introduction To Docx4J](https://www.baeldung.com/docx4j)
- [Breaking YAML Strings Over Multiple Lines](https://www.baeldung.com/yaml-multi-line)
+- [Different Serialization Approaches for Java](https://www.baeldung.com/java-serialization-approaches)
diff --git a/libraries-data-io/pom.xml b/libraries-data-io/pom.xml
index 58bfde9aa0..1335ba54d1 100644
--- a/libraries-data-io/pom.xml
+++ b/libraries-data-io/pom.xml
@@ -55,6 +55,46 @@
docx4j
${docx4j.version}
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.version}
+
+
+
+ com.esotericsoftware.yamlbeans
+ yamlbeans
+ ${yamlbeans.version}
+
+
+
+ org.apache.thrift
+ libthrift
+ ${apache-thrift.version}
+
+
+
+ com.google.protobuf
+ protobuf-java
+ ${google-protobuf.version}
+
org.assertj
assertj-core
@@ -73,6 +113,11 @@
3.9.0
3.3.5
2.1
+ 2.8.7
+ 2.12.3
+ 1.15
+ 0.14.2
+ 3.17.3
\ No newline at end of file
diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java
new file mode 100644
index 0000000000..27eadfb5f6
--- /dev/null
+++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java
@@ -0,0 +1,32 @@
+package com.baeldung.serialization.protocols;
+
+import java.io.Serializable;
+
+public class User implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private int id;
+ private String name;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "User [id=" + id + ", name=" + name + "]";
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java
new file mode 100644
index 0000000000..adefabb7e8
--- /dev/null
+++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java
@@ -0,0 +1,636 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: user.proto
+
+package com.baeldung.serialization.protocols;
+
+public final class UserProtos {
+ private UserProtos() {
+ }
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+
+ public interface UserOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:protobuf.User)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ int getId();
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ java.lang.String getName();
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ com.google.protobuf.ByteString getNameBytes();
+ }
+
+ /**
+ * Protobuf type {@code protobuf.User}
+ */
+ public static final class User extends com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:protobuf.User)
+ UserOrBuilder {
+ private static final long serialVersionUID = 0L;
+
+ // Use User.newBuilder() to construct.
+ private User(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+
+ private User() {
+ name_ = "";
+ }
+
+ @java.lang.Override
+ @SuppressWarnings({ "unused" })
+ protected java.lang.Object newInstance(UnusedPrivateParameter unused) {
+ return new User();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
+ return this.unknownFields;
+ }
+
+ private User(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 8: {
+
+ id_ = input.readInt32();
+ break;
+ }
+ case 18: {
+ java.lang.String s = input.readStringRequireUtf8();
+
+ name_ = s;
+ break;
+ }
+ default: {
+ if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class,
+ com.baeldung.serialization.protocols.UserProtos.User.Builder.class);
+ }
+
+ public static final int ID_FIELD_NUMBER = 1;
+ private int id_;
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ @java.lang.Override
+ public int getId() {
+ return id_;
+ }
+
+ public static final int NAME_FIELD_NUMBER = 2;
+ private volatile java.lang.Object name_;
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ @java.lang.Override
+ public java.lang.String getName() {
+ java.lang.Object ref = name_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ name_ = s;
+ return s;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ @java.lang.Override
+ public com.google.protobuf.ByteString getNameBytes() {
+ java.lang.Object ref = name_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ name_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ private byte memoizedIsInitialized = -1;
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1)
+ return true;
+ if (isInitialized == 0)
+ return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
+ if (id_ != 0) {
+ output.writeInt32(1, id_);
+ }
+ if (!getNameBytes().isEmpty()) {
+ com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1)
+ return size;
+
+ size = 0;
+ if (id_ != 0) {
+ size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, id_);
+ }
+ if (!getNameBytes().isEmpty()) {
+ size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof com.baeldung.serialization.protocols.UserProtos.User)) {
+ return super.equals(obj);
+ }
+ com.baeldung.serialization.protocols.UserProtos.User other = (com.baeldung.serialization.protocols.UserProtos.User) obj;
+
+ if (getId() != other.getId())
+ return false;
+ if (!getName().equals(other.getName()))
+ return false;
+ if (!unknownFields.equals(other.unknownFields))
+ return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + ID_FIELD_NUMBER;
+ hash = (53 * hash) + getId();
+ hash = (37 * hash) + NAME_FIELD_NUMBER;
+ hash = (53 * hash) + getName().hashCode();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() {
+ return newBuilder();
+ }
+
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+
+ public static Builder newBuilder(com.baeldung.serialization.protocols.UserProtos.User prototype) {
+ return DEFAULT_INSTANCE.toBuilder()
+ .mergeFrom(prototype);
+ }
+
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+
+ /**
+ * Protobuf type {@code protobuf.User}
+ */
+ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:protobuf.User)
+ com.baeldung.serialization.protocols.UserProtos.UserOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class,
+ com.baeldung.serialization.protocols.UserProtos.User.Builder.class);
+ }
+
+ // Construct using com.baeldung.serialization.compare.UserProtos.User.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {
+ }
+ }
+
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ id_ = 0;
+
+ name_ = "";
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() {
+ return com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User build() {
+ com.baeldung.serialization.protocols.UserProtos.User result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User buildPartial() {
+ com.baeldung.serialization.protocols.UserProtos.User result = new com.baeldung.serialization.protocols.UserProtos.User(this);
+ result.id_ = id_;
+ result.name_ = name_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+
+ @java.lang.Override
+ public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.setField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+
+ @java.lang.Override
+ public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+
+ @java.lang.Override
+ public Builder setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+
+ @java.lang.Override
+ public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof com.baeldung.serialization.protocols.UserProtos.User) {
+ return mergeFrom((com.baeldung.serialization.protocols.UserProtos.User) other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(com.baeldung.serialization.protocols.UserProtos.User other) {
+ if (other == com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance())
+ return this;
+ if (other.getId() != 0) {
+ setId(other.getId());
+ }
+ if (!other.getName()
+ .isEmpty()) {
+ name_ = other.name_;
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ com.baeldung.serialization.protocols.UserProtos.User parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (com.baeldung.serialization.protocols.UserProtos.User) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private int id_;
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ @java.lang.Override
+ public int getId() {
+ return id_;
+ }
+
+ /**
+ * int32 id = 1;
+ * @param value The id to set.
+ * @return This builder for chaining.
+ */
+ public Builder setId(int value) {
+
+ id_ = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * int32 id = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearId() {
+
+ id_ = 0;
+ onChanged();
+ return this;
+ }
+
+ private java.lang.Object name_ = "";
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ public java.lang.String getName() {
+ java.lang.Object ref = name_;
+ if (!(ref instanceof java.lang.String)) {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ name_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ public com.google.protobuf.ByteString getNameBytes() {
+ java.lang.Object ref = name_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ name_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @param value The name to set.
+ * @return This builder for chaining.
+ */
+ public Builder setName(java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ name_ = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * string name = 2;
+ * @return This builder for chaining.
+ */
+ public Builder clearName() {
+
+ name_ = getDefaultInstance().getName();
+ onChanged();
+ return this;
+ }
+
+ /**
+ * string name = 2;
+ * @param value The bytes for name to set.
+ * @return This builder for chaining.
+ */
+ public Builder setNameBytes(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ checkByteStringIsUtf8(value);
+
+ name_ = value;
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+ // @@protoc_insertion_point(builder_scope:protobuf.User)
+ }
+
+ // @@protoc_insertion_point(class_scope:protobuf.User)
+ private static final com.baeldung.serialization.protocols.UserProtos.User DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new com.baeldung.serialization.protocols.UserProtos.User();
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public User parsePartialFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return new User(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor internal_static_protobuf_User_descriptor;
+ private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_protobuf_User_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
+ static {
+ java.lang.String[] descriptorData = { "\n\nuser.proto\022\010protobuf\" \n\004User\022\n\n\002id\030\001 \001" + "(\005\022\014\n\004name\030\002 \001(\tB0\n\"com.baeldung.seriali" + "zation.compareB\nUserProtosb\006proto3" };
+ descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {});
+ internal_static_protobuf_User_descriptor = getDescriptor().getMessageTypes()
+ .get(0);
+ internal_static_protobuf_User_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(internal_static_protobuf_User_descriptor, new java.lang.String[] { "Id", "Name", });
+ }
+ // @@protoc_insertion_point(outer_class_scope)
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java
new file mode 100644
index 0000000000..7b6aa0a24b
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.junit.jupiter.api.Test;
+
+public class ApacheThriftSerializationUnitTest {
+
+ @Test
+ public void whenUsingThriftForSerialization_ThenDataIsSameAfterDeserialization() throws TException {
+
+ User user = new User();
+ user.setId(2);
+ user.setName("Greg");
+
+ TMemoryBuffer trans = new TMemoryBuffer(4096);
+ TProtocol proto = new TBinaryProtocol(trans);
+
+ proto.writeI32(user.getId());
+ proto.writeString(user.getName());
+
+ int userId = proto.readI32();
+ String userName = proto.readString();
+
+ assertEquals(2, userId);
+ assertEquals("Greg", userName);
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java
new file mode 100644
index 0000000000..46d66049d4
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+public class GoogleProtocolBufferUnitTest {
+
+ @Test
+ public void whenUsingProtocolBuffersSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException {
+
+ String filePath = "src/test/resources/protocols/usersproto";
+
+ UserProtos.User user = UserProtos.User.newBuilder().setId(1234).setName("John Doe").build();
+ FileOutputStream fos = new FileOutputStream(filePath);
+ user.writeTo(fos);
+
+ UserProtos.User deserializedUser = UserProtos.User.newBuilder().mergeFrom(new FileInputStream(filePath)).build();
+
+ assertEquals(1234, deserializedUser.getId());
+ assertEquals("John Doe", deserializedUser.getName());
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java
new file mode 100644
index 0000000000..513dcf6ce0
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class GsonSerializationUnitTest {
+
+ @Test
+ public void whenSerializedUsingGson_ThenObjectIsSameAfterDeserialization() {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark");
+
+ String filePath = "src/test/resources/protocols/gson_user.json";
+
+ try (Writer writer = new FileWriter(filePath)) {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ gson.toJson(user, writer);
+
+ assertTrue(Files.exists(Paths.get(filePath)));
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ User deserializedUser = gson.fromJson(new FileReader(filePath), User.class);
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark", deserializedUser.getName());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java
new file mode 100644
index 0000000000..4bfb88b290
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JacksonSerializationUnitTest {
+
+ @Test
+ public void whenUsingJacksonAPIForJSONSerialization_thenDeserializeCorrectObject() throws IOException {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark Jonson");
+
+ String filePath = "src/test/resources/protocols/jackson_user.json";
+
+ File file = new File(filePath);
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(file, user);
+
+ User deserializedUser = mapper.readValue(new File(filePath), User.class);
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark Jonson", deserializedUser.getName());
+ }
+
+ public static List populateListOfUsers() {
+ User user1 = new User();
+ user1.setId(1);
+ user1.setName("Mark Jonson");
+
+ User user2 = new User();
+ user2.setId(2);
+ user2.setName("Johny Beth");
+
+ User user3 = new User();
+ user3.setId(3);
+ user3.setName("Eliza Green");
+
+ User user4 = new User();
+ user4.setId(4);
+ user4.setName("Monica Doe");
+
+ List users = new ArrayList<>();
+ users.add(user1);
+ users.add(user2);
+ users.add(user3);
+ users.add(user4);
+
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java
new file mode 100644
index 0000000000..bb4cc0df0e
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java
@@ -0,0 +1,38 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.junit.jupiter.api.Test;
+
+public class JavaNativeSerializationUnitTest {
+
+ @Test
+ public void whenUsingJavaNativeSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException, ClassNotFoundException {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark");
+
+ String filePath = "src/test/resources/protocols/user.txt";
+
+ FileOutputStream fileOutputStream = new FileOutputStream(filePath);
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
+ objectOutputStream.writeObject(user);
+ objectOutputStream.flush();
+ objectOutputStream.close();
+
+ FileInputStream fileInputStream = new FileInputStream(filePath);
+ ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
+ User deserializedUser = (User) objectInputStream.readObject();
+ objectInputStream.close();
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark", deserializedUser.getName());
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java
new file mode 100644
index 0000000000..7ef3f88511
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java
@@ -0,0 +1,66 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import com.esotericsoftware.yamlbeans.YamlReader;
+import com.esotericsoftware.yamlbeans.YamlWriter;
+
+public class YAMLSerializationUnitTest {
+
+ @Test
+ public void whenUsingYAMLBeansForSerialization_thenDeserializeCorrectMap() throws IOException {
+
+ String filePath = "src/test/resources/protocols/yamlbeans_user.yaml";
+
+ YamlWriter writer = new YamlWriter(new FileWriter(filePath));
+ writer.write(populateUserMap());
+ writer.close();
+
+ YamlReader reader = new YamlReader(new FileReader(filePath));
+ Object object = reader.read();
+ reader.close();
+
+ assertTrue(object instanceof Map);
+ Map deserializedUsers = (Map) object;
+
+ assertEquals(4, deserializedUsers.size());
+ assertEquals("Mark Jonson", (deserializedUsers.get("User1").getName()));
+ assertEquals(1, (deserializedUsers.get("User1").getId()));
+ }
+
+ private Map populateUserMap() {
+
+ User user1 = new User();
+ user1.setId(1);
+ user1.setName("Mark Jonson");
+
+ User user2 = new User();
+ user2.setId(2);
+ user2.setName("Johny Beth");
+
+ User user3 = new User();
+ user3.setId(3);
+ user3.setName("Eliza Green");
+
+ User user4 = new User();
+ user4.setId(4);
+ user4.setName("Monica Doe");
+
+ Map users = new LinkedHashMap<>();
+ users.put("User1", user1);
+ users.put("User2", user2);
+ users.put("User3", user3);
+ users.put("User4", user4);
+
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/gson_user.json b/libraries-data-io/src/test/resources/protocols/gson_user.json
new file mode 100644
index 0000000000..5440a774f7
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/gson_user.json
@@ -0,0 +1,4 @@
+{
+ "id": 1,
+ "name": "Mark"
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/jackson_user.json b/libraries-data-io/src/test/resources/protocols/jackson_user.json
new file mode 100644
index 0000000000..ce10cca7ea
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/jackson_user.json
@@ -0,0 +1 @@
+{"id":1,"name":"Mark Jonson"}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/user.proto b/libraries-data-io/src/test/resources/protocols/user.proto
new file mode 100644
index 0000000000..d4a231d6ec
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/user.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package protobuf;
+
+option java_package = "com.baeldung.serialization.protocols";
+option java_outer_classname = "UserProtos";
+
+message User {
+ int32 id = 1;
+ string name = 2;
+ }
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/user.txt b/libraries-data-io/src/test/resources/protocols/user.txt
new file mode 100644
index 0000000000..c9f53bfd49
Binary files /dev/null and b/libraries-data-io/src/test/resources/protocols/user.txt differ
diff --git a/libraries-data-io/src/test/resources/protocols/usersproto b/libraries-data-io/src/test/resources/protocols/usersproto
new file mode 100644
index 0000000000..83dd650be6
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/usersproto
@@ -0,0 +1 @@
+Ò John Doe
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml
new file mode 100644
index 0000000000..83726897b9
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml
@@ -0,0 +1,13 @@
+!java.util.LinkedHashMap
+User1: !com.baeldung.serialization.protocols.User
+ id: 1
+ name: Mark Jonson
+User2: !com.baeldung.serialization.protocols.User
+ id: 2
+ name: Johny Beth
+User3: !com.baeldung.serialization.protocols.User
+ id: 3
+ name: Eliza Green
+User4: !com.baeldung.serialization.protocols.User
+ id: 4
+ name: Monica Doe
diff --git a/libraries-data/pom.xml b/libraries-data/pom.xml
index 3c6bedba90..717ee802db 100644
--- a/libraries-data/pom.xml
+++ b/libraries-data/pom.xml
@@ -73,7 +73,7 @@
commons-cli
commons-cli
- ${commons.cli.version}
+ ${commons-cli.version}
provided
@@ -163,7 +163,6 @@
2.3
- 1.2
3.0.1
1.2.2
1.0.0
diff --git a/libraries-http-2/README.md b/libraries-http-2/README.md
index 4085d98ea5..fdad2b3e0a 100644
--- a/libraries-http-2/README.md
+++ b/libraries-http-2/README.md
@@ -9,5 +9,7 @@ This module contains articles about HTTP libraries.
- [Retrofit 2 – Dynamic URL](https://www.baeldung.com/retrofit-dynamic-url)
- [Adding Interceptors in OkHTTP](https://www.baeldung.com/java-okhttp-interceptors)
- [A Guide to Events in OkHTTP](https://www.baeldung.com/java-okhttp-events)
+- [Download a Binary File Using OkHttp](https://www.baeldung.com/java-okhttp-download-binary-file)
+- [Trusting a Self-Signed Certificate in OkHttp](https://www.baeldung.com/okhttp-self-signed-cert)
- More articles [[<-- prev]](/libraries-http)
diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java
new file mode 100644
index 0000000000..a7ef60f124
--- /dev/null
+++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileDownloader.java
@@ -0,0 +1,38 @@
+package com.baeldung.okhttp.download;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
+
+public class BinaryFileDownloader implements AutoCloseable {
+
+ private final OkHttpClient client;
+ private final BinaryFileWriter writer;
+
+ public BinaryFileDownloader(OkHttpClient client, BinaryFileWriter writer) {
+ this.client = client;
+ this.writer = writer;
+ }
+
+ public long download(String url) throws IOException {
+ Request request = new Request.Builder().url(url).build();
+ Response response = client.newCall(request).execute();
+ ResponseBody responseBody = response.body();
+ if (responseBody == null) {
+ throw new IllegalStateException("Response doesn't contain a file");
+ }
+ double length = Double.parseDouble(Objects.requireNonNull(response.header(CONTENT_LENGTH, "1")));
+ return writer.write(responseBody.byteStream(), length);
+ }
+
+ @Override
+ public void close() throws Exception {
+ writer.close();
+ }
+}
diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java
new file mode 100644
index 0000000000..fb3664c08e
--- /dev/null
+++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/BinaryFileWriter.java
@@ -0,0 +1,37 @@
+package com.baeldung.okhttp.download;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BinaryFileWriter implements AutoCloseable {
+
+ private static final int CHUNK_SIZE = 1024;
+ private final OutputStream outputStream;
+ private final ProgressCallback progressCallback;
+
+ public BinaryFileWriter(OutputStream outputStream, ProgressCallback progressCallback) {
+ this.outputStream = outputStream;
+ this.progressCallback = progressCallback;
+ }
+
+ public long write(InputStream inputStream, double length) throws IOException {
+ try (BufferedInputStream input = new BufferedInputStream(inputStream)) {
+ byte[] dataBuffer = new byte[CHUNK_SIZE];
+ int readBytes;
+ long totalBytes = 0;
+ while ((readBytes = input.read(dataBuffer)) != -1) {
+ totalBytes += readBytes;
+ outputStream.write(dataBuffer, 0, readBytes);
+ progressCallback.onProgress(totalBytes / length * 100.0);
+ }
+ return totalBytes;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ outputStream.close();
+ }
+}
diff --git a/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java
new file mode 100644
index 0000000000..b57cabf854
--- /dev/null
+++ b/libraries-http-2/src/main/java/com/baeldung/okhttp/download/ProgressCallback.java
@@ -0,0 +1,7 @@
+package com.baeldung.okhttp.download;
+
+public interface ProgressCallback {
+
+ void onProgress(double progress);
+
+}
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java
new file mode 100644
index 0000000000..3e0c47f793
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java
@@ -0,0 +1,5 @@
+package com.baeldung.okhttp;
+
+public interface Consts {
+ int SSL_APPLICATION_PORT = 8443;
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java
new file mode 100644
index 0000000000..6f0f705350
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderIntegrationTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.okhttp.download;
+
+import okhttp3.OkHttpClient;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class BinaryFileDownloaderIntegrationTest {
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Test
+ public void givenATextFile_whenDownload_thenExpectFileDownloaded() {
+ String body = "Hello Baeldung Readers!";
+ server.enqueue(new MockResponse().setBody(body));
+ String fileName = "download.txt";
+
+ ProgressCallback progressCallback = progress -> assertEquals(100.0, progress, .0);
+ try (BinaryFileWriter writer = new BinaryFileWriter(new FileOutputStream(fileName), progressCallback); BinaryFileDownloader tested = new BinaryFileDownloader(new OkHttpClient(), writer)) {
+ long downloaded = tested.download(server.url("/greetings").toString());
+ assertEquals(body.length(), downloaded);
+ File downloadedFile = new File(fileName);
+ assertTrue(downloadedFile.isFile());
+ assertTrue(downloadedFile.delete());
+ } catch (Exception e) {
+ fail("An unexpected exception has occurred: " + e);
+ }
+ }
+
+}
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java
new file mode 100644
index 0000000000..15dda3a471
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileDownloaderUnitTest.java
@@ -0,0 +1,74 @@
+package com.baeldung.okhttp.download;
+
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BinaryFileDownloaderUnitTest {
+
+ @Mock
+ private OkHttpClient client;
+ @Mock
+ private BinaryFileWriter writer;
+ @InjectMocks
+ private BinaryFileDownloader tested;
+
+ @Test
+ public void givenUrlAndResponse_whenDownload_thenExpectFileWritten() throws Exception {
+ String url = "http://example.com/file";
+ Call call = mock(Call.class);
+ when(client.newCall(any(Request.class))).thenReturn(call);
+ ResponseBody body = ResponseBody.create("BODY", MediaType.get("application/text"));
+ Response response = createResponse(url, body);
+ when(call.execute()).thenReturn(response);
+ when(writer.write(any(), anyDouble())).thenReturn(1L);
+
+ try (BinaryFileDownloader tested = new BinaryFileDownloader(client, writer)) {
+ long size = tested.download(url);
+ assertEquals(1L, size);
+ verify(writer).write(any(InputStream.class), anyDouble());
+ }
+ verify(writer).close();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void givenUrlAndResponseWithNullBody_whenDownload_thenExpectIllegalStateException() throws Exception {
+ String url = "http://example.com/file";
+ Call call = mock(Call.class);
+ when(client.newCall(any(Request.class))).thenReturn(call);
+ Response response = createResponse(url, null);
+ when(call.execute()).thenReturn(response);
+
+ tested.download(url);
+
+ verify(writer, times(0)).write(any(InputStream.class), anyDouble());
+ }
+
+ @NotNull
+ private Response createResponse(String url, ResponseBody body) {
+ Request request = new Request.Builder().url(url).build();
+ return new Response.Builder().code(200).request(request).protocol(Protocol.HTTP_2).message("Message").body(body).build();
+ }
+
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java
new file mode 100644
index 0000000000..2b3f0a1313
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/download/BinaryFileWriterUnitTest.java
@@ -0,0 +1,55 @@
+package com.baeldung.okhttp.download;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BinaryFileWriterUnitTest {
+
+ @Mock
+ private OutputStream outputStream;
+
+ @Test
+ public void givenInputStream_whenWrite_thenExpectWritten() throws Exception {
+ InputStream inputStream = mock(InputStream.class);
+ when(inputStream.read(any(), anyInt(), anyInt())).thenReturn(10, -1);
+
+ try (BinaryFileWriter tested = new BinaryFileWriter(outputStream, progress -> assertEquals(100.0, progress, .0))) {
+ long result = tested.write(inputStream, 10);
+
+ assertEquals(10, result);
+ verify(outputStream).write(any(), eq(0), eq(10));
+ verify(inputStream).close();
+ }
+ verify(outputStream).close();
+ }
+
+ @Test
+ public void givenInputStreamEmpty_whenWrite_thenExpectNotWritten() throws Exception {
+ InputStream inputStream = mock(InputStream.class);
+
+ try (BinaryFileWriter tested = new BinaryFileWriter(outputStream, progress -> assertEquals(100.0, progress, .0))) {
+ long result = tested.write(inputStream, 1);
+
+ assertEquals(0, result);
+ verify(outputStream, times(0)).write(any(), anyInt(), anyInt());
+ verify(inputStream).close();
+ }
+ verify(outputStream).close();
+ }
+
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java
new file mode 100644
index 0000000000..3e7fad2a29
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java
@@ -0,0 +1,108 @@
+package com.baeldung.okhttp.ssl;
+
+import static com.baeldung.okhttp.Consts.SSL_APPLICATION_PORT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Execute spring-security-web-boot-2 module before running this live test
+ * @see com.baeldung.ssl.HttpsEnabledApplication
+ */
+public class OkHttpSSLSelfSignedCertLiveTest {
+
+ private final String HTTPS_WELCOME_URL = "https://localhost:" + SSL_APPLICATION_PORT + "/welcome";
+
+ private OkHttpClient.Builder builder;
+
+ @Before
+ public void init() {
+ builder = new OkHttpClient.Builder();
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void whenHTTPSSelfSignedCertGET_thenException() throws IOException {
+ builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ }
+
+ @Test(expected = SSLPeerUnverifiedException.class)
+ public void givenTrustAllCerts_whenHTTPSSelfSignedCertGET_thenException() throws GeneralSecurityException, IOException {
+ final TrustManager TRUST_ALL_CERTS = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[] {};
+ }
+ };
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom());
+ builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS);
+ builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ }
+
+ @Test
+ public void givenTrustAllCertsSkipHostnameVerification_whenHTTPSSelfSignedCertGET_then200OK() throws GeneralSecurityException, IOException {
+ final TrustManager TRUST_ALL_CERTS = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[] {};
+ }
+ };
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom());
+ builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS);
+ builder.hostnameVerifier(new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+ Response response = builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ assertEquals(200, response.code());
+ assertNotNull(response.body());
+ assertEquals("Welcome to Secured Site
", response.body()
+ .string());
+ }
+}
diff --git a/libraries-io/README.md b/libraries-io/README.md
index 90095a2f23..6cfe978d91 100644
--- a/libraries-io/README.md
+++ b/libraries-io/README.md
@@ -2,4 +2,5 @@
### Relevant Articles:
- [Transferring a File Through SFTP in Java](https://www.baeldung.com/java-file-sftp)
+- [How to Create Password-Protected Zip Files and Unzip Them in Java](https://www.baeldung.com/java-password-protected-zip-unzip)
diff --git a/libraries-io/pom.xml b/libraries-io/pom.xml
index 2f65fd989b..0cea9ae9ac 100644
--- a/libraries-io/pom.xml
+++ b/libraries-io/pom.xml
@@ -29,6 +29,11 @@
commons-vfs2
${vfs.version}
+
+ net.lingala.zip4j
+ zip4j
+ 2.9.0
+
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java
new file mode 100644
index 0000000000..cc39bc9dd2
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/CreateSplitZipFile.java
@@ -0,0 +1,22 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.model.ZipParameters;
+import net.lingala.zip4j.model.enums.EncryptionMethod;
+
+import java.io.File;
+import java.util.Arrays;
+
+public class CreateSplitZipFile {
+
+ public static void main(String[] args) throws ZipException {
+ ZipParameters zipParameters = new ZipParameters();
+ zipParameters.setEncryptFiles(true);
+ zipParameters.setEncryptionMethod(EncryptionMethod.AES);
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ int splitLength = 1024 * 1024 * 10; //10MB
+ zipFile.createSplitZipFile(Arrays.asList(new File("aFile.txt")), zipParameters, true, splitLength);
+ zipFile.createSplitZipFileFromFolder(new File("/users/folder_to_add"), zipParameters, true, splitLength);
+ }
+}
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java
new file mode 100644
index 0000000000..10e7ddd339
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractAllFile.java
@@ -0,0 +1,12 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+
+public class ExtractAllFile {
+
+ public static void main(String[] args) throws ZipException {
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ zipFile.extractAll("/destination_directory");
+ }
+}
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java
new file mode 100644
index 0000000000..4cf466d02b
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ExtractSingleFile.java
@@ -0,0 +1,12 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+
+public class ExtractSingleFile {
+
+ public static void main(String[] args) throws ZipException {
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ zipFile.extractFile("aFile.txt", "/destination_directory");
+ }
+}
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java
new file mode 100644
index 0000000000..4d89e8665f
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipFolder.java
@@ -0,0 +1,19 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.model.ZipParameters;
+import net.lingala.zip4j.model.enums.EncryptionMethod;
+
+import java.io.File;
+
+public class ZipFolder {
+
+ public static void main(String[] args) throws ZipException {
+ ZipParameters zipParameters = new ZipParameters();
+ zipParameters.setEncryptFiles(true);
+ zipParameters.setEncryptionMethod(EncryptionMethod.AES);
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ zipFile.addFolder(new File("/users/folder_to_add"), zipParameters);
+ }
+}
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java
new file mode 100644
index 0000000000..dcb860ef92
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipMultiFile.java
@@ -0,0 +1,27 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.model.ZipParameters;
+import net.lingala.zip4j.model.enums.EncryptionMethod;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+public class ZipMultiFile {
+
+ public static void main(String[] args) throws ZipException {
+ ZipParameters zipParameters = new ZipParameters();
+ zipParameters.setEncryptFiles(true);
+ zipParameters.setEncryptionMethod(EncryptionMethod.AES);
+
+ List filesToAdd = Arrays.asList(
+ new File("aFile.txt"),
+ new File("bFile.txt")
+ );
+
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ zipFile.addFiles(filesToAdd, zipParameters);
+ }
+}
diff --git a/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java
new file mode 100644
index 0000000000..a5f600df47
--- /dev/null
+++ b/libraries-io/src/main/java/com/baeldung/java/io/zip4j/ZipSingleFile.java
@@ -0,0 +1,21 @@
+package com.baeldung.java.io.zip4j;
+
+import net.lingala.zip4j.ZipFile;
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.model.ZipParameters;
+import net.lingala.zip4j.model.enums.CompressionLevel;
+import net.lingala.zip4j.model.enums.EncryptionMethod;
+
+import java.io.File;
+
+public class ZipSingleFile {
+
+ public static void main(String[] args) throws ZipException {
+ ZipParameters zipParameters = new ZipParameters();
+ zipParameters.setEncryptFiles(true);
+ zipParameters.setCompressionLevel(CompressionLevel.HIGHER);
+ zipParameters.setEncryptionMethod(EncryptionMethod.AES);
+ ZipFile zipFile = new ZipFile("compressed.zip", "password".toCharArray());
+ zipFile.addFile(new File("aFile.txt"));
+ }
+}
diff --git a/libraries-security/pom.xml b/libraries-security/pom.xml
index 6ee6b7c358..0031b7bc06 100644
--- a/libraries-security/pom.xml
+++ b/libraries-security/pom.xml
@@ -1,6 +1,7 @@
-
+
4.0.0
libraries-security
libraries-security
diff --git a/libraries-server/pom.xml b/libraries-server/pom.xml
index e36ed88f8f..b283164daa 100644
--- a/libraries-server/pom.xml
+++ b/libraries-server/pom.xml
@@ -1,6 +1,7 @@
-
+
4.0.0
libraries-server
0.0.1-SNAPSHOT
@@ -99,7 +100,7 @@
${nanohttpd.version}
-
+
3.6.2
4.5.3
diff --git a/libraries-testing/README.md b/libraries-testing/README.md
index 447f3f32b9..5498c73094 100644
--- a/libraries-testing/README.md
+++ b/libraries-testing/README.md
@@ -13,3 +13,4 @@ This module contains articles about test libraries.
- [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)
+- [Guide to the ModelAssert Library for JSON](https://www.baeldung.com/json-modelassert)
diff --git a/libraries-testing/pom.xml b/libraries-testing/pom.xml
index 6d7e0c01e1..d7b4a88369 100644
--- a/libraries-testing/pom.xml
+++ b/libraries-testing/pom.xml
@@ -84,6 +84,12 @@
jsonassert
${jsonassert.version}
+
+ uk.org.webcompere
+ model-assert
+ ${modelassert.version}
+ test
+
org.awaitility
awaitility
@@ -209,6 +215,7 @@
1.8
3.8.1
0.14.1
+ 1.0.0
\ No newline at end of file
diff --git a/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java b/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java
new file mode 100644
index 0000000000..b32afdb6d0
--- /dev/null
+++ b/libraries-testing/src/test/java/com/baeldung/modelassert/ModelAssertUnitTest.java
@@ -0,0 +1,289 @@
+package com.baeldung.modelassert;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+import uk.org.webcompere.modelassert.json.dsl.nodespecific.tree.WhereDsl;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static uk.org.webcompere.modelassert.json.JsonAssertions.*;
+import static uk.org.webcompere.modelassert.json.PathWildCard.ANY;
+import static uk.org.webcompere.modelassert.json.PathWildCard.ANY_SUBTREE;
+import static uk.org.webcompere.modelassert.json.Patterns.GUID_PATTERN;
+
+class ModelAssertUnitTest {
+ private static final String ACTUAL_JSON = "{" +
+ "\"name\": \"Baeldung\"," +
+ "\"isOnline\": true," +
+ "\"topics\": [ \"Java\", \"Spring\", \"Kotlin\", \"Scala\", \"Linux\" ]" +
+ "}";
+
+ private static final Path PATH_TO_EXPECTED = Paths.get("src", "test", "resources", "modelassert", "baeldung.json");
+
+ public interface DataService {
+
+ boolean isUserLoggedIn(String userDetails);
+ }
+
+ private DataService mockDataService = mock(DataService.class);
+
+ @Test
+ void givenJson_thenNameIsBaeldung() {
+ assertJson(ACTUAL_JSON)
+ .at("/name").isText("Baeldung");
+ }
+
+ @Test
+ void givenJson_thenSecondTopicIsSpring() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics/1").isText("Spring");
+ }
+
+ @Test
+ void givenJson_thenCanMakeMultipleAssertions() {
+ assertJson(ACTUAL_JSON)
+ .at("/name").isText("Baeldung")
+ .at("/topics/1").isText("Spring");
+ }
+
+ @Test
+ void givenCompareByString_thenFailsOnFormatting() throws IOException {
+ String expected = String.join("\n", Files.readAllLines(PATH_TO_EXPECTED));
+
+ assertThatThrownBy(() -> assertThat(ACTUAL_JSON).isEqualTo(expected))
+ .isInstanceOf(AssertionFailedError.class);
+ }
+
+ @Test
+ void givenTwoIdenticalTreesInDifferentFormat_thenPassesWithIsEqualTo() {
+ assertJson(ACTUAL_JSON)
+ .isEqualTo(PATH_TO_EXPECTED);
+ }
+
+ @Test
+ void givenMap_thenCanCompareToYaml() {
+ Map map = new HashMap<>();
+ map.put("name", "baeldung");
+
+ assertJson(map)
+ .isEqualToYaml("name: baeldung");
+ }
+
+ @Test
+ void givenYaml_thenCanCompareToMap() {
+ Map map = new HashMap<>();
+ map.put("name", "baeldung");
+
+ assertYaml("name: baeldung")
+ .isEqualTo(map);
+ }
+
+ @Test
+ void canProduceHamcrestMatcher() {
+ Matcher matcher = json().at("/name").hasValue("Baeldung");
+
+ MatcherAssert.assertThat(ACTUAL_JSON, matcher);
+ }
+
+ @Test
+ void givenJson_thenCanAssertWithMatcherAssert() {
+
+ MatcherAssert.assertThat(ACTUAL_JSON, json()
+ .at("/name").hasValue("Baeldung")
+ .at("/topics/1").isText("Spring"));
+ }
+
+ @Test
+ void givenUserIsOnline_thenIsLoggedIn() {
+ given(mockDataService.isUserLoggedIn(argThat(json()
+ .at("/isOnline").isTrue()
+ .toArgumentMatcher())))
+ .willReturn(true);
+
+ assertThat(mockDataService.isUserLoggedIn(ACTUAL_JSON))
+ .isTrue();
+
+ verify(mockDataService)
+ .isUserLoggedIn(argThat(json()
+ .at("/name").isText("Baeldung")
+ .toArgumentMatcher()));
+ }
+
+ @Test
+ void givenDocument_canMakeSeveralRootNodeAssertions() {
+ assertJson(ACTUAL_JSON)
+ .isNotNull()
+ .isNotNumber()
+ .isObject()
+ .containsKey("name");
+ }
+
+ @Test
+ void givenDocument_canAssertArray() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics").hasSize(5);
+ }
+
+ @Test
+ void givenDocument_canAssertBooleanByType() {
+ assertJson(ACTUAL_JSON)
+ .at("/isOnline").booleanNode().isTrue();
+ }
+
+ @Test
+ void givenTextNode_canAssertContents() {
+ assertJson(ACTUAL_JSON)
+ .at("/name").textContains("ael");
+ }
+
+ @Test
+ void givenTextNode_canMatchWithRegex() {
+ assertJson(ACTUAL_JSON)
+ .at("/name").matches("[A-Z].+");
+ }
+
+ @Test
+ void givenNumberNode_canTestRange() {
+ assertJson("{count: 12}")
+ .at("/count").isBetween(1, 25);
+ }
+
+ @Test
+ void givenNumberNode_canTestDouble() {
+ assertJson("{height: 6.3}")
+ .at("/height").isGreaterThanDouble(6.0);
+ }
+
+ @Test
+ void givenNumberNode_canTestDoubleEquals() {
+ assertJson("{height: 6.3}")
+ .at("/height").isNumberEqualTo(6.3);
+ }
+
+ @Test
+ void givenArrayNode_canTestContains() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics").isArrayContaining("Scala", "Spring");
+ }
+
+ @Test
+ void givenArrayNode_canTestContainsExactlyInAnyOrder() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics").isArrayContainingExactlyInAnyOrder("Scala", "Spring", "Java", "Linux", "Kotlin");
+ }
+
+ @Test
+ void givenArrayNode_canTestContainsExactly() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics").isArrayContainingExactly("Java", "Spring", "Kotlin", "Scala", "Linux");
+ }
+
+ @Test
+ void givenArrayNode_thenCanAssertBySubtree() {
+ assertJson(ACTUAL_JSON)
+ .at("/topics").isEqualTo("[ \"Java\", \"Spring\", \"Kotlin\", \"Scala\", \"Linux\" ]");
+ }
+
+ @Test
+ void givenKeyOrderIsDifferent_canStillBeEqual() {
+ String actualJson = "{a:{d:3, c:2, b:1}}";
+ String expectedJson = "{a:{b:1, c:2, d:3}}";
+
+ assertJson(actualJson)
+ .where().keysInAnyOrder()
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenKeyOrderIsDifferentInA_canStillBeEqual() {
+ String actualJson = "{a:{d:3, c:2, b:1}}";
+ String expectedJson = "{a:{b:1, c:2, d:3}}";
+
+ assertJson(actualJson)
+ .where()
+ .at("/a").keysInAnyOrder()
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenElementOrderIsDifferent_canStillBeEqual() {
+ String actualJson = "{a:[1, 2, 3, 4, 5]}";
+ String expectedJson = "{a:[5, 4, 3, 2, 1]}";
+
+ assertJson(actualJson)
+ .where().arrayInAnyOrder()
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenActualHasExtraValue_thenIgnoreIt() {
+ String actualJson = "{user:{name: \"Baeldung\", url:\"http://www.baeldung.com\"}}";
+ String expectedJson = "{user:{name: \"Baeldung\"}}";
+
+ assertJson(actualJson)
+ .where().at("/user/url").isIgnored()
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenActualHasIdGUIDS_thenIgnoreThem() {
+ String actualJson = "{user:{credentials:[" +
+ "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," +
+ "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}";
+ String expectedJson = "{user:{credentials:" +
+ "[{id:\"???\",role:\"Admin\"}," +
+ "{id:\"???\",role:\"Sales\"}]}}";
+
+ assertJson(actualJson)
+ .where()
+ .path("user","credentials", ANY, "id").isIgnored()
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenActualHasIdGUIDS_thenMatchThemByRegexAndSubtree() {
+ String actualJson = "{user:{credentials:[" +
+ "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," +
+ "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}";
+ String expectedJson = "{user:{credentials:" +
+ "[{id:\"???\",role:\"Admin\"}," +
+ "{id:\"???\",role:\"Sales\"}]}}";
+
+ assertJson(actualJson)
+ .where()
+ .path(ANY_SUBTREE, "id").matches(GUID_PATTERN)
+ .isEqualTo(expectedJson);
+ }
+
+ @Test
+ void givenActualHasIdGUIDS_thenMatchThemByRegexAndSubtreeUsingStandardPattern() {
+ String actualJson = "{user:{credentials:[" +
+ "{id:\"a7dc2567-3340-4a3b-b1ab-9ce1778f265d\",role:\"Admin\"}," +
+ "{id:\"09da84ba-19c2-4674-974f-fd5afff3a0e5\",role:\"Sales\"}]}}";
+ String expectedJson = "{user:{credentials:" +
+ "[{id:\"???\",role:\"Admin\"}," +
+ "{id:\"???\",role:\"Sales\"}]}}";
+
+ assertJson(actualJson)
+ .where()
+ .configuredBy(where -> idsAreGuids(where))
+ .isEqualTo(expectedJson);
+ }
+
+ private static WhereDsl idsAreGuids(WhereDsl where) {
+ return where.path(ANY_SUBTREE, "id").matches(GUID_PATTERN);
+ }
+}
diff --git a/libraries-testing/src/test/resources/modelassert/baeldung.json b/libraries-testing/src/test/resources/modelassert/baeldung.json
new file mode 100644
index 0000000000..2eb8ed3859
--- /dev/null
+++ b/libraries-testing/src/test/resources/modelassert/baeldung.json
@@ -0,0 +1,5 @@
+{
+ "name": "Baeldung",
+ "isOnline": true,
+ "topics": [ "Java", "Spring", "Kotlin", "Scala", "Linux" ]
+}
diff --git a/libraries/pom.xml b/libraries/pom.xml
index 40cc1b4671..ffb2b09699 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -243,14 +243,6 @@
-
-
- false
-
- bintray-cuba-platform-main
- bintray
- http://dl.bintray.com/cuba-platform/main
-
nm-repo
Numerical Method's Maven Repository
diff --git a/logging-modules/log4j/README.md b/logging-modules/log4j/README.md
index a7a7ea9643..371d0246ce 100644
--- a/logging-modules/log4j/README.md
+++ b/logging-modules/log4j/README.md
@@ -3,3 +3,4 @@
- [Introduction to SLF4J](http://www.baeldung.com/slf4j-with-log4j2-logback)
- [A Guide to Rolling File Appenders](http://www.baeldung.com/java-logging-rolling-file-appenders)
- [Logging Exceptions Using SLF4J](https://www.baeldung.com/slf4j-log-exceptions)
+- [Log4j Warning: "No Appenders Could Be Found for Logger"](https://www.baeldung.com/log4j-no-appenders-found)
diff --git a/logging-modules/logback/README.md b/logging-modules/logback/README.md
index 05a4ab9308..c9e5450cb0 100644
--- a/logging-modules/logback/README.md
+++ b/logging-modules/logback/README.md
@@ -3,3 +3,4 @@
- [Get Log Output in JSON](https://www.baeldung.com/java-log-json-output)
- [SLF4J Warning: Class Path Contains Multiple SLF4J Bindings](https://www.baeldung.com/slf4j-classpath-multiple-bindings)
- [Sending Emails with Logback](https://www.baeldung.com/logback-send-email)
+- [Mask Sensitive Data in Logs With Logback](https://www.baeldung.com/logback-mask-sensitive-data)
diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml
index c11f20955b..6d32025d94 100644
--- a/logging-modules/logback/pom.xml
+++ b/logging-modules/logback/pom.xml
@@ -15,6 +15,11 @@
+
+ org.json
+ json
+ 20180130
+
ch.qos.logback
logback-classic
diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java
new file mode 100644
index 0000000000..04321e9066
--- /dev/null
+++ b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayout.java
@@ -0,0 +1,46 @@
+package com.baeldung.logback;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
+public class MaskingPatternLayout extends PatternLayout {
+
+ private Pattern multilinePattern;
+ private List maskPatterns = new ArrayList<>();
+
+ // invoked for every single entry in the xml
+ public void addMaskPattern(String maskPattern) {
+ maskPatterns.add(maskPattern);
+ multilinePattern = Pattern.compile(maskPatterns.stream().collect(Collectors.joining("|")), Pattern.MULTILINE);
+ }
+
+ @Override
+ public String doLayout(ILoggingEvent event) {
+ return maskMessage(super.doLayout(event));
+ }
+
+ private String maskMessage(String message) {
+ if (multilinePattern == null) {
+ return message;
+ }
+ StringBuilder sb = new StringBuilder(message);
+ Matcher matcher = multilinePattern.matcher(sb);
+ while (matcher.find()) {
+ IntStream.rangeClosed(1, matcher.groupCount()).forEach(group -> {
+ if (matcher.group(group) != null) {
+ // replace each character with asterisk
+ IntStream.range(matcher.start(group), matcher.end(group)).forEach(i -> sb.setCharAt(i, '*'));
+ }
+ });
+ }
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java
new file mode 100644
index 0000000000..cbcd0209ab
--- /dev/null
+++ b/logging-modules/logback/src/main/java/com/baeldung/logback/MaskingPatternLayoutExample.java
@@ -0,0 +1,27 @@
+package com.baeldung.logback;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MaskingPatternLayoutExample {
+
+ private static final Logger logger = LoggerFactory.getLogger(MaskingPatternLayoutExample.class);
+
+ public static void main(String[] args) {
+ Map user = new HashMap();
+ user.put("user_id", "87656");
+ user.put("SSN", "786445563");
+ user.put("address", "22 Street");
+ user.put("city", "Chicago");
+ user.put("Country", "U.S.");
+ user.put("ip_address", "192.168.1.1");
+ user.put("email_id", "spring@baeldung.com");
+ JSONObject userDetails = new JSONObject(user);
+
+ logger.info("MaskingPatternExample log from {}" + userDetails, MaskingPatternLayoutExample.class.getSimpleName());
+ }
+}
diff --git a/logging-modules/logback/src/main/resources/logback.xml b/logging-modules/logback/src/main/resources/logback.xml
index 76ddc2e3ee..2d56c110e0 100644
--- a/logging-modules/logback/src/main/resources/logback.xml
+++ b/logging-modules/logback/src/main/resources/logback.xml
@@ -47,10 +47,23 @@
+
+
+
+ \"SSN\"\s*:\s*\"(.*?)\"
+ \"address\"\s*:\s*\"(.*?)\"
+ (\d+\.\d+\.\d+\.\d+)
+ (\w+@\w+\.\w+)
+ %-5p [%d{ISO8601,UTC}] [%thread] %c: %m%n%rootException
+
+
+
+
+
\ No newline at end of file
diff --git a/logging-modules/pom.xml b/logging-modules/pom.xml
index 6d036d5648..d7b820040a 100644
--- a/logging-modules/pom.xml
+++ b/logging-modules/pom.xml
@@ -26,4 +26,4 @@
5.6.2
-
+
\ No newline at end of file
diff --git a/lombok-custom/pom.xml b/lombok-custom/pom.xml
index 220367bfe9..dc7f0dfec6 100644
--- a/lombok-custom/pom.xml
+++ b/lombok-custom/pom.xml
@@ -1,11 +1,11 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
lombok-custom
- lombok-custom
0.1-SNAPSHOT
+ lombok-custom
com.baeldung
@@ -20,13 +20,11 @@
${lombok.version}
provided
-
org.kohsuke.metainf-services
metainf-services
${metainf-services.version}
-
org.eclipse.jdt
core
@@ -62,4 +60,4 @@
3.3.0-v_771
-
+
\ No newline at end of file
diff --git a/mapstruct/pom.xml b/mapstruct/pom.xml
index 1434db0d54..1e7ce6cbfc 100644
--- a/mapstruct/pom.xml
+++ b/mapstruct/pom.xml
@@ -34,7 +34,12 @@
org.projectlombok
lombok
- ${org.projectlombok.version}
+ ${lombok.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ ${lombok.mapstruct.binding.version}
org.assertj
@@ -63,7 +68,12 @@
org.projectlombok
lombok
- ${org.projectlombok.version}
+ ${lombok.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ ${lombok.mapstruct.binding.version}
@@ -72,11 +82,11 @@
- 1.3.1.Final
+ 1.4.2.Final
4.3.4.RELEASE
1.8
1.8
- 1.18.4
+ 0.2.0
3.16.1
diff --git a/maven-modules/host-maven-repo-example/README.md b/maven-modules/host-maven-repo-example/README.md
new file mode 100644
index 0000000000..032be2416c
--- /dev/null
+++ b/maven-modules/host-maven-repo-example/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Hosting a Maven Repository on GitHub](https://www.baeldung.com/maven-repo-github)
diff --git a/maven-modules/host-maven-repo-example/pom.xml b/maven-modules/host-maven-repo-example/pom.xml
new file mode 100644
index 0000000000..9434e4a3b3
--- /dev/null
+++ b/maven-modules/host-maven-repo-example/pom.xml
@@ -0,0 +1,108 @@
+
+
+ 4.0.0
+
+ com.baeldung.maven.plugin
+ host-maven-repo-example
+ 1.0-SNAPSHOT
+
+ https://github.com/sgrverma23/host-maven-repo-example.git
+
+
+ github
+ 8
+ 8
+
+
+
+ https://github.com/sgrverma23/host-maven-repo-example.git
+ scm:git:git@github.com:sgrverma23/host-maven-repo-example.git
+ scm:git:git@github.com:sgrverma23/host-maven-repo-example.git
+
+
+
+
+
+ internal.repo
+ Temporary Staging Repository
+ file://${project.build.directory}/mvn-artifact
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.1.0
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ com.github.github
+ site-maven-plugin
+ 0.12
+
+ Maven artifacts for ${project.version}
+ true
+ ${project.build.directory}
+ refs/heads/main
+
+ **/*
+
+ true
+ host-maven-repo-example
+ sgrverma23
+ github
+
+
+
+
+ site
+
+ deploy
+
+
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+ internal.repo::default::file://${project.build.directory}/mvn-artifact
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.1.0
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+ PROJECT-REPO-URL
+ https://github.com/sgrverma23/host-maven-repo-example/main
+
+ true
+ always
+
+
+
+
\ No newline at end of file
diff --git a/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java b/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java
new file mode 100644
index 0000000000..521606313c
--- /dev/null
+++ b/maven-modules/host-maven-repo-example/src/java/com/baeldung/maven/plugin/MainApp.java
@@ -0,0 +1,7 @@
+package com.baeldung.maven.plugin;
+
+public class MainApp {
+ public static void main(String[] args){
+ System.out.println("Maven Repo Add");
+ }
+}
diff --git a/maven-modules/maven-builder-plugin/README.md b/maven-modules/maven-builder-plugin/README.md
new file mode 100644
index 0000000000..47cd99d281
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Additional Source Directories in Maven](https://www.baeldung.com/maven-add-src-directories)
diff --git a/maven-modules/maven-builder-plugin/pom.xml b/maven-modules/maven-builder-plugin/pom.xml
new file mode 100644
index 0000000000..2fedee06cc
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+ org.example
+ maven-builder-plugin
+ 1.0-SNAPSHOT
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.2.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ src/main/newsrc/
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java
new file mode 100644
index 0000000000..176951e21a
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java
@@ -0,0 +1,12 @@
+package com.baeldung.maven.plugin;
+
+import com.baeldung.database.DataConnection;
+
+public class MainApp {
+
+ public static void main(String args[]){
+
+ System.out.println(DataConnection.temp());
+
+ }
+}
diff --git a/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java
new file mode 100644
index 0000000000..8ab05a5658
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java
@@ -0,0 +1,7 @@
+package com.baeldung.database;
+public class DataConnection {
+
+ public static String temp(){
+ return "secondary source directory";
+ }
+}
diff --git a/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml b/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml
index 24a7c48499..e2f0d57e3a 100644
--- a/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml
+++ b/maven-modules/maven-copy-files/copy-rename-maven-plugin/pom.xml
@@ -1,20 +1,19 @@
-
+
4.0.0
+ org.baeldung
+ copy-rename-maven-plugin
+ 1.0-SNAPSHOT
+ copy-rename-maven-plugin
+
maven-copy-files
com.baeldung
1.0-SNAPSHOT
- org.baeldung
- copy-rename-maven-plugin
- 1.0-SNAPSHOT
- copy-rename-maven-plugin
-
- UTF-8
- 1.7
- 1.7
-
+
junit
@@ -23,6 +22,7 @@
test
+
@@ -89,4 +89,11 @@
-
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml b/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml
index 61017dd18a..97e714d9ff 100644
--- a/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml
+++ b/maven-modules/maven-copy-files/maven-antrun-plugin/pom.xml
@@ -1,20 +1,19 @@
-
+
4.0.0
+ org.baeldung
+ maven-antrun-plugin
+ 1.0-SNAPSHOT
+ maven-antrun-plugin
+
maven-copy-files
com.baeldung
1.0-SNAPSHOT
- org.baeldung
- maven-antrun-plugin
- 1.0-SNAPSHOT
- maven-antrun-plugin
-
- UTF-8
- 1.7
- 1.7
-
+
junit
@@ -23,6 +22,7 @@
test
+
@@ -35,7 +35,8 @@
-
+
@@ -91,4 +92,11 @@
-
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml b/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml
index 7dbe851f95..eed40565da 100644
--- a/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml
+++ b/maven-modules/maven-copy-files/maven-resources-plugin/pom.xml
@@ -1,20 +1,19 @@
-
+
4.0.0
+ org.baeldung
+ maven-resources-plugin
+ 1.0-SNAPSHOT
+ maven-resoures-plugin
+
maven-copy-files
com.baeldung
1.0-SNAPSHOT
- org.baeldung
- maven-resources-plugin
- 1.0-SNAPSHOT
- maven-resoures-plugin
-
- UTF-8
- 1.7
- 1.7
-
+
junit
@@ -23,6 +22,7 @@
test
+
@@ -89,4 +89,11 @@
-
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-copy-files/pom.xml b/maven-modules/maven-copy-files/pom.xml
index b7b67286bc..d2208d68eb 100644
--- a/maven-modules/maven-copy-files/pom.xml
+++ b/maven-modules/maven-copy-files/pom.xml
@@ -1,23 +1,28 @@
-
+
4.0.0
+ com.baeldung
+ maven-copy-files
+ 1.0-SNAPSHOT
+ maven-copy-files
+ pom
+
+ http://www.example.com
+
maven-modules
com.baeldung
0.0.1-SNAPSHOT
- com.baeldung
- maven-copy-files
- 1.0-SNAPSHOT
- pom
- maven-copy-files
-
- http://www.example.com
-
- UTF-8
- 1.7
- 1.7
-
+
+
+ maven-resources-plugin
+ maven-antrun-plugin
+ copy-rename-maven-plugin
+
+
junit
@@ -26,6 +31,7 @@
test
+
@@ -72,9 +78,11 @@
-
- maven-resources-plugin
- maven-antrun-plugin
- copy-rename-maven-plugin
-
-
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml b/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml
index 7ddf5b739c..9648464102 100644
--- a/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml
+++ b/maven-modules/maven-custom-plugin/counter-maven-plugin/pom.xml
@@ -1,12 +1,12 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
com.baeldung
counter-maven-plugin
0.0.1-SNAPSHOT
- counter-maven-plugin Maven Mojo
+ counter-maven-plugin
maven-plugin
http://maven.apache.org
diff --git a/maven-modules/maven-custom-plugin/usage-example/pom.xml b/maven-modules/maven-custom-plugin/usage-example/pom.xml
index f512fc104d..bf5a69146b 100644
--- a/maven-modules/maven-custom-plugin/usage-example/pom.xml
+++ b/maven-modules/maven-custom-plugin/usage-example/pom.xml
@@ -1,6 +1,5 @@
-
4.0.0
@@ -48,4 +47,4 @@
4.12
-
+
\ No newline at end of file
diff --git a/maven-modules/maven-integration-test/README.md b/maven-modules/maven-integration-test/README.md
index e73a73e61e..708cb3bf23 100644
--- a/maven-modules/maven-integration-test/README.md
+++ b/maven-modules/maven-integration-test/README.md
@@ -7,4 +7,5 @@ This module contains articles about Integration Testing with Maven and related p
- [Integration Testing with Maven](https://www.baeldung.com/maven-integration-test)
- [Build a Jar with Maven and Ignore the Test Results](https://www.baeldung.com/maven-ignore-test-results)
- [Quick Guide to the Maven Surefire Plugin](https://www.baeldung.com/maven-surefire-plugin)
-- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin)
\ No newline at end of file
+- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin)
+- [Difference Between Maven Surefire and Failsafe Plugins](https://www.baeldung.com/maven-surefire-vs-failsafe)
diff --git a/maven-modules/maven-integration-test/pom.xml b/maven-modules/maven-integration-test/pom.xml
index 4ab8de783d..692751366d 100644
--- a/maven-modules/maven-integration-test/pom.xml
+++ b/maven-modules/maven-integration-test/pom.xml
@@ -6,6 +6,7 @@
maven-integration-test
0.0.1-SNAPSHOT
maven-integration-test
+ war
com.baeldung
@@ -116,7 +117,9 @@
verify
-
+
+ **/*IntegrationTest
+
diff --git a/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java
new file mode 100644
index 0000000000..a50fa16394
--- /dev/null
+++ b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java
@@ -0,0 +1,13 @@
+package com.baeldung.maven.it;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class FailsafeBuildPhaseIntegrationTest {
+
+ @Test
+ public void whenTestExecutes_thenPreAndPostIntegrationBuildPhasesAreExecuted() {
+ assertTrue(true);
+ }
+}
diff --git a/maven-modules/maven-pom-types/pom.xml b/maven-modules/maven-pom-types/pom.xml
index 98fbc828a0..8b174ea08e 100644
--- a/maven-modules/maven-pom-types/pom.xml
+++ b/maven-modules/maven-pom-types/pom.xml
@@ -1,7 +1,7 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.baeldung
maven-pom-types
diff --git a/maven-modules/maven-printing-plugins/pom.xml b/maven-modules/maven-printing-plugins/pom.xml
index 805c3c1633..b68c88dbee 100644
--- a/maven-modules/maven-printing-plugins/pom.xml
+++ b/maven-modules/maven-printing-plugins/pom.xml
@@ -1,7 +1,7 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
maven-printing-plugins
maven-printing-plugins
@@ -27,11 +27,12 @@
-
-
-
+
+
+
+ message="Save to file!" />
@@ -84,4 +85,5 @@
-
+
+
\ No newline at end of file
diff --git a/maven-modules/maven-properties/README.md b/maven-modules/maven-properties/README.md
index 52ac8506b3..75ae24d215 100644
--- a/maven-modules/maven-properties/README.md
+++ b/maven-modules/maven-properties/README.md
@@ -6,3 +6,4 @@ have their own dedicated modules.
### Relevant Articles
- [Accessing Maven Properties in Java](https://www.baeldung.com/java-accessing-maven-properties)
+- [Default Values for Maven Properties](https://www.baeldung.com/maven-properties-defaults)
diff --git a/maven-modules/plugin-management/README.md b/maven-modules/plugin-management/README.md
new file mode 100644
index 0000000000..dec3a71cfd
--- /dev/null
+++ b/maven-modules/plugin-management/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Plugin Management in Maven](https://www.baeldung.com/maven-plugin-management)
diff --git a/maven-modules/plugin-management/pom.xml b/maven-modules/plugin-management/pom.xml
new file mode 100644
index 0000000000..cd75e5dbd0
--- /dev/null
+++ b/maven-modules/plugin-management/pom.xml
@@ -0,0 +1,64 @@
+
+
+ 4.0.0
+ plugin-management
+ 0.0.1-SNAPSHOT
+ pom
+
+
+ maven-modules
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
+ submodule-1
+ submodule-2
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${maven.bulid.helper.plugin}
+
+
+ add-resource
+ generate-resources
+
+ add-resource
+
+
+
+
+ src/resources
+ json
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin}
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+
+ 3.8.1
+ 3.2.0
+
+
+
\ No newline at end of file
diff --git a/maven-modules/plugin-management/submodule-1/pom.xml b/maven-modules/plugin-management/submodule-1/pom.xml
new file mode 100644
index 0000000000..c36e092254
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+ submodule-1
+
+
+ plugin-management
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/maven-modules/plugin-management/submodule-1/src/resources/include.json b/maven-modules/plugin-management/submodule-1/src/resources/include.json
new file mode 100644
index 0000000000..d5df76e7e0
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/src/resources/include.json
@@ -0,0 +1,3 @@
+{
+ "key": "value"
+}
\ No newline at end of file
diff --git a/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java
new file mode 100644
index 0000000000..ab6b781790
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java
@@ -0,0 +1,19 @@
+package com.baeldung;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CopiesAdditionalResourcesUnitTest {
+
+ @Test
+ void givenAdditionalResource_whenCopyingFromSourceToDestination_thenShouldBeInDestination() {
+ URL resource = getClass().getClassLoader().getResource("json/include.json");
+ File destinationFile = new File(resource.getFile());
+
+ assertTrue(destinationFile.exists());
+ }
+}
diff --git a/maven-modules/plugin-management/submodule-2/pom.xml b/maven-modules/plugin-management/submodule-2/pom.xml
new file mode 100644
index 0000000000..e50d3cc26d
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-2/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ submodule-2
+
+
+ plugin-management
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml
index 0f146e26da..5b4b567ae6 100644
--- a/maven-modules/pom.xml
+++ b/maven-modules/pom.xml
@@ -16,7 +16,7 @@
- maven-copy-files
+ maven-copy-files
maven-custom-plugin
maven-exec-plugin
maven-integration-test
@@ -32,6 +32,9 @@
version-overriding-plugins
versions-maven-plugin
maven-printing-plugins
+ maven-builder-plugin
+ host-maven-repo-example
+ plugin-management
-
+
\ No newline at end of file
diff --git a/metrics/pom.xml b/metrics/pom.xml
index 2020cd28cf..0ba590ceec 100644
--- a/metrics/pom.xml
+++ b/metrics/pom.xml
@@ -81,13 +81,18 @@
${assertj-core.version}
test
+
+ com.netflix.spectator
+ spectator-api
+ 0.132.0
+
3.1.2
3.1.0
0.12.17
- 0.12.0.RELEASE
+ 1.7.1
2.0.7.RELEASE
3.11.1
diff --git a/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java
index 1d738085ce..8fdb87cfe7 100644
--- a/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java
+++ b/metrics/src/main/java/com/baeldung/metrics/micrometer/MicrometerApp.java
@@ -1,10 +1,10 @@
package com.baeldung.metrics.micrometer;
+import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
-import io.micrometer.core.instrument.binder.JvmThreadMetrics;
@SpringBootApplication
public class MicrometerApp {
@@ -14,7 +14,7 @@ public class MicrometerApp {
return new JvmThreadMetrics();
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
SpringApplication.run(MicrometerApp.class, args);
}
diff --git a/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java
index 689e23ad6d..02ef926794 100644
--- a/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java
+++ b/metrics/src/test/java/com/baeldung/metrics/micrometer/MicrometerAtlasIntegrationTest.java
@@ -1,11 +1,7 @@
package com.baeldung.metrics.micrometer;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.within;
-import static org.assertj.core.api.Assertions.withinPercentage;
-import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.collection.IsMapContaining.hasEntry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -16,29 +12,25 @@ import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.LongTaskTimer;
import io.micrometer.core.instrument.Measurement;
-import io.micrometer.core.instrument.Meter.Type;
-import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
-import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import io.micrometer.core.instrument.distribution.HistogramSnapshot;
+import io.micrometer.core.instrument.distribution.ValueAtPercentile;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import io.micrometer.core.instrument.stats.hist.Histogram;
-import io.micrometer.core.instrument.stats.quantile.WindowSketchQuantiles;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Arrays;
import java.util.Map;
+import java.util.TreeMap;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import org.assertj.core.data.Percentage;
import org.junit.Before;
import org.junit.Test;
-import org.junit.jupiter.api.Assertions;
import com.netflix.spectator.atlas.AtlasConfig;
@@ -55,7 +47,7 @@ public class MicrometerAtlasIntegrationTest {
@Override
public Duration step() {
- return Duration.ofSeconds(1);
+ return Duration.ofSeconds(10);
}
@Override
@@ -77,9 +69,9 @@ public class MicrometerAtlasIntegrationTest {
compositeRegistry.gauge("baeldung.heat", 90);
- Optional oneGauge = oneSimpleMeter
+ Optional oneGauge = Optional.ofNullable(oneSimpleMeter
.find("baeldung.heat")
- .gauge();
+ .gauge());
assertTrue(oneGauge.isPresent());
Iterator measurements = oneGauge
.get()
@@ -91,9 +83,9 @@ public class MicrometerAtlasIntegrationTest {
.next()
.getValue(), equalTo(90.00));
- Optional atlasGauge = atlasMeterRegistry
+ Optional atlasGauge = Optional.ofNullable(atlasMeterRegistry
.find("baeldung.heat")
- .gauge();
+ .gauge());
assertTrue(atlasGauge.isPresent());
Iterator anotherMeasurements = atlasGauge
.get()
@@ -122,14 +114,14 @@ public class MicrometerAtlasIntegrationTest {
.increment();
new CountedObject();
- Optional counterOptional = Metrics.globalRegistry
+ Optional counterOptional = Optional.ofNullable(Metrics.globalRegistry
.find("objects.instance")
- .counter();
+ .counter());
assertTrue(counterOptional.isPresent());
- assertTrue(counterOptional
+ assertEquals(counterOptional
.get()
- .count() == 2.0);
+ .count() , 2.0, 0.0);
}
@Test
@@ -142,10 +134,10 @@ public class MicrometerAtlasIntegrationTest {
.register(registry);
counter.increment(2.0);
- assertTrue(counter.count() == 2);
+ assertEquals(counter.count(), 2, 0);
counter.increment(-1);
- assertTrue(counter.count() == 2);
+ assertEquals(counter.count(), 1, 0);
}
@Test
@@ -161,8 +153,8 @@ public class MicrometerAtlasIntegrationTest {
timer.record(30, TimeUnit.MILLISECONDS);
- assertTrue(2 == timer.count());
-
+ assertEquals(2, timer.count(), 0);
+
assertThat(timer.totalTime(TimeUnit.MILLISECONDS)).isBetween(40.0, 55.0);
}
@@ -173,12 +165,12 @@ public class MicrometerAtlasIntegrationTest {
.builder("3rdPartyService")
.register(registry);
- long currentTaskId = longTaskTimer.start();
+ LongTaskTimer.Sample currentTaskId = longTaskTimer.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
} catch (InterruptedException ignored) {
}
- long timeElapsed = longTaskTimer.stop(currentTaskId);
+ long timeElapsed = currentTaskId.stop();
assertEquals(2L, timeElapsed/((int) 1e6),1L);
}
@@ -191,10 +183,10 @@ public class MicrometerAtlasIntegrationTest {
.builder("cache.size", list, List::size)
.register(registry);
- assertTrue(gauge.value() == 0.0);
+ assertEquals(gauge.value(), 0.0, 0.0);
list.add("1");
- assertTrue(gauge.value() == 1.0);
+ assertEquals(gauge.value(), 1.0, 0.0);
}
@Test
@@ -208,18 +200,17 @@ public class MicrometerAtlasIntegrationTest {
distributionSummary.record(4);
distributionSummary.record(5);
- assertTrue(3 == distributionSummary.count());
- assertTrue(12 == distributionSummary.totalAmount());
+ assertEquals(3, distributionSummary.count(), 0);
+ assertEquals(12, distributionSummary.totalAmount(), 0);
}
@Test
- public void givenTimer_whenEnrichWithQuantile_thenQuantilesComputed() {
+ public void givenTimer_whenEnrichWithPercentile_thenPercentilesComputed() {
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer
.builder("test.timer")
- .quantiles(WindowSketchQuantiles
- .quantiles(0.3, 0.5, 0.95)
- .create())
+ .publishPercentiles(0.3, 0.5, 0.95)
+ .publishPercentileHistogram()
.register(registry);
timer.record(2, TimeUnit.SECONDS);
@@ -229,27 +220,18 @@ public class MicrometerAtlasIntegrationTest {
timer.record(8, TimeUnit.SECONDS);
timer.record(13, TimeUnit.SECONDS);
- Map quantileMap = extractTagValueMap(registry, Type.Gauge, 1e9);
- assertThat(quantileMap, allOf(hasEntry("quantile=0.3", 2), hasEntry("quantile=0.5", 3), hasEntry("quantile=0.95", 8)));
- }
+ Map expectedMicrometer = new TreeMap<>();
+ expectedMicrometer.put(0.3, 1946.157056);
+ expectedMicrometer.put(0.5, 3019.89888);
+ expectedMicrometer.put(0.95, 13354.663936);
- private Map extractTagValueMap(MeterRegistry registry, Type meterType, double valueDivisor) {
- return registry
- .getMeters()
- .stream()
- .filter(meter -> meter.getType() == meterType)
- .collect(Collectors.toMap(meter -> {
- Tag tag = meter
- .getId()
- .getTags()
- .iterator()
- .next();
- return tag.getKey() + "=" + tag.getValue();
- }, meter -> (int) (meter
- .measure()
- .iterator()
- .next()
- .getValue() / valueDivisor)));
+ Map actualMicrometer = new TreeMap<>();
+ ValueAtPercentile[] percentiles = timer.takeSnapshot().percentileValues();
+ for (ValueAtPercentile percentile : percentiles) {
+ actualMicrometer.put(percentile.percentile(), percentile.value(TimeUnit.MILLISECONDS));
+ }
+
+ assertEquals(expectedMicrometer, actualMicrometer);
}
@Test
@@ -257,7 +239,7 @@ public class MicrometerAtlasIntegrationTest {
SimpleMeterRegistry registry = new SimpleMeterRegistry();
DistributionSummary hist = DistributionSummary
.builder("summary")
- .histogram(Histogram.linear(0, 10, 5))
+ .serviceLevelObjectives(1, 10, 5)
.register(registry);
hist.record(3);
@@ -267,17 +249,28 @@ public class MicrometerAtlasIntegrationTest {
hist.record(13);
hist.record(26);
- Map histograms = extractTagValueMap(registry, Type.Counter, 1.0);
+ Map expectedMicrometer = new TreeMap<>();
+ expectedMicrometer.put(1,0D);
+ expectedMicrometer.put(10,2D);
+ expectedMicrometer.put(5,1D);
- assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=10.0", 2), hasEntry("bucket=20.0", 2), hasEntry("bucket=30.0", 1), hasEntry("bucket=40.0", 1), hasEntry("bucket=Infinity", 0)));
+ Map actualMicrometer = new TreeMap<>();
+ HistogramSnapshot snapshot = hist.takeSnapshot();
+ Arrays.stream(snapshot.histogramCounts()).forEach(p -> {
+ actualMicrometer.put((Integer.valueOf((int) p.bucket())), p.count());
+ });
+
+ assertEquals(expectedMicrometer, actualMicrometer);
}
@Test
public void givenTimer_whenEnrichWithTimescaleHistogram_thenTimeScaleDataCollected() {
SimpleMeterRegistry registry = new SimpleMeterRegistry();
+ Duration[] durations = {Duration.ofMillis(25), Duration.ofMillis(300), Duration.ofMillis(600)};
Timer timer = Timer
.builder("timer")
- .histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3))
+ .sla(durations)
+ .publishPercentileHistogram()
.register(registry);
timer.record(1000, TimeUnit.MILLISECONDS);
@@ -286,10 +279,18 @@ public class MicrometerAtlasIntegrationTest {
timer.record(341, TimeUnit.MILLISECONDS);
timer.record(500, TimeUnit.MILLISECONDS);
- Map histograms = extractTagValueMap(registry, Type.Counter, 1.0);
+ Map expectedMicrometer = new TreeMap<>();
+ expectedMicrometer.put(2.5E7,1D);
+ expectedMicrometer.put(3.0E8,1D);
+ expectedMicrometer.put(6.0E8,4D);
- assertThat(histograms, allOf(hasEntry("bucket=0.0", 0), hasEntry("bucket=2.0E8", 1), hasEntry("bucket=4.0E8", 1), hasEntry("bucket=Infinity", 3)));
+ Map actualMicrometer = new TreeMap<>();
+ HistogramSnapshot snapshot = timer.takeSnapshot();
+ Arrays.stream(snapshot.histogramCounts()).forEach(p -> {
+ actualMicrometer.put((Double.valueOf((int) p.bucket())), p.count());
+ });
+ assertEquals(expectedMicrometer, actualMicrometer);
}
}
diff --git a/netflix-modules/mantis/pom.xml b/netflix-modules/mantis/pom.xml
index 1f8b377b94..0bab944b8a 100644
--- a/netflix-modules/mantis/pom.xml
+++ b/netflix-modules/mantis/pom.xml
@@ -55,9 +55,10 @@
test
+
- jcenter
+ jcenter
https://jcenter.bintray.com/
diff --git a/open-liberty/pom.xml b/open-liberty/pom.xml
index aff951cfd8..268b65c07e 100644
--- a/open-liberty/pom.xml
+++ b/open-liberty/pom.xml
@@ -111,4 +111,5 @@
9443
7070
+
\ No newline at end of file
diff --git a/parent-boot-1/pom.xml b/parent-boot-1/pom.xml
index 96f4b1cbe3..7029d96a7e 100644
--- a/parent-boot-1/pom.xml
+++ b/parent-boot-1/pom.xml
@@ -60,4 +60,4 @@
1.5.22.RELEASE
-
+
\ No newline at end of file
diff --git a/parent-boot-2/pom.xml b/parent-boot-2/pom.xml
index c67842e313..accbc2df96 100644
--- a/parent-boot-2/pom.xml
+++ b/parent-boot-2/pom.xml
@@ -81,10 +81,10 @@
3.3.0
1.0.22.RELEASE
- 2.4.3
+ 2.5.1
1.9.1
3.4.0
-
+
\ No newline at end of file
diff --git a/parent-java/pom.xml b/parent-java/pom.xml
index c1abe79b2e..09d7f4c96d 100644
--- a/parent-java/pom.xml
+++ b/parent-java/pom.xml
@@ -46,4 +46,4 @@
2.2
-
+
\ No newline at end of file
diff --git a/patterns/design-patterns-architectural/README.md b/patterns/design-patterns-architectural/README.md
index ae6781c66c..a8a5a98b88 100644
--- a/patterns/design-patterns-architectural/README.md
+++ b/patterns/design-patterns-architectural/README.md
@@ -2,3 +2,4 @@
- [Service Locator Pattern and Java Implementation](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)
+- [Difference Between MVC and MVP Patterns](https://www.baeldung.com/mvc-vs-mvp-pattern)
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java
new file mode 100644
index 0000000000..9ccd4ee74c
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/MvcMainClass.java
@@ -0,0 +1,24 @@
+package com.baeldung.mvc_mvp.mvc;
+
+public class MvcMainClass {
+
+ public static void main(String[] args) {
+
+ Product model = retrieveProductFromDatabase();
+ ProductView view = new ProductView();
+ model.setView(view);
+ model.showProduct();
+
+ ProductController controller = new ProductController(model);
+ controller.setName("SmartPhone");
+ model.showProduct();
+ }
+
+ private static Product retrieveProductFromDatabase() {
+ Product product = new Product();
+ product.setName("Mobile");
+ product.setDescription("New Brand");
+ product.setPrice(1000.0);
+ return product;
+ }
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java
new file mode 100644
index 0000000000..5c44d4475a
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/Product.java
@@ -0,0 +1,45 @@
+package com.baeldung.mvc_mvp.mvc;
+
+public class Product {
+ private String name;
+ private String description;
+ private Double price;
+ private ProductView view;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Double getPrice() {
+ return price;
+ }
+
+ public void setPrice(Double price) {
+ this.price = price;
+ }
+
+ public ProductView getView() {
+ return view;
+ }
+
+ public void setView(ProductView view) {
+ this.view = view;
+ }
+
+ public void showProduct() {
+ view.printProductDetails(name, description, price);
+ }
+
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java
new file mode 100644
index 0000000000..62cdb76f23
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductController.java
@@ -0,0 +1,34 @@
+package com.baeldung.mvc_mvp.mvc;
+
+public class ProductController {
+ private final Product product;
+
+ public ProductController(Product product) {
+ this.product = product;
+ }
+
+ public String getName() {
+ return product.getName();
+ }
+
+ public void setName(String name) {
+ product.setName(name);
+ }
+
+ public String getDescription() {
+ return product.getDescription();
+ }
+
+ public void setDescription(String description) {
+ product.setDescription(description);
+ }
+
+ public Double getPrice() {
+ return product.getPrice();
+ }
+
+ public void setPrice(Double price) {
+ product.setPrice(price);
+ }
+
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java
new file mode 100644
index 0000000000..43f45b6df7
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvc/ProductView.java
@@ -0,0 +1,15 @@
+package com.baeldung.mvc_mvp.mvc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProductView {
+ private static Logger log = LoggerFactory.getLogger(ProductView.class);
+
+ public void printProductDetails(String name, String description, Double price) {
+ log.info("Product details:");
+ log.info("product Name: " + name);
+ log.info("product Description: " + description);
+ log.info("product price: " + price);
+ }
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java
new file mode 100644
index 0000000000..7d12a0142c
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/MvpMainClass.java
@@ -0,0 +1,22 @@
+package com.baeldung.mvc_mvp.mvp;
+
+public class MvpMainClass {
+
+ public static void main(String[] args) {
+
+ Product model = retrieveProductFromDatabase();
+ ProductView view = new ProductView();
+ ProductPresenter presenter = new ProductPresenter(model, view);
+ presenter.showProduct();
+ presenter.setName("SmartPhone");
+ presenter.showProduct();
+ }
+
+ private static Product retrieveProductFromDatabase() {
+ Product product = new Product();
+ product.setName("Mobile");
+ product.setDescription("New Brand");
+ product.setPrice(1000.0);
+ return product;
+ }
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java
new file mode 100644
index 0000000000..315e6e69b6
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/Product.java
@@ -0,0 +1,32 @@
+package com.baeldung.mvc_mvp.mvp;
+
+public class Product {
+ private String name;
+ private String description;
+ private Double price;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Double getPrice() {
+ return price;
+ }
+
+ public void setPrice(Double price) {
+ this.price = price;
+ }
+
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java
new file mode 100644
index 0000000000..2b87bdbbb5
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductPresenter.java
@@ -0,0 +1,40 @@
+package com.baeldung.mvc_mvp.mvp;
+
+public class ProductPresenter {
+ private final Product product;
+ private final ProductView view;
+
+ public ProductPresenter(Product product, ProductView view) {
+ this.product = product;
+ this.view = view;
+ }
+
+ public String getName() {
+ return product.getName();
+ }
+
+ public void setName(String name) {
+ product.setName(name);
+ }
+
+ public String getDescription() {
+ return product.getDescription();
+ }
+
+ public void setDescription(String description) {
+ product.setDescription(description);
+ }
+
+ public Double getProductPrice() {
+ return product.getPrice();
+ }
+
+ public void setPrice(Double price) {
+ product.setPrice(price);
+ }
+
+ public void showProduct() {
+ view.printProductDetails(product.getName(), product.getDescription(), product.getPrice());
+ }
+
+}
diff --git a/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java
new file mode 100644
index 0000000000..27ccc6743d
--- /dev/null
+++ b/patterns/design-patterns-architectural/src/main/java/com/baeldung/mvc_mvp/mvp/ProductView.java
@@ -0,0 +1,15 @@
+package com.baeldung.mvc_mvp.mvp;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProductView {
+ private static Logger log = LoggerFactory.getLogger(ProductView.class);
+
+ public void printProductDetails(String name, String description, Double price) {
+ log.info("Product details:");
+ log.info("product Name: " + name);
+ log.info("product Description: " + description);
+ log.info("product price: " + price);
+ }
+}
diff --git a/patterns/enterprise-patterns/pom.xml b/patterns/enterprise-patterns/pom.xml
index 3637072c58..5b952b9743 100644
--- a/patterns/enterprise-patterns/pom.xml
+++ b/patterns/enterprise-patterns/pom.xml
@@ -1,62 +1,67 @@
- 4.0.0
-
- com.baeldung
- patterns
- 1.0.0-SNAPSHOT
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ enterprise-patterns
+ pom
- enterprise-patterns
- pom
+
+ com.baeldung
+ patterns
+ 1.0.0-SNAPSHOT
+
-
- 3.7.4
-
+
+ wire-tap
+
-
-
- org.apache.camel.springboot
- camel-spring-boot-starter
-
-
- org.apache.camel.springboot
- camel-activemq-starter
-
+
+
+ org.apache.camel.springboot
+ camel-spring-boot-starter
+
+
+ org.apache.camel.springboot
+ camel-activemq-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.2.2.RELEASE
+ test
+
+
+ org.apache.camel
+ camel-test-spring-junit5
+ test
+
+
-
-
- org.springframework.boot
- spring-boot-starter-test
- 2.2.2.RELEASE
-
-
- org.apache.camel
- camel-test-spring-junit5
- test
-
-
+
+
+
+ org.apache.camel.springboot
+ camel-spring-boot-dependencies
+ ${camel.version}
+ pom
+ import
+
+
+
-
-
-
- org.apache.camel.springboot
- camel-spring-boot-dependencies
- ${camel.version}
- pom
- import
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
+
+ 3.7.4
+
+
+
\ No newline at end of file
diff --git a/patterns/enterprise-patterns/wire-tap/README.md b/patterns/enterprise-patterns/wire-tap/README.md
index 5f2aaf0d5a..95cd1d585f 100644
--- a/patterns/enterprise-patterns/wire-tap/README.md
+++ b/patterns/enterprise-patterns/wire-tap/README.md
@@ -31,4 +31,5 @@ For more details, check out the AmqApplicationUnitTest.class.
- [Wire tap (Enterprise Integration Pattern)](https://drafts.baeldung.com/?p=103346&preview=true)
- [Intro to Apache camel](https://www.baeldung.com/apache-camel-intro)
+- [Wire Tap Enterprise Integration Pattern](https://www.baeldung.com/wiretap-pattern)
diff --git a/patterns/enterprise-patterns/wire-tap/pom.xml b/patterns/enterprise-patterns/wire-tap/pom.xml
index 9169c4ac91..e7959e17f0 100644
--- a/patterns/enterprise-patterns/wire-tap/pom.xml
+++ b/patterns/enterprise-patterns/wire-tap/pom.xml
@@ -1,29 +1,25 @@
- 4.0.0
- wire-tap
- 1.0
- jar
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ wire-tap
+ 1.0
+ jar
-
- enterprise-patterns
- com.baeldung
- 1.0.0-SNAPSHOT
-
+
+ enterprise-patterns
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
-
+
\ No newline at end of file
diff --git a/patterns/hexagonal-architecture/pom.xml b/patterns/hexagonal-architecture/pom.xml
index f8d4524514..9dfc113d03 100644
--- a/patterns/hexagonal-architecture/pom.xml
+++ b/patterns/hexagonal-architecture/pom.xml
@@ -21,7 +21,6 @@
org.springframework.boot
spring-boot-starter-web
-
org.springframework.boot
spring-boot-starter-data-mongodb
diff --git a/patterns/simplehexagonalexample/pom.xml b/patterns/simplehexagonalexample/pom.xml
new file mode 100644
index 0000000000..b73e81be44
--- /dev/null
+++ b/patterns/simplehexagonalexample/pom.xml
@@ -0,0 +1,31 @@
+
+ 4.0.0
+ 1.0.0-SNAPSHOT
+ simple-hexagonal-example
+ simpleHexagonalExample
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java
new file mode 100644
index 0000000000..de8b2f4793
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/DailyQuoteMain.java
@@ -0,0 +1,32 @@
+package com.baeldung.simplehexagonalex;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import com.baeldung.simplehexagonalex.controller.QuoteCliController;
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+
+@SpringBootApplication
+public class DailyQuoteMain implements CommandLineRunner {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DailyQuoteMain.class);
+ @Autowired
+ private QuoteCliController quoteCliController;
+
+ public static void main(final String[] args) {
+
+ SpringApplication.run(DailyQuoteMain.class, args);
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+
+ QuoteOfTheDay quoteOfTheDay = quoteCliController.getQuote("cliController");
+
+ LOG.info(quoteOfTheDay.toString());
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java
new file mode 100644
index 0000000000..9ecb2bf822
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteCliController.java
@@ -0,0 +1,24 @@
+package com.baeldung.simplehexagonalex.controller;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.service.QuoteService;
+
+@Component
+public class QuoteCliController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(QuoteCliController.class);
+
+ @Autowired
+ private QuoteService quoteService;
+
+ public QuoteOfTheDay getQuote(String userId) {
+
+ LOG.info("Getting quote");
+ return quoteService.getQuote(userId);
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java
new file mode 100644
index 0000000000..061cff8cec
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/controller/QuoteRestController.java
@@ -0,0 +1,24 @@
+package com.baeldung.simplehexagonalex.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.service.QuoteService;
+
+@RestController
+@RequestMapping("/quote")
+public class QuoteRestController {
+
+ @Autowired
+ private QuoteService quoteService;
+
+ @GetMapping(path = "/{userId}")
+ public QuoteOfTheDay getEmployee(@PathVariable("userId") String userId) {
+ return quoteService.getQuote(userId);
+ }
+
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java
new file mode 100644
index 0000000000..c74c224c70
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/QuoteOfTheDay.java
@@ -0,0 +1,56 @@
+package com.baeldung.simplehexagonalex.domain;
+
+import java.util.Objects;
+
+public class QuoteOfTheDay {
+
+ private String userName;
+ private String quote;
+ private String provider;
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getQuote() {
+ return quote;
+ }
+
+ public void setQuote(String quote) {
+ this.quote = quote;
+ }
+
+ public String getProvider() {
+ return provider;
+ }
+
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(provider, quote, userName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ QuoteOfTheDay other = (QuoteOfTheDay) obj;
+ return Objects.equals(provider, other.provider) && Objects.equals(quote, other.quote) && Objects.equals(userName, other.userName);
+ }
+
+ @Override
+ public String toString() {
+ return "QuoteOfTheDay [userName=" + userName + ", quote=" + quote + ", provider=" + provider + "]";
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java
new file mode 100644
index 0000000000..7e70cebd12
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/repository/QuoteOfTheDayFromProvider.java
@@ -0,0 +1,8 @@
+package com.baeldung.simplehexagonalex.domain.repository;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+
+public interface QuoteOfTheDayFromProvider {
+
+ QuoteOfTheDay getQuote();
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java
new file mode 100644
index 0000000000..16f7015ace
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteAggregator.java
@@ -0,0 +1,22 @@
+package com.baeldung.simplehexagonalex.domain.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider;
+
+@Service
+public class QuoteAggregator implements QuoteService {
+
+ @Autowired
+ private QuoteOfTheDayFromProvider quoteOfTheDayFromProvider;
+
+ @Override
+ public QuoteOfTheDay getQuote(String userName) {
+
+ QuoteOfTheDay quoteOfTheDay = quoteOfTheDayFromProvider.getQuote();
+ quoteOfTheDay.setUserName(userName);
+ return quoteOfTheDay;
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java
new file mode 100644
index 0000000000..300387a41d
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/domain/service/QuoteService.java
@@ -0,0 +1,8 @@
+package com.baeldung.simplehexagonalex.domain.service;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+
+public interface QuoteService {
+
+ QuoteOfTheDay getQuote(String userName);
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java
new file mode 100644
index 0000000000..0cbbc60726
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuote.java
@@ -0,0 +1,109 @@
+package com.baeldung.simplehexagonalex.repository.primary.quoteadapter;
+
+import java.util.List;
+import java.util.Objects;
+
+public class ProviderQuote {
+
+ private String quote;
+ private String length;
+ private String author;
+ private List tags;
+ private String category;
+ private String language;
+ private String date;
+ private String permalink;
+ private String id;
+ private String background;
+ private String title;
+
+ public ProviderQuote() {
+
+ }
+
+ public ProviderQuote(String quote, String length, String author, List tags, String category, String language, String date, String permalink, String id, String background, String title) {
+ super();
+ this.quote = quote;
+ this.length = length;
+ this.author = author;
+ this.tags = tags;
+ this.category = category;
+ this.language = language;
+ this.date = date;
+ this.permalink = permalink;
+ this.id = id;
+ this.background = background;
+ this.title = title;
+ }
+
+ public String getQuote() {
+ return quote;
+ }
+
+ public String getLength() {
+ return length;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public List getTags() {
+ return tags;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ public String getPermalink() {
+ return permalink;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getBackground() {
+ return background;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProviderQuote other = (ProviderQuote) obj;
+ return Objects.equals(id, other.id);
+ }
+
+ @Override
+ public String toString() {
+ return "TheysaysoQuote [quote=" + quote + ", length=" + length + ", " + "author=" + author + ", tags=" + tags + ", category=" + category + ", language=" + language + ", date=" + date + ", permalink=" + permalink + ", id=" + id + ", background="
+ + background + ", title=" + title + "]";
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java
new file mode 100644
index 0000000000..ded08f7b18
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteAdapter.java
@@ -0,0 +1,54 @@
+package com.baeldung.simplehexagonalex.repository.primary.quoteadapter;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Service("providerQuoteAdapter")
+@Primary
+public class ProviderQuoteAdapter implements QuoteOfTheDayFromProvider {
+
+ @Autowired
+ private Environment env;
+
+ @Override
+ public QuoteOfTheDay getQuote() {
+
+ QuoteOfTheDay quoteOfTheDay = new QuoteOfTheDay();
+ ProviderQuoteEnvelope providerQuote;
+ try {
+ providerQuote = getProviderQuote();
+ quoteOfTheDay.setQuote(providerQuote.getContents()
+ .getQuotes()
+ .get(0)
+ .getQuote());
+ quoteOfTheDay.setProvider(providerQuote.getCopyright()
+ .getUrl());
+ } catch (Exception e) {
+ quoteOfTheDay.setQuote("Unable to get the quote");
+ quoteOfTheDay.setProvider("none");
+ }
+ return quoteOfTheDay;
+ }
+
+ private ProviderQuoteEnvelope getProviderQuote() throws Exception {
+
+ HttpGet request = new HttpGet(URI.create(env.getProperty("theysayso.quote.provider.url")));
+ CloseableHttpClient client = HttpClients.createDefault();
+ CloseableHttpResponse response = client.execute(request);
+ ProviderQuoteEnvelope providersQuote = new ObjectMapper().readValue(response.getEntity()
+ .getContent(), ProviderQuoteEnvelope.class);
+ return providersQuote;
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java
new file mode 100644
index 0000000000..611549f23d
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/repository/primary/quoteadapter/ProviderQuoteEnvelope.java
@@ -0,0 +1,68 @@
+package com.baeldung.simplehexagonalex.repository.primary.quoteadapter;
+
+import java.util.List;
+
+public class ProviderQuoteEnvelope {
+
+ private Success success;
+ private Contents contents;
+ private String baseurl;
+ private Copyright copyright;
+
+ public ProviderQuoteEnvelope() {
+
+ }
+
+ public ProviderQuoteEnvelope(Success success, Contents contents, String baseurl, Copyright copyright) {
+ super();
+ this.success = success;
+ this.contents = contents;
+ this.baseurl = baseurl;
+ this.copyright = copyright;
+ }
+
+ public Success getSuccess() {
+ return success;
+ }
+
+ public Contents getContents() {
+ return contents;
+ }
+
+ public String getBaseurl() {
+ return baseurl;
+ }
+
+ public Copyright getCopyright() {
+ return copyright;
+ }
+
+ public class Contents {
+ private List quotes;
+
+ public List getQuotes() {
+ return quotes;
+ }
+ }
+
+ public class Copyright {
+ private int year;
+ private String url;
+
+ public int getYear() {
+ return year;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+ }
+
+ public class Success {
+ private int total;
+
+ public int getTotal() {
+ return total;
+ }
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java
new file mode 100644
index 0000000000..54c28fc94e
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/java/com/baeldung/simplehexagonalex/reposity/mock/quoteadapter/MockQuoteAdapter.java
@@ -0,0 +1,20 @@
+package com.baeldung.simplehexagonalex.reposity.mock.quoteadapter;
+
+import org.springframework.stereotype.Service;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider;
+
+@Service
+public class MockQuoteAdapter implements QuoteOfTheDayFromProvider {
+
+ @Override
+ public QuoteOfTheDay getQuote() {
+
+ QuoteOfTheDay quoteOfTheDay = new QuoteOfTheDay();
+ quoteOfTheDay.setQuote("Mock quote of the day");
+ quoteOfTheDay.setProvider("Mock Provider");
+
+ return quoteOfTheDay;
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/main/resources/application.properties b/patterns/simplehexagonalexample/src/main/resources/application.properties
new file mode 100644
index 0000000000..dd9413bfd5
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/main/resources/application.properties
@@ -0,0 +1 @@
+theysayso.quote.provider.url=https://quotes.rest/qod?language=en
\ No newline at end of file
diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java
new file mode 100644
index 0000000000..602f7ea5d4
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/MockAccessProviderUnitTest.java
@@ -0,0 +1,22 @@
+package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.reposity.mock.quoteadapter.MockQuoteAdapter;
+
+public class MockAccessProviderUnitTest {
+
+ @Test
+ public void givenProvider_whenConnect_thenResponse() throws Exception {
+
+ MockQuoteAdapter provider = new MockQuoteAdapter();
+ QuoteOfTheDay quote = provider.getQuote();
+ assertNotNull(quote);
+ assertEquals("Mock quote of the day", quote.getQuote());
+ assertEquals("Mock Provider", quote.getProvider());
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java
new file mode 100644
index 0000000000..47e1dde136
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/PrimaryAccessProviderIntegrationTest.java
@@ -0,0 +1,28 @@
+package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import com.baeldung.simplehexagonalex.domain.QuoteOfTheDay;
+import com.baeldung.simplehexagonalex.domain.repository.QuoteOfTheDayFromProvider;
+
+@SpringBootTest
+public class PrimaryAccessProviderIntegrationTest {
+
+ @Autowired
+ @Qualifier("providerQuoteAdapter")
+ QuoteOfTheDayFromProvider provider;
+
+ @Test
+ public void whenQuoteProvider_thenResponse() throws Exception {
+
+ QuoteOfTheDay quote = provider.getQuote();
+ assertNotNull(quote);
+ assertThat(quote.getProvider()).contains("theysaidso");
+ }
+}
diff --git a/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java
new file mode 100644
index 0000000000..552b5d51c2
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/test/java/com/baeldung/simplehexagonalex/repository/primaryQuoteProvider/QuoteRequestIntegrationTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.simplehexagonalex.repository.primaryQuoteProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+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.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+public class QuoteRequestIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void whenRestQuoteRequest_thenResponse() throws Exception {
+
+ MvcResult result = this.mockMvc.perform(get("/quote/tester"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andReturn();
+
+ String content = result.getResponse()
+ .getContentAsString();
+
+ assertThat(content).contains("tester");
+ assertThat(content).contains("theysaidso");
+ }
+}
\ No newline at end of file
diff --git a/patterns/simplehexagonalexample/src/test/resources/application.properties b/patterns/simplehexagonalexample/src/test/resources/application.properties
new file mode 100644
index 0000000000..dd9413bfd5
--- /dev/null
+++ b/patterns/simplehexagonalexample/src/test/resources/application.properties
@@ -0,0 +1 @@
+theysayso.quote.provider.url=https://quotes.rest/qod?language=en
\ No newline at end of file
diff --git a/persistence-modules/core-java-persistence-2/README.md b/persistence-modules/core-java-persistence-2/README.md
index cebd81388d..7b215bfef1 100644
--- a/persistence-modules/core-java-persistence-2/README.md
+++ b/persistence-modules/core-java-persistence-2/README.md
@@ -3,3 +3,5 @@
- [Getting Database URL From JDBC Connection Object](https://www.baeldung.com/jdbc-get-url-from-connection)
- [JDBC URL Format For Different Databases](https://www.baeldung.com/java-jdbc-url-format)
- [How to Check if a Database Table Exists with JDBC](https://www.baeldung.com/jdbc-check-table-exists)
+- [Inserting Null Into an Integer Column Using JDBC](https://www.baeldung.com/jdbc-insert-null-into-integer-column)
+- [A Guide to Auto-Commit in JDBC](https://www.baeldung.com/java-jdbc-auto-commit)
diff --git a/persistence-modules/core-java-persistence-2/pom.xml b/persistence-modules/core-java-persistence-2/pom.xml
index 092900b628..15676bf03e 100644
--- a/persistence-modules/core-java-persistence-2/pom.xml
+++ b/persistence-modules/core-java-persistence-2/pom.xml
@@ -45,7 +45,6 @@
1.4.200
- 42.2.5.jre7
8.4.1.jre11
10.2.0.4.0
8.0.22
diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java
new file mode 100644
index 0000000000..3907434239
--- /dev/null
+++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/DBConfig.java
@@ -0,0 +1,26 @@
+package com.baeldung.insertnull;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class DBConfig {
+
+ private static Connection INSTANCE;
+
+ public static Connection getConnection() throws SQLException {
+ if (INSTANCE == null) {
+ INSTANCE = DriverManager.getConnection("jdbc:h2:mem:insertnull", "user", "password");
+ createPersonTable();
+ }
+ return INSTANCE;
+ }
+
+ private static void createPersonTable() throws SQLException {
+ try(Statement statement = INSTANCE.createStatement()) {
+ String sql = "CREATE TABLE Person (id INTEGER not null, name VARCHAR(50), lastName VARCHAR(50), age INTEGER, PRIMARY KEY (id))";
+ statement.executeUpdate(sql);
+ }
+ }
+}
diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java
new file mode 100644
index 0000000000..e261a7d74c
--- /dev/null
+++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/insertnull/Person.java
@@ -0,0 +1,48 @@
+package com.baeldung.insertnull;
+
+public class Person {
+
+ private Integer id;
+ private String name;
+ private String lastName;
+ private Integer age;
+
+ public Person(Integer id, String name, String lastName, Integer age) {
+ this.id = id;
+ this.name = name;
+ this.lastName = lastName;
+ this.age = age;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+}
diff --git a/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java
new file mode 100644
index 0000000000..81c60a0db1
--- /dev/null
+++ b/persistence-modules/core-java-persistence-2/src/main/java/com/baeldung/jdbcautocommit/Person.java
@@ -0,0 +1,51 @@
+package com.baeldung.jdbcautocommit;
+
+public class Person {
+
+ private Integer id;
+ private String name;
+ private String lastName;
+ private Integer age;
+
+ public Person() {
+ }
+
+ public Person(Integer id, String name, String lastName, Integer age) {
+ this.id = id;
+ this.name = name;
+ this.lastName = lastName;
+ this.age = age;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+}
diff --git a/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java
new file mode 100644
index 0000000000..508fa98e8f
--- /dev/null
+++ b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/insertnull/InsertNullUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.insertnull;
+
+import org.junit.jupiter.api.Test;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class InsertNullUnitTest {
+
+ private final String SQL = "INSERT INTO Person VALUES(?,?,?,?)";
+
+ @Test
+ public void givenNewPerson_whenSetNullIsUsed_thenNewRecordIsCreated() throws SQLException {
+ Person person = new Person(1, "John", "Doe", null);
+
+ try (PreparedStatement preparedStatement = DBConfig.getConnection().prepareStatement(SQL)) {
+ preparedStatement.setInt(1, person.getId());
+ preparedStatement.setString(2, person.getName());
+ preparedStatement.setString(3, person.getLastName());
+ if (person.getAge() == null) {
+ preparedStatement.setNull(4, Types.INTEGER);
+ }
+ else {
+ preparedStatement.setInt(4, person.getAge());
+ }
+ int noOfRows = preparedStatement.executeUpdate();
+
+ assertThat(noOfRows, equalTo(1));
+ }
+ }
+
+ @Test
+ public void givenNewPerson_whenSetObjectIsUsed_thenNewRecordIsCreated() throws SQLException {
+ Person person = new Person(2, "John", "Doe", null);
+
+ try (PreparedStatement preparedStatement = DBConfig.getConnection().prepareStatement(SQL)) {
+ preparedStatement.setInt(1, person.getId());
+ preparedStatement.setString(2, person.getName());
+ preparedStatement.setString(3, person.getLastName());
+ preparedStatement.setObject(4, person.getAge(), Types.INTEGER);
+ int noOfRows = preparedStatement.executeUpdate();
+
+ assertThat(noOfRows, equalTo(1));
+ }
+ }
+}
diff --git a/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java
new file mode 100644
index 0000000000..a3e3d8f34c
--- /dev/null
+++ b/persistence-modules/core-java-persistence-2/src/test/java/com/baeldung/jdbcautocommit/AutoCommitUnitTest.java
@@ -0,0 +1,259 @@
+package com.baeldung.jdbcautocommit;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class AutoCommitUnitTest {
+
+ private static final String INSERT_PERSON_SQL = "INSERT INTO Person VALUES (?,?,?,?)";
+ private static final String SELECT_ALL_PEOPLE_SQL = "SELECT * FROM Person";
+ private static final String UPDATE_PERSON_AGE_BY_ID_SQL = "UPDATE Person SET age = ? WHERE id = ?";
+ private static final String DELETE_ALL_PEOPLE_SQL = "DELETE FROM Person";
+ private static final String UPDATE_PERSON_AGE_BY_NAME_SQL
+ = "UPDATE Person SET age = ? WHERE id = (SELECT id FROM Person WHERE name = ?)";
+ private static final String CREATE_PERSON_TABLE_SQL
+ = "CREATE TABLE Person (id INTEGER not null, name VARCHAR(50), lastName VARCHAR(50), age INTEGER, PRIMARY KEY (id))";
+
+ private static Connection connection1;
+ private static Connection connection2;
+
+ @BeforeAll
+ static void setup() throws SQLException {
+ connection1 = DriverManager.getConnection("jdbc:h2:mem:autocommit", "sa", "");
+ createPersonTable(connection1);
+
+ connection2 = DriverManager.getConnection("jdbc:h2:mem:autocommit", "sa", "");
+ }
+
+ @Test
+ void givenAutoCommitTrue_whenInsertAndUpdateRecord_thenDataPersistedAfterEachWithoutCommit() throws SQLException {
+
+ connection1.setAutoCommit(true);
+
+ Person person = new Person(1, "John", "Doe", 45);
+ insertPerson(connection1, person);
+
+ // no explicit commit needed here when auto-commit true
+
+ // viewed from different connection, connection2 : assert person has been persisted into
+ // the database
+ List people = selectAllPeople(connection2);
+ assertThat("person record inserted OK into empty table", people.size(), is(equalTo(1)));
+ Person personInserted = people.iterator().next();
+ assertThat("id correct", personInserted.getId(), is(equalTo(1)));
+
+ // update age for person in database
+ updatePersonAgeById(connection1, 1, 65);
+
+ // no explicit commit needed here
+
+ // viewed from connection2 : assert person's age has been updated to database
+ people = selectAllPeople(connection2);
+ Person personUpdated = people.iterator().next();
+ assertThat("updated age correct", personUpdated.getAge(), is(equalTo(65)));
+ }
+
+ @Test
+ void givenAutoCommitFalse_whenInsertCommitAndUpdateCommit_thenDataViewableAfterEachCommit() throws SQLException {
+
+ connection1.setAutoCommit(false);
+
+ Person person = new Person(1, "John", "Doe", 45);
+ insertPerson(connection1, person);
+
+ // viewed from different connection, connection2 : assert that person has not yet been
+ // persisted to database before issuing commit
+ List people = selectAllPeople(connection2);
+ assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0)));
+
+ connection1.commit();
+
+ // viewed from connection2 : assert that person has been persisted to database after
+ // issuing commit
+ people = selectAllPeople(connection2);
+ assertThat("Person has been inserted into empty table after commit", people.size(), is(equalTo(1)));
+ Person personInserted = people.iterator().next();
+ assertThat("id correct", personInserted.getId(), is(equalTo(1)));
+
+ updatePersonAgeById(connection1, 1, 65);
+
+ // assert that person's age has not been updated before issuing commit
+ people = selectAllPeople(connection2);
+ Person personUpdated = people.iterator().next();
+ assertThat("person's age still 45, not updated", personUpdated.getAge(), is(equalTo(45)));
+
+ connection1.commit();
+
+ // viewed from connection2 : assert that person's age has been updated after issuing commit
+ people = selectAllPeople(connection2);
+ personUpdated = people.iterator().next();
+ assertThat("person's age updated to 65", personUpdated.getAge(), is(equalTo(65)));
+ }
+
+ @Test
+ void givenAutoCommitFalse_whenInsertAndUpdateWithCommitOnlyAtEnd_thenDataOnlyViewableAfterCommit() throws SQLException {
+
+ connection1.setAutoCommit(false);
+
+ Person person = new Person(1, "John", "Doe", 45);
+ insertPerson(connection1, person);
+
+ // viewed from different connection, connection2 : assert that person has not yet been
+ // persisted to database before issuing commit
+ List people = selectAllPeople(connection2);
+ assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0)));
+
+ updatePersonAgeById(connection1, 1, 65);
+
+ // viewed from connection2 : assert that person has still not yet been
+ // persisted to database before issuing commit
+ people = selectAllPeople(connection2);
+ assertThat("No people have been inserted into database yet", people.size(), is(equalTo(0)));
+
+ connection1.commit();
+
+ // viewed from connection2 : assert that person record has been persisted to
+ // database and person's age has been updated after issuing commit
+ people = selectAllPeople(connection2);
+ Person personUpdated = people.iterator().next();
+ assertThat("person's age updated to 65", personUpdated.getAge(), is(equalTo(65)));
+ }
+
+ @Test
+ void givenAutoCommitTrue_whenUpdateWithNestedSelect_thenUpdatePersistedWithoutCommit() throws SQLException {
+
+ connection1.setAutoCommit(true);
+
+ Person person = new Person(1, "John", "Doe", 45);
+ insertPerson(connection1, person);
+
+ updatePersonAgeByName(connection1, "John", 77);
+
+ // viewed from connection2 : assert person's age has been updated correctly to database
+ // without issuing commit
+ List people = selectAllPeople(connection2);
+ Person personUpdated = people.iterator().next();
+ assertThat("updated age correct", personUpdated.getAge(), is(equalTo(77)));
+ }
+
+ @Test
+ void givenAutoCommitFalse_whenModeChangedToTrueAfterSQLUpdate_thenUpdatePersistedWithoutCommit() throws SQLException {
+
+ connection1.setAutoCommit(false);
+
+ Person person = new Person(1, "John", "Doe", 45);
+ insertPerson(connection1, person);
+ updatePersonAgeByName(connection1, "John", 77);
+
+ connection1.setAutoCommit(true);
+
+ // viewed from connection2 : assert record inserted and person's age has been updated
+ // correctly to database after auto-commit changed false -> true ... without explicit commit needed
+ List people = selectAllPeople(connection2);
+ Person personUpdated = people.iterator().next();
+ assertThat("updated age correct", personUpdated.getAge(), is(equalTo(77)));
+ }
+
+ @AfterEach
+ void deleteAllRecords() throws SQLException {
+
+ if (connection1.getAutoCommit() == false) {
+ connection1.setAutoCommit(true);
+ }
+
+ deleteAllPeople(connection1);
+ }
+
+ @AfterAll
+ static void closeConnections() throws SQLException {
+
+ connection1.close();
+ connection2.close();
+ }
+
+ private static void createPersonTable(Connection connection) throws SQLException {
+ try(Statement statement = connection.createStatement()) {
+ statement.executeUpdate(CREATE_PERSON_TABLE_SQL);
+ }
+ }
+
+ private static int insertPerson(Connection connection, Person person) throws SQLException {
+
+ try (PreparedStatement preparedStatement = connection.prepareStatement(INSERT_PERSON_SQL)) {
+
+ preparedStatement.setInt(1, person.getId());
+ preparedStatement.setString(2, person.getName());
+ preparedStatement.setString(3, person.getLastName());
+ preparedStatement.setInt(4, person.getAge());
+
+ // execute statement and return the number of rows inserted
+ return preparedStatement.executeUpdate();
+ }
+ }
+
+ private static void deleteAllPeople(Connection connection) throws SQLException {
+ try (Statement statement = connection.createStatement()) {
+ statement.execute(DELETE_ALL_PEOPLE_SQL);
+ }
+ }
+
+ private static List selectAllPeople(Connection connection) throws SQLException {
+
+ List people = null;
+
+ try (Statement statement = connection.createStatement()) {
+
+ people = new ArrayList<>();
+ ResultSet resultSet = statement.executeQuery(SELECT_ALL_PEOPLE_SQL);
+
+ while (resultSet.next()) {
+ Person person = new Person();
+
+ person.setId(resultSet.getInt("id"));
+ person.setName(resultSet.getString("name"));
+ person.setLastName(resultSet.getString("lastName"));
+ person.setAge(resultSet.getInt("age"));
+
+ people.add(person);
+ }
+ }
+
+ return people;
+ }
+
+ private static void updatePersonAgeById(Connection connection, int id, int newAge) throws SQLException {
+
+ try (PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_PERSON_AGE_BY_ID_SQL)) {
+
+ preparedStatement.setInt(1, newAge);
+ preparedStatement.setInt(2, id);
+
+ preparedStatement.executeUpdate();
+ }
+ }
+
+ private static void updatePersonAgeByName(Connection connection, String name, int newAge) throws SQLException {
+ try (PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_PERSON_AGE_BY_NAME_SQL)) {
+
+ preparedStatement.setInt(1, newAge);
+ preparedStatement.setString(2, name);
+
+ preparedStatement.executeUpdate();
+ }
+ }
+}
diff --git a/persistence-modules/core-java-persistence/pom.xml b/persistence-modules/core-java-persistence/pom.xml
index f1d779cf2f..96f8cef310 100644
--- a/persistence-modules/core-java-persistence/pom.xml
+++ b/persistence-modules/core-java-persistence/pom.xml
@@ -62,7 +62,6 @@
1.4.200
- 42.2.5.jre7
3.10.0
2.4.0
3.2.0
diff --git a/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java b/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java
similarity index 91%
rename from persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java
rename to persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java
index 78642459d5..431427d332 100644
--- a/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3poDataSource.java
+++ b/persistence-modules/core-java-persistence/src/main/java/com/baeldung/connectionpool/C3p0DataSource.java
@@ -5,7 +5,7 @@ import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
-public class C3poDataSource {
+public class C3p0DataSource {
private static final ComboPooledDataSource cpds = new ComboPooledDataSource();
@@ -24,5 +24,5 @@ public class C3poDataSource {
return cpds.getConnection();
}
- private C3poDataSource(){}
+ private C3p0DataSource(){}
}
\ No newline at end of file
diff --git a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java
new file mode 100644
index 0000000000..1095214e24
--- /dev/null
+++ b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3p0DataSourceUnitTest.java
@@ -0,0 +1,13 @@
+package com.baeldung.connectionpool;
+
+import java.sql.SQLException;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class C3p0DataSourceUnitTest {
+
+ @Test
+ public void givenC3p0DataSourceClass_whenCallGetConnection_thenCorrect() throws SQLException {
+ assertTrue(C3p0DataSource.getConnection().isValid(1));
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java b/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java
deleted file mode 100644
index acad9fe5e4..0000000000
--- a/persistence-modules/core-java-persistence/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.baeldung.connectionpool;
-
-import java.sql.SQLException;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
-
-public class C3poDataSourceUnitTest {
-
- @Test
- public void givenC3poDataSourceClass_whenCalledgetConnection_thenCorrect() throws SQLException {
- assertTrue(C3poDataSource.getConnection().isValid(1));
- }
-}
\ No newline at end of file
diff --git a/persistence-modules/hibernate-jpa/README.md b/persistence-modules/hibernate-jpa/README.md
index 64ec9dcae3..bb079b1705 100644
--- a/persistence-modules/hibernate-jpa/README.md
+++ b/persistence-modules/hibernate-jpa/README.md
@@ -13,3 +13,5 @@ This module contains articles specific to use of Hibernate as a JPA implementati
- [Enabling Transaction Locks in Spring Data JPA](https://www.baeldung.com/java-jpa-transaction-locks)
- [JPA/Hibernate Persistence Context](https://www.baeldung.com/jpa-hibernate-persistence-context)
- [Quick Guide to EntityManager#getReference()](https://www.baeldung.com/jpa-entity-manager-get-reference)
+- [JPA Entities and the Serializable Interface](https://www.baeldung.com/jpa-entities-serializable)
+- [EntityNotFoundException in Hibernate](https://www.baeldung.com/hibernate-entitynotfoundexception)
diff --git a/persistence-modules/hibernate-jpa/pom.xml b/persistence-modules/hibernate-jpa/pom.xml
index 6d300c620b..85bfdac07f 100644
--- a/persistence-modules/hibernate-jpa/pom.xml
+++ b/persistence-modules/hibernate-jpa/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
hibernate-jpa
0.0.1-SNAPSHOT
@@ -91,7 +91,6 @@
5.3.7.Final
8.0.13
- 42.2.11
2.2.3
3.8.0
2.1.7.RELEASE
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java
new file mode 100644
index 0000000000..25d31d50c7
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Category.java
@@ -0,0 +1,42 @@
+package com.baeldung.hibernate.entitynotfoundexception;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+public class Category implements Serializable {
+
+ @Id
+ @Column(unique = true, nullable = false)
+ private long id;
+ private String name;
+ @OneToMany
+ @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
+ private List- items = new ArrayList<>();
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List
- getItems() {
+ return items;
+ }
+
+ public void setItems(List
- items) {
+ this.items = items;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java
new file mode 100644
index 0000000000..3abed00eb8
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/Item.java
@@ -0,0 +1,43 @@
+package com.baeldung.hibernate.entitynotfoundexception;
+
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Entity
+public class Item implements Serializable {
+
+ @Id
+ @Column(unique = true, nullable = false)
+ private long id;
+ private String name;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
+ private Category category;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public void setCategory(Category category) {
+ this.category = category;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java
new file mode 100644
index 0000000000..d89047195c
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/entitynotfoundexception/User.java
@@ -0,0 +1,28 @@
+package com.baeldung.hibernate.entitynotfoundexception;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class User {
+
+ @Id
+ private long id;
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java
new file mode 100644
index 0000000000..b051809ee5
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Account.java
@@ -0,0 +1,41 @@
+package com.baeldung.hibernate.serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Account {
+
+ @Id
+ private long id;
+ private String type;
+ @ManyToOne
+ @JoinColumn(referencedColumnName = "email")
+ private User user;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java
new file mode 100644
index 0000000000..11e7c6f159
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/Email.java
@@ -0,0 +1,38 @@
+package com.baeldung.hibernate.serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.io.Serializable;
+
+@Entity
+public class Email implements Serializable {
+
+ @Id
+ private long id;
+ private String name;
+ private String domain;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java
new file mode 100644
index 0000000000..e7820fe52f
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/User.java
@@ -0,0 +1,36 @@
+package com.baeldung.hibernate.serializable;
+
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+
+@Entity
+public class User {
+
+ @EmbeddedId private UserId userId;
+ private Email email;
+
+
+ public User() {
+ }
+
+ public User(UserId userId, Email email) {
+ this.userId = userId;
+ this.email = email;
+ }
+
+ public UserId getUserId() {
+ return userId;
+ }
+
+ public void setUserId(UserId userId) {
+ this.userId = userId;
+ }
+
+ public Email getEmail() {
+ return email;
+ }
+
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java
new file mode 100644
index 0000000000..7d3d382f67
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/serializable/UserId.java
@@ -0,0 +1,27 @@
+package com.baeldung.hibernate.serializable;
+
+import javax.persistence.Embeddable;
+import java.io.Serializable;
+
+@Embeddable
+public class UserId implements Serializable {
+
+ private String name;
+ private String lastName;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+}
diff --git a/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml b/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml
index c2d5bf59ab..e895ac6ba9 100644
--- a/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml
+++ b/persistence-modules/hibernate-jpa/src/main/resources/META-INF/persistence.xml
@@ -100,4 +100,41 @@
+
+
+ EntityManager serializable persistence unit
+ com.baeldung.hibernate.serializable.Email
+ com.baeldung.hibernate.serializable.Account
+ com.baeldung.hibernate.serializable.User
+ com.baeldung.hibernate.serializable.UserId
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EntityManager EntityNotFoundException persistence unit
+ com.baeldung.hibernate.entitynotfoundexception.Category
+ com.baeldung.hibernate.entitynotfoundexception.Item
+ com.baeldung.hibernate.entitynotfoundexception.User
+ true
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java
new file mode 100644
index 0000000000..bcb4e3eb95
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/entitynotfoundexception/EntityNotFoundExceptionIntegrationTest.java
@@ -0,0 +1,45 @@
+package com.baeldung.hibernate.entitynotfoundexception;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityNotFoundException;
+import javax.persistence.Persistence;
+import java.io.IOException;
+
+public class EntityNotFoundExceptionIntegrationTest {
+
+ private static EntityManager entityManager;
+
+ @Before
+ public void setUp() throws IOException {
+ EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("com.baeldung.hibernate.entitynotfoundexception.h2_persistence_unit");
+ entityManager = entityManagerFactory.createEntityManager();
+ entityManager.getTransaction().begin();
+
+ }
+
+
+ @Test(expected = EntityNotFoundException.class)
+ public void givenNonExistingUserId_whenGetReferenceIsUsed_thenExceptionIsThrown() {
+ User user = entityManager.getReference(User.class, 1L);
+ user.getName();
+ }
+
+ @Test(expected = EntityNotFoundException.class)
+ public void givenItem_whenManyToOneEntityIsMissing_thenExceptionIsThrown() {
+ entityManager.createNativeQuery("Insert into Item (category_id, name, id) values (1, 'test', 1)").executeUpdate();
+ entityManager.flush();
+ Item item = entityManager.find(Item.class, 1L);
+ item.getCategory().getName();
+ }
+
+ @After
+ public void tearDown() {
+ entityManager.close();
+ }
+
+}
diff --git a/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java
new file mode 100644
index 0000000000..696bc23ab0
--- /dev/null
+++ b/persistence-modules/hibernate-jpa/src/test/java/com/baeldung/hibernate/serializable/JPASerializableIntegrationTest.java
@@ -0,0 +1,77 @@
+package com.baeldung.hibernate.serializable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class JPASerializableIntegrationTest {
+
+ private static EntityManager entityManager;
+
+ @Before
+ public void setUp() throws IOException {
+ EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("com.baeldung.hibernate.serializable.h2_persistence_unit");
+ entityManager = entityManagerFactory.createEntityManager();
+ entityManager.getTransaction().begin();
+ }
+
+ @Test
+ public void givenUser_whenPersisted_thenUserWillBeFoundById() {
+ UserId userId = new UserId();
+ userId.setName("John");
+ userId.setLastName("Doe");
+ Email email = new Email();
+ email.setId(1);
+ email.setName("johndoe");
+ email.setDomain("gmail.com");
+ User user = new User(userId, email);
+
+ entityManager.persist(user);
+
+ User userDb = entityManager.find(User.class, userId);
+ assertEquals("johndoe", userDb.getEmail().getName());
+ }
+
+ @Test
+ public void givenAssociation_whenPersisted_thenMultipleAccountsWillBeFoundByEmail() {
+ UserId userId = new UserId();
+ userId.setName("John");
+ userId.setLastName("Doe");
+ Email email = new Email();
+ email.setId(1);
+ email.setName("johndoe");
+ email.setDomain("gmail.com");
+ User user = new User(userId, email);
+ Account account = new Account();
+ account.setType("test");
+ account.setId(10);
+ account.setUser(user);
+ Account account2 = new Account();
+ account2.setType("main");
+ account2.setId(11);
+ account2.setUser(user);
+
+ entityManager.persist(user);
+ entityManager.persist(account);
+ entityManager.persist(account2);
+
+ List userAccounts = entityManager.createQuery("select a from Account a join fetch a.user where a.user.email = :email")
+ .setParameter("email", email)
+ .getResultList();
+ assertEquals(2, userAccounts.size());
+ }
+
+ @After
+ public void tearDown() {
+ entityManager.close();
+ }
+
+}
diff --git a/persistence-modules/hibernate-mapping/pom.xml b/persistence-modules/hibernate-mapping/pom.xml
index 0248f0cb04..805402951e 100644
--- a/persistence-modules/hibernate-mapping/pom.xml
+++ b/persistence-modules/hibernate-mapping/pom.xml
@@ -1,7 +1,7 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4.0.0
hibernate-mapping
hibernate-mapping
@@ -75,7 +75,6 @@
- 42.2.20
5.4.12.Final
2.10.4
3.8.0
diff --git a/persistence-modules/hibernate-ogm/pom.xml b/persistence-modules/hibernate-ogm/pom.xml
index 58098ebb65..9274186cd1 100644
--- a/persistence-modules/hibernate-ogm/pom.xml
+++ b/persistence-modules/hibernate-ogm/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
hibernate-ogm
0.0.1-SNAPSHOT
diff --git a/persistence-modules/hibernate-queries/pom.xml b/persistence-modules/hibernate-queries/pom.xml
index 4a8c578aba..83b2ea4d00 100644
--- a/persistence-modules/hibernate-queries/pom.xml
+++ b/persistence-modules/hibernate-queries/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
hibernate-queries
0.0.1-SNAPSHOT
diff --git a/persistence-modules/hibernate5/pom.xml b/persistence-modules/hibernate5/pom.xml
index 9b4ffff739..d46d2c16d4 100644
--- a/persistence-modules/hibernate5/pom.xml
+++ b/persistence-modules/hibernate5/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
hibernate5
0.0.1-SNAPSHOT
diff --git a/persistence-modules/java-cassandra/README.md b/persistence-modules/java-cassandra/README.md
index f3710c9d69..81ad5aca59 100644
--- a/persistence-modules/java-cassandra/README.md
+++ b/persistence-modules/java-cassandra/README.md
@@ -1,3 +1,4 @@
### Relevant Articles:
- [A Guide to Cassandra with Java](http://www.baeldung.com/cassandra-with-java)
- [Intro to DataStax Java Driver for Apache Cassandra](https://www.baeldung.com/cassandra-datastax-java-driver)
+- [CQL Data Types](https://www.baeldung.com/cassandra-data-types)
diff --git a/persistence-modules/java-cassandra/src/main/resources/data_types.cql b/persistence-modules/java-cassandra/src/main/resources/data_types.cql
new file mode 100644
index 0000000000..67e6fb4634
--- /dev/null
+++ b/persistence-modules/java-cassandra/src/main/resources/data_types.cql
@@ -0,0 +1,75 @@
+CREATE
+ KEYSPACE baeldung
+ WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
+USE baeldung;
+
+CREATE TABLE numeric_types
+(
+ type1 int PRIMARY KEY,
+ type2 bigint,
+ type3 smallint,
+ type4 tinyint,
+ type5 varint,
+ type6 float,
+ type7 double,
+ type8 decimal
+);
+
+CREATE TABLE text_types
+(
+ primaryKey int PRIMARY KEY,
+ type2 text,
+ type3 varchar,
+ type4 ascii
+);
+
+CREATE TABLE date_types
+(
+ primaryKey int PRIMARY KEY,
+ type1 timestamp,
+ type2 time,
+ type3 date,
+ type4 timeuuid,
+ type5 duration
+);
+
+CREATE TABLE other_types
+(
+ primaryKey int PRIMARY KEY,
+ type1 boolean,
+ type2 uuid,
+ type3 blob,
+ type4 inet
+);
+
+CREATE TABLE counter_type
+(
+ primaryKey uuid PRIMARY KEY,
+ type1 counter
+);
+
+CREATE TABLE collection_types
+(
+ primaryKey int PRIMARY KEY,
+ email set
+);
+
+ALTER TABLE collection_types
+ ADD scores list;
+
+ALTER TABLE collection_types
+ ADD address map;
+
+CREATE TABLE tuple_type
+(
+ primaryKey int PRIMARY KEY,
+ type1 tuple
+);
+
+CREATE TYPE user_defined_type ( type1 timestamp, type2 text, type3 text, type4 text);
+
+CREATE TABLE user_type
+(
+ primaryKey int PRIMARY KEY,
+ our_type user_defined_type
+);
diff --git a/persistence-modules/java-cockroachdb/pom.xml b/persistence-modules/java-cockroachdb/pom.xml
index 588ad29cb1..61fb193975 100644
--- a/persistence-modules/java-cockroachdb/pom.xml
+++ b/persistence-modules/java-cockroachdb/pom.xml
@@ -21,8 +21,4 @@
-
- 42.1.4
-
-
\ No newline at end of file
diff --git a/persistence-modules/java-jpa-2/pom.xml b/persistence-modules/java-jpa-2/pom.xml
index daa775f39d..26895f3a87 100644
--- a/persistence-modules/java-jpa-2/pom.xml
+++ b/persistence-modules/java-jpa-2/pom.xml
@@ -44,7 +44,7 @@
org.postgresql
postgresql
- ${postgres.version}
+ ${postgresql.version}
runtime
@@ -138,7 +138,6 @@
5.4.14.Final
2.7.4
- 42.2.5
2.2
3.11.1
3.5.1
diff --git a/persistence-modules/java-jpa-3/pom.xml b/persistence-modules/java-jpa-3/pom.xml
index d81bfca5bd..cecabc10cc 100644
--- a/persistence-modules/java-jpa-3/pom.xml
+++ b/persistence-modules/java-jpa-3/pom.xml
@@ -59,7 +59,7 @@
org.postgresql
postgresql
- ${postgres.version}
+ ${postgresql.version}
runtime
@@ -92,7 +92,6 @@
5.4.14.Final
2.7.4
- 42.2.5
8.0.21
2.2
3.11.1
diff --git a/persistence-modules/java-jpa/pom.xml b/persistence-modules/java-jpa/pom.xml
index bb87c10edb..9a55633773 100644
--- a/persistence-modules/java-jpa/pom.xml
+++ b/persistence-modules/java-jpa/pom.xml
@@ -44,7 +44,7 @@
org.postgresql
postgresql
- ${postgres.version}
+ ${postgresql.version}
runtime
@@ -104,7 +104,6 @@
5.4.0.Final
2.7.4
- 42.2.5
2.2
3.3.3
3.0.0
diff --git a/persistence-modules/jooq/pom.xml b/persistence-modules/jooq/pom.xml
index f4751f412a..c66be9db77 100644
--- a/persistence-modules/jooq/pom.xml
+++ b/persistence-modules/jooq/pom.xml
@@ -45,7 +45,6 @@
3.13.4
- 42.2.16
1.4.200
diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml
index 5e0cd59d18..a8f1d5c103 100644
--- a/persistence-modules/pom.xml
+++ b/persistence-modules/pom.xml
@@ -57,7 +57,9 @@
spring-boot-persistence
spring-boot-persistence-h2
spring-boot-persistence-mongodb
+ spring-data-arangodb
spring-data-cassandra
+ spring-data-cassandra-test
spring-data-cassandra-reactive
spring-data-cosmosdb
spring-data-couchbase-2
@@ -77,6 +79,7 @@
spring-data-jdbc
spring-data-keyvalue
spring-data-mongodb
+ spring-data-mongodb-reactive
spring-data-neo4j
spring-data-redis
spring-data-solr
@@ -86,7 +89,7 @@
spring-jpa-2
spring-jdbc
spring-jooq
-
+ spring-mybatis
spring-persistence-simple
@@ -94,6 +97,8 @@
5.2.17.Final
+ 42.2.20
+
2.22.2
5.6.2
diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml
index c407482788..8079d56cbd 100644
--- a/persistence-modules/redis/pom.xml
+++ b/persistence-modules/redis/pom.xml
@@ -59,7 +59,6 @@
3.13.1
3.3.0
4.1.50.Final
-
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
index 0466eaac79..134cda6628 100644
--- a/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
+++ b/persistence-modules/spring-boot-persistence-h2/src/main/resources/application.properties
@@ -2,10 +2,11 @@ spring.datasource.url=jdbc:h2:mem:mydb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
+spring.jpa.defer-datasource-initialization=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.validator.apply_to_ddl=false
#spring.jpa.properties.hibernate.check_nullability=true
spring.h2.console.enabled=true
-spring.h2.console.path=/h2-console
\ No newline at end of file
+spring.h2.console.path=/h2-console
diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
index 21cf56172e..354aa6fe56 100644
--- a/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
+++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/mongodb/ManualEmbeddedMongoDbIntegrationTest.java
@@ -5,8 +5,8 @@ import com.mongodb.DBObject;
import com.mongodb.client.MongoClients;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodStarter;
-import de.flapdoodle.embed.mongo.config.IMongodConfig;
-import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
+import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig;
+import de.flapdoodle.embed.mongo.config.MongodConfig;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
@@ -36,7 +36,9 @@ class ManualEmbeddedMongoDbIntegrationTest {
String ip = "localhost";
int randomPort = SocketUtils.findAvailableTcpPort();
- IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
+ ImmutableMongodConfig mongodConfig = MongodConfig
+ .builder()
+ .version(Version.Main.PRODUCTION)
.net(new Net(ip, randomPort, Network.localhostIsIPv6()))
.build();
diff --git a/persistence-modules/spring-data-arangodb/README.md b/persistence-modules/spring-data-arangodb/README.md
new file mode 100644
index 0000000000..632d9a256e
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/README.md
@@ -0,0 +1,6 @@
+=========
+
+## Spring Data ArangoDB
+
+
+### Relevant Articles:
diff --git a/persistence-modules/spring-data-arangodb/pom.xml b/persistence-modules/spring-data-arangodb/pom.xml
new file mode 100644
index 0000000000..562f06ae40
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/pom.xml
@@ -0,0 +1,29 @@
+
+
+ 4.0.0
+ spring-data-arangodb
+ spring-data-arangodb
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ com.arangodb
+ arangodb-spring-data
+ 3.5.0
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile b/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile
new file mode 100644
index 0000000000..8edb2bbbf6
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/Dockerfile
@@ -0,0 +1,7 @@
+FROM arangodb:3.8.0
+
+COPY init-session.js /docker-entrypoint-initdb.d/
+
+EXPOSE 8529
+
+ENV ARANGO_ROOT_PASSWORD=password
diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js b/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js
new file mode 100644
index 0000000000..2e968884cc
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/init-session.js
@@ -0,0 +1 @@
+rs.initiate();
diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh
new file mode 100644
index 0000000000..b1a4cfb9d0
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-setup.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+docker image build -t spring-data-arangodb:live-test .
+
+docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=password --name spring-data-arangodb-live-test spring-data-arangodb:live-test
diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh
new file mode 100644
index 0000000000..2199cf7403
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test-teardown.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+docker stop spring-data-arangodb-live-test
+docker rm spring-data-arangodb-live-test
diff --git a/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh
new file mode 100644
index 0000000000..307a68a3bd
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/live-test/resources/live-test.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+mvn clean compile test -P live-all -f ../../../pom.xml
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java
new file mode 100644
index 0000000000..355001df06
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/ArangoDbSpringDataApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.arangodb;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ArangoDbSpringDataApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ArangoDbSpringDataApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java
new file mode 100644
index 0000000000..3aae10ce60
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/configuration/ArangoDbConfiguration.java
@@ -0,0 +1,24 @@
+package com.baeldung.arangodb.configuration;
+
+import com.arangodb.ArangoDB;
+import com.arangodb.springframework.annotation.EnableArangoRepositories;
+import com.arangodb.springframework.config.ArangoConfiguration;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableArangoRepositories(basePackages = {"com.baeldung"})
+public class ArangoDbConfiguration implements ArangoConfiguration {
+
+ @Override
+ public ArangoDB.Builder arango() {
+ return new ArangoDB.Builder()
+ .host("127.0.0.1", 8529)
+ .user("root")
+ .password("password");
+ }
+
+ @Override
+ public String database() {
+ return "baeldung-database";
+ }
+}
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java
new file mode 100644
index 0000000000..ea7ee1d2a6
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Article.java
@@ -0,0 +1,86 @@
+package com.baeldung.arangodb.model;
+
+import com.arangodb.springframework.annotation.ArangoId;
+import com.arangodb.springframework.annotation.Document;
+import com.arangodb.springframework.annotation.Relations;
+import org.springframework.data.annotation.Id;
+
+import java.time.ZonedDateTime;
+import java.util.Collection;
+
+@Document("articles")
+public class Article {
+
+ @Id
+ private String id;
+
+ @ArangoId
+ private String arangoId;
+
+ private String name;
+ private String author;
+ private ZonedDateTime publishDate;
+ private String htmlContent;
+
+ @Relations(edges = ArticleLink.class, lazy = true)
+ private Collection authors;
+
+ public Article() {
+ super();
+ }
+
+ public Article(String name, String author, ZonedDateTime publishDate, String htmlContent) {
+ this.name = name;
+ this.author = author;
+ this.publishDate = publishDate;
+ this.htmlContent = htmlContent;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getArangoId() {
+ return arangoId;
+ }
+
+ public void setArangoId(String arangoId) {
+ this.arangoId = arangoId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public ZonedDateTime getPublishDate() {
+ return publishDate;
+ }
+
+ public void setPublishDate(ZonedDateTime publishDate) {
+ this.publishDate = publishDate;
+ }
+
+ public String getHtmlContent() {
+ return htmlContent;
+ }
+
+ public void setHtmlContent(String htmlContent) {
+ this.htmlContent = htmlContent;
+ }
+}
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java
new file mode 100644
index 0000000000..18a35815e1
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/ArticleLink.java
@@ -0,0 +1,39 @@
+package com.baeldung.arangodb.model;
+
+import com.arangodb.springframework.annotation.Edge;
+import com.arangodb.springframework.annotation.From;
+import com.arangodb.springframework.annotation.To;
+
+@Edge
+public class ArticleLink {
+
+ @From
+ private Article article;
+
+ @To
+ private Author author;
+
+ public ArticleLink() {
+ }
+
+ public ArticleLink(Article article, Author author) {
+ this.article = article;
+ this.author = author;
+ }
+
+ public Article getArticle() {
+ return article;
+ }
+
+ public void setArticle(Article article) {
+ this.article = article;
+ }
+
+ public Author getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(Author author) {
+ this.author = author;
+ }
+}
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java
new file mode 100644
index 0000000000..6e9fed3312
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/model/Author.java
@@ -0,0 +1,49 @@
+package com.baeldung.arangodb.model;
+
+import com.arangodb.springframework.annotation.ArangoId;
+import com.arangodb.springframework.annotation.Document;
+import org.springframework.data.annotation.Id;
+
+@Document("articles")
+public class Author {
+
+ @Id
+ private String id;
+
+ @ArangoId
+ private String arangoId;
+
+ private String name;
+
+ public Author() {
+ super();
+ }
+
+ public Author(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getArangoId() {
+ return arangoId;
+ }
+
+ public void setArangoId(String arangoId) {
+ this.arangoId = arangoId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java
new file mode 100644
index 0000000000..fdc434cae4
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/java/com/baeldung/arangodb/repository/ArticleRepository.java
@@ -0,0 +1,17 @@
+package com.baeldung.arangodb.repository;
+
+import com.arangodb.springframework.annotation.Query;
+import com.arangodb.springframework.repository.ArangoRepository;
+import com.baeldung.arangodb.model.Article;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ArticleRepository extends ArangoRepository {
+
+ Iterable findByAuthor(String author);
+
+ @Query("FOR a IN articles FILTER a.author == @author SORT a.publishDate ASC RETURN a")
+ Iterable getByAuthor(@Param("author") String author);
+
+}
diff --git a/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties b/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties
new file mode 100644
index 0000000000..7f8ded478c
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/main/resources/arangodb.properties
@@ -0,0 +1,3 @@
+arangodb.hosts=127.0.0.1:8529
+arangodb.user=root
+arangodb.password=password
\ No newline at end of file
diff --git a/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java b/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..1d4258688a
--- /dev/null
+++ b/persistence-modules/spring-data-arangodb/src/test/java/com/baeldung/arangodb/ArticleRepositoryIntegrationTest.java
@@ -0,0 +1,113 @@
+package com.baeldung.arangodb;
+
+import com.baeldung.arangodb.model.Article;
+import com.baeldung.arangodb.repository.ArticleRepository;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@SpringBootTest
+public class ArticleRepositoryIntegrationTest {
+
+ @Autowired
+ ArticleRepository articleRepository;
+
+ @Test
+ public void givenNewArticle_whenSaveInArangoDb_thenDataIsCorrect() {
+ Article newArticle = new Article(
+ "ArangoDb with Spring Data",
+ "Baeldung Writer",
+ ZonedDateTime.now(),
+ "Some HTML content"
+ );
+
+ Article savedArticle = articleRepository.save(newArticle);
+
+ assertNotNull(savedArticle.getId());
+ assertNotNull(savedArticle.getArangoId());
+
+ assertEquals(savedArticle.getName(), newArticle.getName());
+ assertEquals(savedArticle.getAuthor(), newArticle.getAuthor());
+ assertEquals(savedArticle.getPublishDate(), newArticle.getPublishDate());
+ assertEquals(savedArticle.getHtmlContent(), newArticle.getHtmlContent());
+ }
+
+ @Test
+ public void givenArticleId_whenReadFromArangoDb_thenDataIsCorrect() {
+ Article newArticle = new Article(
+ "ArangoDb with Spring Data",
+ "Baeldung Writer",
+ ZonedDateTime.now(),
+ "Some HTML content"
+ );
+
+ Article savedArticle = articleRepository.save(newArticle);
+
+ String articleId = savedArticle.getId();
+
+ Optional article = articleRepository.findById(articleId);
+ assertTrue(article.isPresent());
+
+ Article foundArticle = article.get();
+
+ assertEquals(foundArticle.getId(), articleId);
+ assertEquals(foundArticle.getArangoId(), savedArticle.getArangoId());
+ assertEquals(foundArticle.getName(), savedArticle.getName());
+ assertEquals(foundArticle.getAuthor(), savedArticle.getAuthor());
+ assertEquals(foundArticle.getPublishDate(), savedArticle.getPublishDate());
+ assertEquals(foundArticle.getHtmlContent(), savedArticle.getHtmlContent());
+ }
+
+ @Test
+ public void givenArticleId_whenDeleteFromArangoDb_thenDataIsGone() {
+ Article newArticle = new Article(
+ "ArangoDb with Spring Data",
+ "Baeldung Writer",
+ ZonedDateTime.now(),
+ "Some HTML content"
+ );
+
+ Article savedArticle = articleRepository.save(newArticle);
+
+ String articleId = savedArticle.getId();
+
+ articleRepository.deleteById(articleId);
+
+ Optional article = articleRepository.findById(articleId);
+ assertFalse(article.isPresent());
+ }
+
+ @Test
+ public void givenAuthorName_whenGetByAuthor_thenListOfArticles() {
+ Article newArticle = new Article(
+ "ArangoDb with Spring Data",
+ "Baeldung Writer",
+ ZonedDateTime.now(),
+ "Some HTML content"
+ );
+ articleRepository.save(newArticle);
+
+ Iterable articlesByAuthor = articleRepository.findByAuthor(newArticle.getAuthor());
+ List articlesByAuthorList = new ArrayList<>();
+ articlesByAuthor.forEach(articlesByAuthorList::add);
+
+ assertEquals(1, articlesByAuthorList.size());
+
+ Article foundArticle = articlesByAuthorList.get(0);
+ assertEquals(foundArticle.getName(), newArticle.getName());
+ assertEquals(foundArticle.getAuthor(), newArticle.getAuthor());
+ assertEquals(foundArticle.getPublishDate(), newArticle.getPublishDate());
+ assertEquals(foundArticle.getHtmlContent(), newArticle.getHtmlContent());
+ }
+
+}
diff --git a/persistence-modules/spring-data-cassandra-reactive/pom.xml b/persistence-modules/spring-data-cassandra-reactive/pom.xml
index 630e13583b..5dd5ab4b69 100644
--- a/persistence-modules/spring-data-cassandra-reactive/pom.xml
+++ b/persistence-modules/spring-data-cassandra-reactive/pom.xml
@@ -54,7 +54,6 @@
2.2.6.RELEASE
3.11.2.0
-
\ No newline at end of file
diff --git a/persistence-modules/spring-data-cassandra-test/README.md b/persistence-modules/spring-data-cassandra-test/README.md
new file mode 100644
index 0000000000..cfad972cb3
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/README.md
@@ -0,0 +1,16 @@
+## Spring @DataCassandraTest
+
+### Build the Project
+```
+mvn clean install
+```
+
+### Prerequisite To Run Test
+- Docker Engine must be running on the system
+- Docker Compose must be installed
+
+### Run Tests Directly
+```
+mvn test
+```
+
diff --git a/persistence-modules/spring-data-cassandra-test/pom.xml b/persistence-modules/spring-data-cassandra-test/pom.xml
new file mode 100644
index 0000000000..f2cbc834de
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+ spring-data-cassandra-test
+ spring-data-cassandra-test
+ jar
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+ 2.5.3
+ 1.18.18
+ 4.13.0
+ 4.3.1.0
+ 1.15.3
+ 1.5.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-cassandra
+ ${spring-boot-starter-data-cassandra.version}
+
+
+
+ com.datastax.oss
+ java-driver-core
+ ${java-driver-core.version}
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+ com.datastax.oss
+ native-protocol
+ ${native-protocol.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.testcontainers
+ testcontainers
+ ${testcontainers.version}
+ test
+
+
+
+ org.testcontainers
+ cassandra
+ ${testcontainers.version}
+ test
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java
new file mode 100644
index 0000000000..f0e6ba3c88
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/CassandraDataTestApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.data.cassandra.test;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class CassandraDataTestApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CassandraDataTestApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java
new file mode 100644
index 0000000000..6c4dfe055c
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/api/InventoryController.java
@@ -0,0 +1,43 @@
+package com.baeldung.spring.data.cassandra.test.api;
+
+import com.baeldung.spring.data.cassandra.test.domain.Vehicle;
+import com.baeldung.spring.data.cassandra.test.service.InventoryService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/v1/api/inventory")
+public class InventoryController {
+ private InventoryService inventoryService;
+
+ public InventoryController(InventoryService inventoryService) {
+ this.inventoryService = inventoryService;
+ }
+
+ @GetMapping("/vehicles")
+ public List getVehicles() {
+ return this.inventoryService.getVehicles();
+ }
+
+ @PostMapping("/vehicles")
+ public void addVehicles(@RequestBody List vehicles) {
+ this.inventoryService.addVehicles(vehicles);
+ }
+
+ @PutMapping("/vehicles")
+ public void updateVehicles(@RequestBody List vehicles) {
+ this.inventoryService.updateVehicles(vehicles);
+ }
+
+ @PutMapping("/vehicles/{vin}")
+ public void updateVehicles(@PathVariable(name = "vin") String vin,
+ @RequestBody Vehicle vehicles) {
+ this.inventoryService.updateVehicle(vin, vehicles);
+ }
+
+ @DeleteMapping("/vehicles/{vin}")
+ public void removeVehicle(@PathVariable(name = "vin") String vin) {
+ this.inventoryService.deleteVehicle(vin);
+ }
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java
new file mode 100644
index 0000000000..d362d67a68
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/config/CassandraConfig.java
@@ -0,0 +1,24 @@
+package com.baeldung.spring.data.cassandra.test.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
+
+@Configuration
+public class CassandraConfig extends AbstractCassandraConfiguration {
+ @Override
+ protected String getKeyspaceName() {
+ return "inventory";
+ }
+
+ @Override
+ public String getContactPoints() {
+ return "localhost";
+ }
+
+ @Override
+ protected String getLocalDataCenter() {
+ return "datacenter1";
+ }
+
+
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java
new file mode 100644
index 0000000000..aa16440c83
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/domain/Vehicle.java
@@ -0,0 +1,17 @@
+package com.baeldung.spring.data.cassandra.test.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.cassandra.core.mapping.Table;
+
+@Data
+@Table("vehicles")
+@AllArgsConstructor
+public class Vehicle {
+ @Id
+ private String vin;
+ private Integer year;
+ private String make;
+ private String model;
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java
new file mode 100644
index 0000000000..c6fe4a91fe
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/repository/InventoryRepository.java
@@ -0,0 +1,26 @@
+package com.baeldung.spring.data.cassandra.test.repository;
+
+import com.baeldung.spring.data.cassandra.test.domain.Vehicle;
+import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
+import org.springframework.data.cassandra.repository.Consistency;
+import org.springframework.data.cassandra.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface InventoryRepository extends CrudRepository {
+
+ @Query("select * from vehicles")
+ @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM)
+ List findAllVehicles();
+
+ @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM)
+ Optional findByVin(@Param("vin") String vin);
+
+ @Consistency(DefaultConsistencyLevel.LOCAL_QUORUM)
+ void deleteByVin(String vin);
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java
new file mode 100644
index 0000000000..75a198738a
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/java/com/baeldung/spring/data/cassandra/test/service/InventoryService.java
@@ -0,0 +1,47 @@
+package com.baeldung.spring.data.cassandra.test.service;
+
+import com.baeldung.spring.data.cassandra.test.domain.Vehicle;
+import com.baeldung.spring.data.cassandra.test.repository.InventoryRepository;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class InventoryService {
+ private final InventoryRepository inventoryRepository;
+
+ public InventoryService(InventoryRepository inventoryRepository) {
+ this.inventoryRepository = inventoryRepository;
+ }
+
+ public List getVehicles() {
+ return this.inventoryRepository.findAllVehicles();
+ }
+
+ public Vehicle getVehicle(String vin) {
+ return this.inventoryRepository.findByVin(vin).orElse(null);
+ }
+
+ public void addVehicles(List vehicles) {
+ this.inventoryRepository.saveAll(vehicles);
+ }
+
+ public void updateVehicles(List vehicles) {
+ this.inventoryRepository.saveAll(vehicles);
+ }
+
+ public void updateVehicle(String vin, Vehicle vehicle) {
+ Vehicle existingVehicle = this.inventoryRepository.findByVin(vin)
+ .orElseThrow(() -> new RuntimeException("Vehicle not found"));
+
+ existingVehicle.setMake(vehicle.getMake());
+ existingVehicle.setYear(vehicle.getYear());
+ existingVehicle.setModel(vehicle.getModel());
+
+ this.inventoryRepository.save(existingVehicle);
+ }
+
+ public void deleteVehicle(String vin) {
+ this.inventoryRepository.deleteByVin(vin);
+ }
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/resources/application.yml b/persistence-modules/spring-data-cassandra-test/src/main/resources/application.yml
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml b/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml
new file mode 100644
index 0000000000..612e9cac40
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/main/resources/logback.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java b/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java
new file mode 100644
index 0000000000..80958d01d2
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/test/java/com/baeldung/spring/data/cassandra/test/service/InventoryServiceLiveTest.java
@@ -0,0 +1,71 @@
+package com.baeldung.spring.data.cassandra.test.service;
+
+import com.baeldung.spring.data.cassandra.test.config.CassandraConfig;
+import com.baeldung.spring.data.cassandra.test.domain.Vehicle;
+import com.baeldung.spring.data.cassandra.test.repository.InventoryRepository;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.testcontainers.containers.DockerComposeContainer;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@DataCassandraTest
+@Import(CassandraConfig.class)
+public class InventoryServiceLiveTest {
+ @Autowired
+ private InventoryRepository repository;
+
+ private InventoryService inventoryService;
+
+ @ClassRule
+ public static DockerComposeContainer environment =
+ new DockerComposeContainer(new File("src/test/resources/compose-test.yml"));
+
+ @Before
+ public void setUp() {
+ inventoryService = new InventoryService(this.repository);
+ }
+
+ @Test
+ public void givenVehiclesInDBInitially_whenRetrieved_thenReturnAllVehiclesFromDB() {
+ List vehicles = inventoryService.getVehicles();
+ assertThat(vehicles).isNotNull();
+ assertThat(vehicles).isNotEmpty();
+ }
+
+ @Test
+ public void whenAddMoreVehiclesToDB_thenRetrievalReturnsAllVehicles() {
+ String vin1 = "ABC123";
+ String vin2 = "XYZ123";
+ List vehicles = Arrays.asList(
+ new Vehicle(vin1, 2020, "Toyota", "Camry"),
+ new Vehicle(vin2, 2019, "Honda", "Prius")
+ );
+ inventoryService.addVehicles(vehicles);
+
+ vehicles = inventoryService.getVehicles();
+ assertThat(vehicles).isNotNull();
+ assertThat(vehicles).isNotEmpty();
+ assertThat(vehicles.size()).isEqualTo(5);
+
+ Vehicle vehicle = inventoryService.getVehicle(vin1);
+ assertThat(vehicle).isNotNull();
+ assertThat(vehicle.getVin()).isEqualTo(vin1);
+
+ vehicle = inventoryService.getVehicle(vin2);
+ assertThat(vehicle).isNotNull();
+ assertThat(vehicle.getVin()).isEqualTo(vin2);
+ }
+}
diff --git a/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql b/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql
new file mode 100644
index 0000000000..6f3821ed83
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/test/resources/bootstrap-test.cql
@@ -0,0 +1,20 @@
+CREATE KEYSPACE inventory
+WITH replication = {
+ 'class' : 'NetworkTopologyStrategy',
+ 'datacenter1' : 3
+};
+
+use inventory;
+
+CREATE TABLE vehicles (
+ vin text PRIMARY KEY,
+ year int,
+ make varchar,
+ model varchar
+);
+
+consistency LOCAL_QUORUM;
+
+insert into vehicles (vin, year, make, model) values ('387KSJHFK23874GH', 2020, 'Ford', 'F-150');
+insert into vehicles (vin, year, make, model) values ('534HNDHFK23873EF', 2020, 'Honda', 'Accord');
+insert into vehicles (vin, year, make, model) values ('953TOYJEK23853DB', 2020, 'Toyota', 'Camry');
\ No newline at end of file
diff --git a/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml b/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml
new file mode 100644
index 0000000000..50f0f448f6
--- /dev/null
+++ b/persistence-modules/spring-data-cassandra-test/src/test/resources/compose-test.yml
@@ -0,0 +1,79 @@
+version: '2.1'
+services:
+ cassandra1:
+ image: cassandra:3.11.10
+ hostname: cassandra1
+ networks:
+ - cassandranet
+ ports:
+ - "9042:9042"
+ environment:
+ CASSANDRA_SEEDS: "cassandra1"
+ CASSANDRA_DC: datacenter1
+ CASSANDRA_RACK: rack1
+ CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch
+ healthcheck:
+ test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ]
+ interval: 15s
+ timeout: 10s
+ retries: 10
+
+ cassandra2:
+ image: cassandra:3.11.10
+ hostname: cassandra2
+ networks:
+ - cassandranet
+ depends_on:
+ cassandra1:
+ condition: service_healthy
+ ports:
+ - "9043:9042"
+ environment:
+ CASSANDRA_SEEDS: "cassandra1"
+ CASSANDRA_DC: datacenter1
+ CASSANDRA_RACK: rack1
+ CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch
+ healthcheck:
+ test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ]
+ interval: 15s
+ timeout: 10s
+ retries: 10
+
+ cassandra3:
+ image: cassandra:3.11.10
+ hostname: cassandra3
+ networks:
+ - cassandranet
+ depends_on:
+ cassandra2:
+ condition: service_healthy
+ ports:
+ - "9044:9042"
+ environment:
+ CASSANDRA_SEEDS: "cassandra1"
+ CASSANDRA_DC: datacenter1
+ CASSANDRA_RACK: rack1
+ CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch
+ healthcheck:
+ test: [ "CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces" ]
+ interval: 15s
+ timeout: 10s
+ retries: 10
+
+ cassandra-load-keyspace:
+ image: cassandra:3.11.10
+ networks:
+ - cassandranet
+ depends_on:
+ cassandra1:
+ condition: service_healthy
+ cassandra2:
+ condition: service_healthy
+ cassandra3:
+ condition: service_healthy
+ volumes:
+ - ./bootstrap-test.cql:/schema.cql
+ command: /bin/bash -c "echo loading cassandra keyspace && cqlsh cassandra1 -f /schema.cql"
+
+networks:
+ cassandranet:
\ No newline at end of file
diff --git a/persistence-modules/spring-data-cassandra/pom.xml b/persistence-modules/spring-data-cassandra/pom.xml
index e43e3821ae..d21541abf8 100644
--- a/persistence-modules/spring-data-cassandra/pom.xml
+++ b/persistence-modules/spring-data-cassandra/pom.xml
@@ -105,7 +105,6 @@
2.1.9.2
2.1.9.2
2.0-0
-
\ No newline at end of file
diff --git a/persistence-modules/spring-data-dynamodb/pom.xml b/persistence-modules/spring-data-dynamodb/pom.xml
index 28127179c2..148215b68a 100644
--- a/persistence-modules/spring-data-dynamodb/pom.xml
+++ b/persistence-modules/spring-data-dynamodb/pom.xml
@@ -183,6 +183,7 @@
1.11.86
https://s3-us-west-2.amazonaws.com/dynamodb-local/release
3.1.1
+ 2.4.7
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jdbc/pom.xml b/persistence-modules/spring-data-jdbc/pom.xml
index 2672405dd6..2b4c6d21aa 100644
--- a/persistence-modules/spring-data-jdbc/pom.xml
+++ b/persistence-modules/spring-data-jdbc/pom.xml
@@ -2,14 +2,15 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+ spring-data-jdbc
+ spring-data-jdbc
+
com.baeldung
parent-boot-2
0.0.1-SNAPSHOT
../../parent-boot-2
- spring-data-jdbc
- spring-data-jdbc
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
index 18ef8d4e60..3829f676d3 100644
--- a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
@@ -1,5 +1,3 @@
-spring.main.allow-bean-definition-overriding=true
-
spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
diff --git a/persistence-modules/spring-data-jpa-enterprise/pom.xml b/persistence-modules/spring-data-jpa-enterprise/pom.xml
index 4367a11222..a76bcb8b0a 100644
--- a/persistence-modules/spring-data-jpa-enterprise/pom.xml
+++ b/persistence-modules/spring-data-jpa-enterprise/pom.xml
@@ -2,7 +2,6 @@
-
4.0.0
spring-data-jpa-enterprise
spring-data-jpa-enterprise
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
index 29326c6061..32d3e640f9 100644
--- a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
@@ -10,7 +10,6 @@ spring.datasource.url=jdbc:h2:mem:baeldung
#spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true
-spring.main.allow-bean-definition-overriding=true
#hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.id.new_generator_mappings=false
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java
new file mode 100644
index 0000000000..c976590966
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.boot.daos;
+
+import com.baeldung.boot.Application;
+import com.baeldung.boot.daos.user.UserRepository;
+import com.baeldung.boot.domain.User;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@ActiveProfiles("tc-jdbc")
+@SpringBootTest(classes = Application.class)
+public class UserRepositoryTCJdbcLiveTest {
+
+ final String USER_EMAIL = "email@example.com";
+ final String USER_EMAIL2 = "email2@example.com";
+ final String USER_EMAIL3 = "email3@example.com";
+ final String USER_EMAIL4 = "email4@example.com";
+ final Integer INACTIVE_STATUS = 0;
+ final Integer ACTIVE_STATUS = 1;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Test
+ @Transactional
+ public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers() {
+ userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE1", LocalDate.now(), USER_EMAIL2, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL3, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE3", LocalDate.now(), USER_EMAIL4, ACTIVE_STATUS));
+ userRepository.flush();
+
+ int updatedUsersSize = userRepository.updateUserSetStatusForNameNativePostgres(INACTIVE_STATUS, "SAMPLE");
+
+ assertThat(updatedUsersSize).isEqualTo(2);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml
new file mode 100644
index 0000000000..ad5906fa6e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml
@@ -0,0 +1,6 @@
+spring:
+ datasource:
+ url: jdbc:tc:postgresql:11.1:///integration-tests-db
+ jpa:
+ hibernate:
+ ddl-auto: create
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..f37cce0b29
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-2/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.jpa.defer-datasource-initialization=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties
index 72fc330767..9bb870895d 100644
--- a/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-query/src/main/resources/application.properties
@@ -1 +1,2 @@
-spring.jpa.show-sql=true
\ No newline at end of file
+spring.jpa.show-sql=true
+spring.jpa.defer-datasource-initialization=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-repo-2/pom.xml b/persistence-modules/spring-data-jpa-repo-2/pom.xml
index b382e35e28..12e178dd49 100644
--- a/persistence-modules/spring-data-jpa-repo-2/pom.xml
+++ b/persistence-modules/spring-data-jpa-repo-2/pom.xml
@@ -42,4 +42,5 @@
29.0-jre
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties
index 65d7b0bf29..ae1afe6e98 100644
--- a/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-repo/src/main/resources/application.properties
@@ -1,4 +1,5 @@
spring.jpa.show-sql=true
+spring.jpa.defer-datasource-initialization=true
#MySql
#spring.datasource.url=jdbc:mysql://localhost:3306/baeldung
#spring.datasource.username=baeldung
diff --git a/persistence-modules/spring-data-keyvalue/pom.xml b/persistence-modules/spring-data-keyvalue/pom.xml
index aa2696dd12..704be15b4a 100644
--- a/persistence-modules/spring-data-keyvalue/pom.xml
+++ b/persistence-modules/spring-data-keyvalue/pom.xml
@@ -26,4 +26,5 @@
spring-boot-starter-test
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/README.md b/persistence-modules/spring-data-mongodb-reactive/README.md
new file mode 100644
index 0000000000..0931161700
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/README.md
@@ -0,0 +1,12 @@
+## Spring Data Reactive Project
+
+This module contains articles about reactive Spring 5 Data
+
+### The Course
+The "REST With Spring" Classes: http://bit.ly/restwithspring
+
+### Relevant Articles
+- [Spring Data Reactive Repositories with MongoDB](https://www.baeldung.com/spring-data-mongodb-reactive)
+- [Spring Data MongoDB Tailable Cursors](https://www.baeldung.com/spring-data-mongodb-tailable-cursors)
+- [A Quick Look at R2DBC with Spring Data](https://www.baeldung.com/spring-data-r2dbc)
+- [Spring Data Reactive Repositories with Couchbase](https://www.baeldung.com/spring-data-reactive-couchbase)
diff --git a/persistence-modules/spring-data-mongodb-reactive/pom.xml b/persistence-modules/spring-data-mongodb-reactive/pom.xml
new file mode 100644
index 0000000000..9fb22b6033
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/pom.xml
@@ -0,0 +1,157 @@
+
+
+ 4.0.0
+ spring-data-mongodb-reactive
+ spring-data-mongodb-reactive
+ jar
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ io.projectreactor
+ reactor-core
+ ${reactor-core.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-couchbase-reactive
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb-reactive
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+
+
+ io.projectreactor
+ reactor-test
+ test
+
+
+ io.reactivex.rxjava2
+ rxjava
+
+
+ org.springframework
+ spring-test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework
+ spring-tx
+ ${spring-tx.version}
+
+
+ org.springframework.data
+ spring-data-r2dbc
+ ${spring-data-r2dbc.version}
+
+
+ io.r2dbc
+ r2dbc-h2
+ ${r2dbc-h2.version}
+
+
+ com.h2database
+ h2
+ ${h2.version}
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+
+
+ com.couchbase.mock
+ CouchbaseMock
+ ${couchbaseMock.version}
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java.version}
+ ${java.version}
+
+
+
+
+ default-compile
+ none
+
+
+
+ default-testCompile
+ none
+
+
+ java-compile
+ compile
+
+ compile
+
+
+
+ java-test-compile
+ test-compile
+
+ testCompile
+
+
+
+
+
+
+
+
+ 5.2.2.RELEASE
+ 1.0.0.RELEASE
+ 0.8.1.RELEASE
+ 4.5.2
+ 1.4.200
+ 1.5.23
+ 3.3.1.RELEASE
+
+ 2.2.6.RELEASE
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java
new file mode 100644
index 0000000000..4e5bf9d5dc
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/ReactiveCouchbaseApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.couchbase;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
+
+@SpringBootApplication(exclude = MongoAutoConfiguration.class)
+public class ReactiveCouchbaseApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ReactiveCouchbaseApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java
new file mode 100644
index 0000000000..81f19eebd6
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/CouchbaseProperties.java
@@ -0,0 +1,43 @@
+package com.baeldung.couchbase.configuration;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+import java.util.Collections;
+import java.util.List;
+
+@Configuration
+@PropertySource("classpath:couchbase.properties")
+public class CouchbaseProperties {
+
+ private final List bootstrapHosts;
+ private final String bucketName;
+ private final String bucketPassword;
+ private final int port;
+
+ public CouchbaseProperties(@Value("${spring.couchbase.bootstrap-hosts}") final List bootstrapHosts, @Value("${spring.couchbase.bucket.name}") final String bucketName, @Value("${spring.couchbase.bucket.password}") final String bucketPassword,
+ @Value("${spring.couchbase.port}") final int port) {
+ this.bootstrapHosts = Collections.unmodifiableList(bootstrapHosts);
+ this.bucketName = bucketName;
+ this.bucketPassword = bucketPassword;
+ this.port = port;
+ }
+
+ public List getBootstrapHosts() {
+ return bootstrapHosts;
+ }
+
+ public String getBucketName() {
+ return bucketName;
+ }
+
+ public String getBucketPassword() {
+ return bucketPassword;
+ }
+
+ public int getPort() {
+ return port;
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java
new file mode 100644
index 0000000000..059bd36cae
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/N1QLReactiveCouchbaseConfiguration.java
@@ -0,0 +1,15 @@
+package com.baeldung.couchbase.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories;
+
+@Configuration
+@EnableReactiveCouchbaseRepositories("com.baeldung.couchbase.domain.repository.n1ql")
+@Primary
+public class N1QLReactiveCouchbaseConfiguration extends ReactiveCouchbaseConfiguration {
+
+ public N1QLReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) {
+ super(couchbaseProperties);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java
new file mode 100644
index 0000000000..a51b19ee22
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ReactiveCouchbaseConfiguration.java
@@ -0,0 +1,48 @@
+package com.baeldung.couchbase.configuration;
+
+import com.couchbase.client.java.env.CouchbaseEnvironment;
+import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.couchbase.config.AbstractReactiveCouchbaseConfiguration;
+import org.springframework.data.couchbase.config.BeanNames;
+import org.springframework.data.couchbase.repository.support.IndexManager;
+
+import java.util.List;
+
+public abstract class ReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration {
+
+ private CouchbaseProperties couchbaseProperties;
+
+ public ReactiveCouchbaseConfiguration(final CouchbaseProperties couchbaseProperties) {
+ this.couchbaseProperties = couchbaseProperties;
+ }
+
+ @Override
+ protected List getBootstrapHosts() {
+ return couchbaseProperties.getBootstrapHosts();
+ }
+
+ @Override
+ protected String getBucketName() {
+ return couchbaseProperties.getBucketName();
+ }
+
+ @Override
+ protected String getBucketPassword() {
+ return couchbaseProperties.getBucketPassword();
+ }
+
+ @Override
+ public CouchbaseEnvironment couchbaseEnvironment() {
+ return DefaultCouchbaseEnvironment
+ .builder()
+ .bootstrapHttpDirectPort(couchbaseProperties.getPort())
+ .build();
+ }
+
+ @Bean(name = BeanNames.COUCHBASE_INDEX_MANAGER)
+ public IndexManager couchbaseIndexManager() {
+ return new IndexManager(true, true, false);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java
new file mode 100644
index 0000000000..9b4d9b0319
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/configuration/ViewReactiveCouchbaseConfiguration.java
@@ -0,0 +1,13 @@
+package com.baeldung.couchbase.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories;
+
+@Configuration
+@EnableReactiveCouchbaseRepositories("com.baeldung.couchbase.domain.repository.view")
+public class ViewReactiveCouchbaseConfiguration extends ReactiveCouchbaseConfiguration {
+
+ public ViewReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) {
+ super(couchbaseProperties);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java
new file mode 100644
index 0000000000..285de34df8
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/Person.java
@@ -0,0 +1,43 @@
+package com.baeldung.couchbase.domain;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.couchbase.core.mapping.Document;
+
+import java.util.Objects;
+import java.util.UUID;
+
+@Document
+public class Person {
+
+ @Id private UUID id;
+ private String firstName;
+
+ public Person(final UUID id, final String firstName) {
+ this.id = id;
+ this.firstName = firstName;
+ }
+
+ private Person() {
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Person person = (Person) o;
+ return Objects.equals(id, person.id) && Objects.equals(firstName, person.firstName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, firstName);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java
new file mode 100644
index 0000000000..6f73a77ceb
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepository.java
@@ -0,0 +1,16 @@
+package com.baeldung.couchbase.domain.repository.n1ql;
+
+import com.baeldung.couchbase.domain.Person;
+import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+import java.util.UUID;
+
+@Repository
+@N1qlPrimaryIndexed
+public interface N1QLPersonRepository extends ReactiveCrudRepository {
+
+ Flux findAllByFirstName(final String firstName);
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java
new file mode 100644
index 0000000000..57dd149425
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepository.java
@@ -0,0 +1,13 @@
+package com.baeldung.couchbase.domain.repository.n1ql;
+
+import com.baeldung.couchbase.domain.Person;
+import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
+import org.springframework.data.repository.reactive.ReactiveSortingRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.UUID;
+
+@Repository
+@N1qlPrimaryIndexed
+public interface N1QLSortingPersonRepository extends ReactiveSortingRepository {
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java
new file mode 100644
index 0000000000..06c47c2393
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepository.java
@@ -0,0 +1,20 @@
+package com.baeldung.couchbase.domain.repository.view;
+
+import com.baeldung.couchbase.domain.Person;
+import org.springframework.data.couchbase.core.query.View;
+import org.springframework.data.couchbase.core.query.ViewIndexed;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+
+import java.util.UUID;
+
+@Repository
+@ViewIndexed(designDoc = ViewPersonRepository.DESIGN_DOCUMENT)
+public interface ViewPersonRepository extends ReactiveCrudRepository {
+
+ String DESIGN_DOCUMENT = "person";
+
+ @View(designDocument = ViewPersonRepository.DESIGN_DOCUMENT)
+ Flux findByFirstName(String firstName);
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java
new file mode 100644
index 0000000000..557b6ff42a
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/R2dbcApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.r2dbc;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan(basePackages = "com.baeldung.r2dbc")
+public class R2dbcApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(R2dbcApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java
new file mode 100644
index 0000000000..54f06d9c6c
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/configuration/R2DBCConfiguration.java
@@ -0,0 +1,21 @@
+package com.baeldung.r2dbc.configuration;
+
+import io.r2dbc.h2.H2ConnectionConfiguration;
+import io.r2dbc.h2.H2ConnectionFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
+import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
+
+@Configuration
+@EnableR2dbcRepositories(basePackages = "com.baeldung.r2dbc.repository")
+public class R2DBCConfiguration extends AbstractR2dbcConfiguration {
+ @Bean
+ public H2ConnectionFactory connectionFactory() {
+ return new H2ConnectionFactory(
+ H2ConnectionConfiguration.builder()
+ .url("mem:testdb;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4")
+ .username("sa")
+ .build());
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java
new file mode 100644
index 0000000000..1e28cb3d07
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/model/Player.java
@@ -0,0 +1,18 @@
+package com.baeldung.r2dbc.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.relational.core.mapping.Table;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Table
+public class Player {
+ @Id
+ Integer id;
+ String name;
+ Integer age;
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java
new file mode 100644
index 0000000000..20f7642a7c
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/r2dbc/repository/PlayerRepository.java
@@ -0,0 +1,17 @@
+package com.baeldung.r2dbc.repository;
+
+import org.springframework.data.r2dbc.repository.Query;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+import com.baeldung.r2dbc.model.Player;
+
+import reactor.core.publisher.Flux;
+
+public interface PlayerRepository extends ReactiveCrudRepository {
+
+ @Query("select id, name, age from player where name = $1")
+ Flux findAllByName(String name);
+
+ @Query("select * from player where age = $1")
+ Flux findByAge(int age);
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
new file mode 100644
index 0000000000..e96767145e
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
@@ -0,0 +1,25 @@
+package com.baeldung.reactive;
+
+import com.mongodb.reactivestreams.client.MongoClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+
+@SpringBootApplication
+public class Spring5ReactiveApplication{
+
+ public static void main(String[] args) {
+ SpringApplication.run(Spring5ReactiveApplication.class, args);
+ }
+
+ @Autowired
+ MongoClient mongoClient;
+
+ @Bean
+ public ReactiveMongoTemplate reactiveMongoTemplate() {
+ return new ReactiveMongoTemplate(mongoClient, "test");
+ }
+
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java
new file mode 100644
index 0000000000..57abd80009
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/model/Account.java
@@ -0,0 +1,21 @@
+package com.baeldung.reactive.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document
+@Data
+@ToString
+@AllArgsConstructor
+@NoArgsConstructor
+public class Account {
+
+ @Id
+ private String id;
+ private String owner;
+ private Double value;
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java
new file mode 100644
index 0000000000..8798c13772
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.reactive.repository;
+
+import com.baeldung.reactive.model.Account;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Repository
+public interface AccountCrudRepository extends ReactiveCrudRepository {
+
+ public Flux findAllByValue(Double value);
+
+ public Mono findFirstByOwner(Mono owner);
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java
new file mode 100644
index 0000000000..5c09e4a264
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java
@@ -0,0 +1,7 @@
+package com.baeldung.reactive.repository;
+
+import com.baeldung.reactive.model.Account;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+
+public interface AccountMongoRepository extends ReactiveMongoRepository {
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java
new file mode 100644
index 0000000000..6afe92a21b
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.reactive.repository;
+
+import com.baeldung.reactive.model.Account;
+import io.reactivex.Observable;
+import io.reactivex.Single;
+import org.springframework.data.repository.reactive.RxJava2CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AccountRxJavaRepository extends RxJava2CrudRepository{
+
+ public Observable findAllByValue(Double value);
+
+ public Single findFirstByOwner(Single owner);
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java
new file mode 100644
index 0000000000..9d32f34e3b
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java
@@ -0,0 +1,33 @@
+package com.baeldung.reactive.template;
+
+import com.baeldung.reactive.model.Account;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.data.mongodb.core.ReactiveRemoveOperation;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Service
+public class AccountTemplateOperations {
+
+ @Autowired
+ ReactiveMongoTemplate template;
+
+ public Mono findById(String id) {
+ return template.findById(id, Account.class);
+ }
+
+ public Flux findAll() {
+ return template.findAll(Account.class);
+ }
+
+ public Mono save(Mono account) {
+ return template.save(account);
+ }
+
+ public ReactiveRemoveOperation.ReactiveRemove deleteAll() {
+ return template.remove(Account.class);
+ }
+
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java
new file mode 100644
index 0000000000..8b2511a8f3
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.tailablecursor;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class LogsCounterApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(LogsCounterApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java
new file mode 100644
index 0000000000..717a367751
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java
@@ -0,0 +1,21 @@
+package com.baeldung.tailablecursor.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Data
+@Document
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Log {
+ @Id
+ private String id;
+ private String service;
+ private LogLevel level;
+ private String message;
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java
new file mode 100644
index 0000000000..6826fbffd3
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java
@@ -0,0 +1,5 @@
+package com.baeldung.tailablecursor.domain;
+
+public enum LogLevel {
+ ERROR, WARN, INFO
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java
new file mode 100644
index 0000000000..dce11c548c
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java
@@ -0,0 +1,12 @@
+package com.baeldung.tailablecursor.repository;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import org.springframework.data.mongodb.repository.Tailable;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+import reactor.core.publisher.Flux;
+
+public interface LogsRepository extends ReactiveCrudRepository {
+ @Tailable
+ Flux findByLevel(LogLevel level);
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java
new file mode 100644
index 0000000000..c243e64f97
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java
@@ -0,0 +1,62 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainer;
+import org.springframework.data.mongodb.core.messaging.MessageListener;
+import org.springframework.data.mongodb.core.messaging.MessageListenerContainer;
+import org.springframework.data.mongodb.core.messaging.TailableCursorRequest;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
+
+@Slf4j
+public class ErrorLogsCounter implements LogsCounter {
+
+ private static final String LEVEL_FIELD_NAME = "level";
+
+ private final String collectionName;
+ private final MessageListenerContainer container;
+
+ private final AtomicInteger counter = new AtomicInteger();
+
+ public ErrorLogsCounter(MongoTemplate mongoTemplate,
+ String collectionName) {
+ this.collectionName = collectionName;
+ this.container = new DefaultMessageListenerContainer(mongoTemplate);
+
+ container.start();
+ TailableCursorRequest request = getTailableCursorRequest();
+ container.register(request, Log.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private TailableCursorRequest getTailableCursorRequest() {
+ MessageListener listener = message -> {
+ log.info("ERROR log received: {}", message.getBody());
+ counter.incrementAndGet();
+ };
+
+ return TailableCursorRequest.builder()
+ .collection(collectionName)
+ .filter(query(where(LEVEL_FIELD_NAME).is(LogLevel.ERROR)))
+ .publishTo(listener)
+ .build();
+ }
+
+ @Override
+ public int count() {
+ return counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ container.stop();
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java
new file mode 100644
index 0000000000..29301bffec
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java
@@ -0,0 +1,36 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Slf4j
+public class InfoLogsCounter implements LogsCounter {
+
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Disposable subscription;
+
+ public InfoLogsCounter(LogsRepository repository) {
+ Flux stream = repository.findByLevel(LogLevel.INFO);
+ this.subscription = stream.subscribe(logEntity -> {
+ log.info("INFO log received: " + logEntity);
+ counter.incrementAndGet();
+ });
+ }
+
+ @Override
+ public int count() {
+ return this.counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ this.subscription.dispose();
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java
new file mode 100644
index 0000000000..e14a3eadd7
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java
@@ -0,0 +1,5 @@
+package com.baeldung.tailablecursor.service;
+
+public interface LogsCounter {
+ int count();
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java
new file mode 100644
index 0000000000..2dff8e8e40
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java
@@ -0,0 +1,41 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.mongodb.core.ReactiveMongoOperations;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
+
+@Slf4j
+public class WarnLogsCounter implements LogsCounter {
+
+ private static final String LEVEL_FIELD_NAME = "level";
+
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Disposable subscription;
+
+ public WarnLogsCounter(ReactiveMongoOperations template) {
+ Flux stream = template.tail(query(where(LEVEL_FIELD_NAME).is(LogLevel.WARN)), Log.class);
+ subscription = stream.subscribe(logEntity -> {
+ log.warn("WARN log received: " + logEntity);
+ counter.incrementAndGet();
+ });
+ }
+
+ @Override
+ public int count() {
+ return counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ subscription.dispose();
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties
new file mode 100644
index 0000000000..53fad807fe
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/couchbase.properties
@@ -0,0 +1,4 @@
+spring.couchbase.bucket.name=default
+spring.couchbase.bootstrap-hosts=localhost
+spring.couchbase.port=8091
+spring.couchbase.bucket.password=123456
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/main/resources/logback.xml b/persistence-modules/spring-data-mongodb-reactive/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/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-boot-rest/src/test/java/com/baeldung/SpringContextTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java
similarity index 60%
rename from spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java
rename to persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java
index 9cba7f8fc1..bedb30fcaa 100644
--- a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/SpringContextTest.java
@@ -5,12 +5,13 @@ import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
+import com.baeldung.reactive.Spring5ReactiveApplication;
+
@RunWith(SpringRunner.class)
-@SpringBootTest(classes = {SpringBootRestApplication.class})
+@SpringBootTest(classes = Spring5ReactiveApplication.class)
public class SpringContextTest {
@Test
- public void contextLoads() {
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
-
}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java
new file mode 100644
index 0000000000..2a09fce4b0
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/CouchbaseMockConfiguration.java
@@ -0,0 +1,54 @@
+package com.baeldung.couchbase.domain.repository;
+
+import com.baeldung.couchbase.configuration.CouchbaseProperties;
+import com.couchbase.mock.Bucket;
+import com.couchbase.mock.BucketConfiguration;
+import com.couchbase.mock.CouchbaseMock;
+import org.springframework.boot.test.context.TestConfiguration;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Collections;
+
+@TestConfiguration
+public class CouchbaseMockConfiguration {
+
+ private CouchbaseMock couchbaseMock;
+
+ public CouchbaseMockConfiguration(final CouchbaseProperties couchbaseProperties) {
+ final BucketConfiguration bucketConfiguration = new BucketConfiguration();
+ bucketConfiguration.numNodes = 1;
+ bucketConfiguration.numReplicas = 1;
+ bucketConfiguration.numVBuckets = 1024;
+ bucketConfiguration.name = couchbaseProperties.getBucketName();
+ bucketConfiguration.type = Bucket.BucketType.COUCHBASE;
+ bucketConfiguration.password = couchbaseProperties.getBucketPassword();
+
+ try {
+ couchbaseMock = new CouchbaseMock(couchbaseProperties.getPort(), Collections.singletonList(bucketConfiguration));
+ } catch (final IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+ try {
+ couchbaseMock.start();
+ } catch (final IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ try {
+ couchbaseMock.waitForStartup();
+ } catch (final InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @PreDestroy
+ public void preDestroy() {
+ couchbaseMock.stop();
+ }
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java
new file mode 100644
index 0000000000..c8dbbf429e
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLPersonRepositoryLiveTest.java
@@ -0,0 +1,55 @@
+package com.baeldung.couchbase.domain.repository.n1ql;
+
+import com.baeldung.couchbase.domain.Person;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.test.StepVerifier;
+
+import java.util.UUID;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(properties = {"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration"})
+public class N1QLPersonRepositoryLiveTest {
+
+ @Autowired private N1QLPersonRepository personRepository;
+
+ @Test
+ public void shouldFindAll_byLastName() {
+ //Given
+ final String firstName = "John";
+ final Person matchingPerson = new Person(UUID.randomUUID(), firstName);
+ final Person nonMatchingPerson = new Person(UUID.randomUUID(), "NotJohn");
+ wrap(() -> {
+ personRepository
+ .save(matchingPerson)
+ .subscribe();
+ personRepository
+ .save(nonMatchingPerson)
+ .subscribe();
+ //When
+ final Flux allByFirstName = personRepository.findAllByFirstName(firstName);
+ //Then
+ StepVerifier
+ .create(allByFirstName)
+ .expectNext(matchingPerson)
+ .verifyComplete();
+
+ }, matchingPerson, nonMatchingPerson);
+ }
+
+ private void wrap(final Runnable runnable, final Person... people) {
+ try {
+ runnable.run();
+ } finally {
+ for (final Person person : people) {
+ personRepository
+ .delete(person)
+ .subscribe();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java
new file mode 100644
index 0000000000..8c6ce137f1
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/n1ql/N1QLSortingPersonRepositoryLiveTest.java
@@ -0,0 +1,60 @@
+package com.baeldung.couchbase.domain.repository.n1ql;
+
+import com.baeldung.couchbase.domain.Person;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Sort;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.test.StepVerifier;
+
+import java.util.UUID;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(properties = {"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration"})
+public class N1QLSortingPersonRepositoryLiveTest {
+
+ @Autowired private N1QLSortingPersonRepository personRepository;
+
+ @Test
+ public void shouldFindAll_sortedByFirstName() {
+ //Given
+ final Person firstPerson = new Person(UUID.randomUUID(), "John");
+ final Person secondPerson = new Person(UUID.randomUUID(), "Mikki");
+ wrap(() -> {
+ personRepository
+ .save(firstPerson)
+ .subscribe();
+ personRepository
+ .save(secondPerson)
+ .subscribe();
+ //When
+ final Flux allByFirstName = personRepository.findAll(Sort.by(Sort.Direction.DESC, "firstName"));
+ //Then
+ StepVerifier
+ .create(allByFirstName)
+ .expectNextMatches(person -> person
+ .getFirstName()
+ .equals(secondPerson.getFirstName()))
+ .expectNextMatches(person -> person
+ .getFirstName()
+ .equals(firstPerson.getFirstName()))
+ .verifyComplete();
+ }, firstPerson, secondPerson);
+ }
+
+ //workaround for deleteAll()
+ private void wrap(final Runnable runnable, final Person... people) {
+ try {
+ runnable.run();
+ } finally {
+ for (final Person person : people) {
+ personRepository
+ .delete(person)
+ .subscribe();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..15688e1b80
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/couchbase/domain/repository/view/ViewPersonRepositoryIntegrationTest.java
@@ -0,0 +1,81 @@
+package com.baeldung.couchbase.domain.repository.view;
+
+import com.baeldung.couchbase.configuration.CouchbaseProperties;
+import com.baeldung.couchbase.configuration.ViewReactiveCouchbaseConfiguration;
+import com.baeldung.couchbase.domain.Person;
+import com.baeldung.couchbase.domain.repository.CouchbaseMockConfiguration;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.UUID;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(properties = { "spring.couchbase.port=10010", "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration" },
+ classes = { CouchbaseMockConfiguration.class, ViewReactiveCouchbaseConfiguration.class, CouchbaseProperties.class })
+public class ViewPersonRepositoryIntegrationTest {
+
+ @Autowired private ViewPersonRepository personRepository;
+
+ @Test
+ public void shouldSavePerson_findById_thenDeleteIt() {
+ //Given
+ final UUID id = UUID.randomUUID();
+ final Person person = new Person(id, "John");
+ wrap(() -> {
+ personRepository
+ .save(person)
+ .subscribe();
+ //When
+ final Mono byId = personRepository.findById(id);
+ //Then
+ StepVerifier
+ .create(byId)
+ .expectNextMatches(result -> result
+ .getId()
+ .equals(id))
+ .expectComplete()
+ .verify();
+ }, person);
+ }
+
+ @Test
+ public void shouldFindAll_thenDeleteIt() {
+ //Given
+ final Person person = new Person(UUID.randomUUID(), "John");
+ final Person secondPerson = new Person(UUID.randomUUID(), "Mikki");
+ wrap(() -> {
+ personRepository
+ .save(person)
+ .subscribe();
+ personRepository
+ .save(secondPerson)
+ .subscribe();
+ //When
+ final Flux all = personRepository.findAll();
+ //Then
+ StepVerifier
+ .create(all)
+ .expectNextCount(2)
+ .verifyComplete();
+ }, person, secondPerson);
+ }
+
+ private void wrap(final Runnable runnable, final Person... people) {
+ try {
+ runnable.run();
+ } finally {
+ for (final Person person : people) {
+ personRepository
+ .delete(person)
+ .subscribe();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java
new file mode 100644
index 0000000000..1af570587e
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/r2dbc/R2dbcApplicationIntegrationTest.java
@@ -0,0 +1,124 @@
+package com.baeldung.r2dbc;
+
+
+import com.baeldung.r2dbc.model.Player;
+import com.baeldung.r2dbc.repository.PlayerRepository;
+import io.r2dbc.h2.H2ConnectionFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.r2dbc.core.DatabaseClient;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Hooks;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class R2dbcApplicationIntegrationTest {
+
+
+ @Autowired
+ PlayerRepository playerRepository;
+
+ @Autowired
+ DatabaseClient client;
+
+ @Autowired
+ H2ConnectionFactory factory;
+
+
+ @Before
+ public void setup() {
+
+ Hooks.onOperatorDebug();
+
+ List statements = Arrays.asList(//
+ "DROP TABLE IF EXISTS player;",
+ "CREATE table player (id INT AUTO_INCREMENT NOT NULL, name VARCHAR2, age INT NOT NULL);");
+
+ statements.forEach(it -> client.execute(it) //
+ .fetch() //
+ .rowsUpdated() //
+ .as(StepVerifier::create) //
+ .expectNextCount(1) //
+ .verifyComplete());
+
+ }
+
+ @Test
+ public void whenDeleteAll_then0IsExpected() {
+
+
+ playerRepository.deleteAll()
+ .as(StepVerifier::create)
+ .expectNextCount(0)
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenInsert6_then6AreExpected() {
+
+ insertPlayers();
+
+ playerRepository.findAll()
+ .as(StepVerifier::create)
+ .expectNextCount(6)
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenSearchForCR7_then1IsExpected() {
+
+ insertPlayers();
+
+ playerRepository.findAllByName("CR7")
+ .as(StepVerifier::create)
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenSearchFor32YearsOld_then2AreExpected() {
+ insertPlayers();
+
+ playerRepository.findByAge(32)
+ .as(StepVerifier::create)
+ .expectNextCount(2)
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenBatchHas2Operations_then2AreExpected() {
+ Mono.from(factory.create())
+ .flatMapMany(connection -> Flux.from(connection
+ .createBatch()
+ .add("select * from player")
+ .add("select * from player")
+ .execute()))
+ .as(StepVerifier::create)
+ .expectNextCount(2)
+ .verifyComplete();
+
+ }
+
+ private void insertPlayers() {
+ List players = Arrays.asList(
+ new Player(null, "Kaka", 37),
+ new Player(null, "Messi", 32),
+ new Player(null, "Mbappé", 20),
+ new Player(null, "CR7", 34),
+ new Player(null, "Lewandowski", 30),
+ new Player(null, "Cavani", 32)
+ );
+
+ playerRepository.saveAll(players).subscribe();
+ }
+}
+
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java
new file mode 100644
index 0000000000..d4b1d0eeda
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryManualTest.java
@@ -0,0 +1,70 @@
+package com.baeldung.reactive.repository;
+
+
+import com.baeldung.reactive.Spring5ReactiveApplication;
+import com.baeldung.reactive.model.Account;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class)
+public class AccountCrudRepositoryManualTest {
+
+ @Autowired
+ AccountCrudRepository repository;
+
+ @Test
+ public void givenValue_whenFindAllByValue_thenFindAccount() {
+ repository.save(new Account(null, "Bill", 12.3)).block();
+ Flux accountFlux = repository.findAllByValue(12.3);
+
+ StepVerifier.create(accountFlux)
+ .assertNext(account -> {
+ assertEquals("Bill", account.getOwner());
+ assertEquals(Double.valueOf(12.3) , account.getValue());
+ assertNotNull(account.getId());
+ })
+ .expectComplete()
+ .verify();
+ }
+
+ @Test
+ public void givenOwner_whenFindFirstByOwner_thenFindAccount() {
+ repository.save(new Account(null, "Bill", 12.3)).block();
+ Mono accountMono = repository.findFirstByOwner(Mono.just("Bill"));
+
+ StepVerifier.create(accountMono)
+ .assertNext(account -> {
+ assertEquals("Bill", account.getOwner());
+ assertEquals(Double.valueOf(12.3) , account.getValue());
+ assertNotNull(account.getId());
+ })
+ .expectComplete()
+ .verify();
+
+
+
+ }
+
+ @Test
+ public void givenAccount_whenSave_thenSaveAccount() {
+ Mono accountMono = repository.save(new Account(null, "Bill", 12.3));
+
+ StepVerifier
+ .create(accountMono)
+ .assertNext(account -> assertNotNull(account.getId()))
+ .expectComplete()
+ .verify();
+ }
+
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java
new file mode 100644
index 0000000000..2ca075aa5e
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryManualTest.java
@@ -0,0 +1,67 @@
+package com.baeldung.reactive.repository;
+
+import com.baeldung.reactive.Spring5ReactiveApplication;
+import com.baeldung.reactive.model.Account;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class)
+public class AccountMongoRepositoryManualTest {
+
+ @Autowired
+ AccountMongoRepository repository;
+
+ @Test
+ public void givenExample_whenFindAllWithExample_thenFindAllMacthings() {
+ repository.save(new Account(null, "john", 12.3)).block();
+ ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("owner", startsWith());
+ Example example = Example.of(new Account(null, "jo", null), matcher);
+ Flux accountFlux = repository.findAll(example);
+
+ StepVerifier
+ .create(accountFlux)
+ .assertNext(account -> assertEquals("john", account.getOwner()))
+ .expectComplete()
+ .verify();
+ }
+
+ @Test
+ public void givenAccount_whenSave_thenSave() {
+ Mono accountMono = repository.save(new Account(null, "john", 12.3));
+
+ StepVerifier
+ .create(accountMono)
+ .assertNext(account -> assertNotNull(account.getId()))
+ .expectComplete()
+ .verify();
+ }
+
+ @Test
+ public void givenId_whenFindById_thenFindAccount() {
+ Account inserted = repository.save(new Account(null, "john", 12.3)).block();
+ Mono accountMono = repository.findById(inserted.getId());
+
+ StepVerifier
+ .create(accountMono)
+ .assertNext(account -> {
+ assertEquals("john", account.getOwner());
+ assertEquals(Double.valueOf(12.3), account.getValue());
+ assertNotNull(account.getId());
+ })
+ .expectComplete()
+ .verify();
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java
new file mode 100644
index 0000000000..d91acd24e2
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryManualTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.reactive.repository;
+
+import com.baeldung.reactive.Spring5ReactiveApplication;
+import com.baeldung.reactive.model.Account;
+import io.reactivex.Observable;
+import io.reactivex.Single;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class)
+public class AccountRxJavaRepositoryManualTest {
+
+ @Autowired
+ AccountRxJavaRepository repository;
+
+ @Test
+ public void givenValue_whenFindAllByValue_thenFindAccounts() throws InterruptedException {
+ repository.save(new Account(null, "bruno", 12.3)).blockingGet();
+ Observable accountObservable = repository.findAllByValue(12.3);
+
+ accountObservable
+ .test()
+ .await()
+ .assertComplete()
+ .assertValueAt(0, account -> {
+ assertEquals("bruno", account.getOwner());
+ assertEquals(Double.valueOf(12.3), account.getValue());
+ return true;
+ });
+
+ }
+
+ @Test
+ public void givenOwner_whenFindFirstByOwner_thenFindAccount() throws InterruptedException {
+ repository.save(new Account(null, "bruno", 12.3)).blockingGet();
+ Single accountSingle = repository.findFirstByOwner(Single.just("bruno"));
+
+ accountSingle
+ .test()
+ .await()
+ .assertComplete()
+ .assertValueAt(0, account -> {
+ assertEquals("bruno", account.getOwner());
+ assertEquals(Double.valueOf(12.3), account.getValue());
+ assertNotNull(account.getId());
+ return true;
+ });
+
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java
new file mode 100644
index 0000000000..5fa0e39317
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsManualTest.java
@@ -0,0 +1,48 @@
+package com.baeldung.reactive.template;
+
+import com.baeldung.reactive.Spring5ReactiveApplication;
+import com.baeldung.reactive.model.Account;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class)
+public class AccountTemplateOperationsManualTest {
+
+ @Autowired
+ AccountTemplateOperations accountTemplate;
+
+ @Test
+ public void givenAccount_whenSave_thenSave() {
+ Account account = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3))).block();
+ assertNotNull( account.getId() );
+ }
+
+ @Test
+ public void givenId_whenFindById_thenFindAccount() {
+ Mono accountMono = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3)));
+ Mono accountMonoResult = accountTemplate.findById(accountMono.block().getId());
+ assertNotNull(accountMonoResult.block().getId());
+ assertEquals(accountMonoResult.block().getOwner(), "Raul");
+ }
+
+ @Test
+ public void whenFindAll_thenFindAllAccounts() {
+ Account account1 = accountTemplate.save(Mono.just(new Account(null, "Raul", 12.3))).block();
+ Account account2 = accountTemplate.save(Mono.just(new Account(null, "Raul Torres", 13.3))).block();
+ Flux accountFlux = accountTemplate.findAll();
+ List accounts = accountFlux.collectList().block();
+ assertTrue(accounts.stream().anyMatch(x -> account1.getId().equals(x.getId()) ));
+ assertTrue(accounts.stream().anyMatch(x -> account2.getId().equals(x.getId()) ));
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java
new file mode 100644
index 0000000000..5e20d3ec79
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java
@@ -0,0 +1,112 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.mongodb.MongoClient;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.CreateCollectionOptions;
+import de.flapdoodle.embed.mongo.MongodExecutable;
+import de.flapdoodle.embed.mongo.MongodProcess;
+import de.flapdoodle.embed.mongo.MongodStarter;
+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
+import de.flapdoodle.embed.mongo.config.Net;
+import de.flapdoodle.embed.mongo.distribution.Version;
+import de.flapdoodle.embed.process.runtime.Network;
+import org.bson.Document;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.util.SocketUtils;
+
+import java.io.IOException;
+import java.util.stream.IntStream;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ErrorLogsCounterManualTest {
+
+ private static final String SERVER = "localhost";
+ private static final int PORT = SocketUtils.findAvailableTcpPort(10000);
+ private static final String DB_NAME = "test";
+ private static final String COLLECTION_NAME = Log.class.getName().toLowerCase();
+
+ private static final MongodStarter starter = MongodStarter.getDefaultInstance();
+ private static final int MAX_DOCUMENTS_IN_COLLECTION = 3;
+
+ private ErrorLogsCounter errorLogsCounter;
+ private MongodExecutable mongodExecutable;
+ private MongodProcess mongoDaemon;
+ private MongoDatabase db;
+
+ @Before
+ public void setup() throws Exception {
+ MongoTemplate mongoTemplate = initMongoTemplate();
+
+ MongoCollection collection = createCappedCollection();
+
+ persistDocument(collection, -1, LogLevel.ERROR, "my-service", "Initial log");
+
+ errorLogsCounter = new ErrorLogsCounter(mongoTemplate, COLLECTION_NAME);
+ Thread.sleep(1000L); // wait for initialization
+ }
+
+ private MongoTemplate initMongoTemplate() throws IOException {
+ mongodExecutable = starter.prepare(new MongodConfigBuilder()
+ .version(Version.Main.PRODUCTION)
+ .net(new Net(SERVER, PORT, Network.localhostIsIPv6()))
+ .build());
+ mongoDaemon = mongodExecutable.start();
+
+ MongoClient mongoClient = new MongoClient(SERVER, PORT);
+ db = mongoClient.getDatabase(DB_NAME);
+
+ return new MongoTemplate(mongoClient, DB_NAME);
+ }
+
+ private MongoCollection createCappedCollection() {
+ db.createCollection(COLLECTION_NAME, new CreateCollectionOptions()
+ .capped(true)
+ .sizeInBytes(100000)
+ .maxDocuments(MAX_DOCUMENTS_IN_COLLECTION));
+ return db.getCollection(COLLECTION_NAME);
+ }
+
+ private void persistDocument(MongoCollection collection,
+ int i, LogLevel level, String service, String message) {
+ Document logMessage = new Document();
+ logMessage.append("_id", i);
+ logMessage.append("level", level.toString());
+ logMessage.append("service", service);
+ logMessage.append("message", message);
+ collection.insertOne(logMessage);
+ }
+
+ @After
+ public void tearDown() {
+ errorLogsCounter.close();
+ mongoDaemon.stop();
+ mongodExecutable.stop();
+ }
+
+ @Test
+ public void whenErrorLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ MongoCollection collection = db.getCollection(COLLECTION_NAME);
+
+ IntStream.range(1, 10)
+ .forEach(i -> persistDocument(collection,
+ i,
+ i > 5 ? LogLevel.ERROR : LogLevel.INFO,
+ "service" + i,
+ "Message from service " + i)
+ );
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(collection.countDocuments(), is((long) MAX_DOCUMENTS_IN_COLLECTION));
+ assertThat(errorLogsCounter.count(), is(5));
+ }
+
+}
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java
new file mode 100644
index 0000000000..cd8bd68257
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.LogsCounterApplication;
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.CollectionOptions;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LogsCounterApplication.class)
+@Slf4j
+public class InfoLogsCounterManualTest {
+ @Autowired
+ private LogsRepository repository;
+
+ @Autowired
+ private ReactiveMongoTemplate template;
+
+ @Before
+ public void setUp() {
+ createCappedCollectionUsingReactiveMongoTemplate(template);
+
+ persistDocument(Log.builder()
+ .level(LogLevel.INFO)
+ .service("Service 2")
+ .message("Initial INFO message")
+ .build());
+ }
+
+ private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) {
+ reactiveMongoTemplate.dropCollection(Log.class).block();
+ reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty()
+ .maxDocuments(5)
+ .size(1024 * 1024L)
+ .capped()).block();
+ }
+
+ private void persistDocument(Log log) {
+ repository.save(log).block();
+ }
+
+ @Test
+ public void wheInfoLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ InfoLogsCounter infoLogsCounter = new InfoLogsCounter(repository);
+
+ Thread.sleep(1000L); // wait for initialization
+
+ Flux.range(0,10)
+ .map(i -> Log.builder()
+ .level(i > 5 ? LogLevel.WARN : LogLevel.INFO)
+ .service("some-service")
+ .message("some log message")
+ .build())
+ .map(entity -> repository.save(entity).subscribe())
+ .blockLast();
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(infoLogsCounter.count(), is(7));
+ infoLogsCounter.close();
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java
new file mode 100644
index 0000000000..79d94b6784
--- /dev/null
+++ b/persistence-modules/spring-data-mongodb-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.LogsCounterApplication;
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.CollectionOptions;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LogsCounterApplication.class)
+@Slf4j
+public class WarnLogsCounterManualTest {
+ @Autowired
+ private LogsRepository repository;
+
+ @Autowired
+ private ReactiveMongoTemplate template;
+
+ @Before
+ public void setUp() {
+ createCappedCollectionUsingReactiveMongoTemplate(template);
+
+ persistDocument(Log.builder()
+ .level(LogLevel.WARN)
+ .service("Service 1")
+ .message("Initial Warn message")
+ .build());
+ }
+
+ private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) {
+ reactiveMongoTemplate.dropCollection(Log.class).block();
+ reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty()
+ .maxDocuments(5)
+ .size(1024 * 1024L)
+ .capped()).block();
+ }
+
+ private void persistDocument(Log log) {
+ repository.save(log).block();
+ }
+
+ @Test
+ public void whenWarnLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ WarnLogsCounter warnLogsCounter = new WarnLogsCounter(template);
+
+ Thread.sleep(1000L); // wait for initialization
+
+ Flux.range(0,10)
+ .map(i -> Log.builder()
+ .level(i > 5 ? LogLevel.WARN : LogLevel.INFO)
+ .service("some-service")
+ .message("some log message")
+ .build())
+ .map(entity -> repository.save(entity).subscribe())
+ .blockLast();
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(warnLogsCounter.count(), is(5));
+ warnLogsCounter.close();
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java
similarity index 98%
rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java
rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java
index af790c99b7..2aabe099ea 100644
--- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsIntegrationTest.java
+++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisKeyCommandsManualTest.java
@@ -26,7 +26,7 @@ import java.nio.ByteBuffer;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class)
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-public class RedisKeyCommandsIntegrationTest {
+public class RedisKeyCommandsManualTest {
private static redis.embedded.RedisServer redisServer;
diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java
similarity index 97%
rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java
rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java
index cd5994c854..7353b9630c 100644
--- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsIntegrationTest.java
+++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateListOpsManualTest.java
@@ -23,7 +23,7 @@ import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class)
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-public class RedisTemplateListOpsIntegrationTest {
+public class RedisTemplateListOpsManualTest {
private static final String LIST_NAME = "demo_list";
private static redis.embedded.RedisServer redisServer;
diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java
similarity index 98%
rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java
rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java
index 5d12f90f34..af8e41f9e8 100644
--- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsIntegrationTest.java
+++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/reactive/redis/template/RedisTemplateValueOpsManualTest.java
@@ -27,7 +27,7 @@ import redis.embedded.RedisServerBuilder;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisReactiveApplication.class)
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-public class RedisTemplateValueOpsIntegrationTest {
+public class RedisTemplateValueOpsManualTest {
private static redis.embedded.RedisServer redisServer;
diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java
similarity index 96%
rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java
rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java
index 6501f3f9f0..9166377959 100644
--- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerIntegrationTest.java
+++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/RedisMessageListenerManualTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringRedisApplication.class)
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-public class RedisMessageListenerIntegrationTest {
+public class RedisMessageListenerManualTest {
private static redis.embedded.RedisServer redisServer;
diff --git a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java
similarity index 98%
rename from persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java
rename to persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java
index 24191a42b9..a2b62167a1 100644
--- a/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryIntegrationTest.java
+++ b/persistence-modules/spring-data-redis/src/test/java/com/baeldung/spring/data/redis/repo/StudentRepositoryManualTest.java
@@ -25,7 +25,7 @@ import redis.embedded.RedisServerBuilder;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RedisConfig.class)
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
-public class StudentRepositoryIntegrationTest {
+public class StudentRepositoryManualTest {
@Autowired
private StudentRepository studentRepository;
diff --git a/persistence-modules/spring-mybatis/pom.xml b/persistence-modules/spring-mybatis/pom.xml
index 9247d59fe4..1a55e76900 100644
--- a/persistence-modules/spring-mybatis/pom.xml
+++ b/persistence-modules/spring-mybatis/pom.xml
@@ -84,7 +84,7 @@
- 5.1.8.RELEASE
+ 5.3.8
2.0.2
3.5.2
diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java
similarity index 82%
rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java
rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java
index 531356ffa2..d9f757aa30 100644
--- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootIntegrationTest.java
+++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperBootUnitTest.java
@@ -8,6 +8,6 @@ import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = PersistenceAutoConfig.class)
-public class ArticleMapperBootIntegrationTest extends ArticleMapperCommonTest {
+public class ArticleMapperBootUnitTest extends ArticleMapperCommonUnitTest {
}
diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java
similarity index 94%
rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java
rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java
index 33071dc0c1..2a93ca3d61 100644
--- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonTest.java
+++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperCommonUnitTest.java
@@ -5,7 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import static org.assertj.core.api.Assertions.assertThat;
-class ArticleMapperCommonTest {
+class ArticleMapperCommonUnitTest {
@Autowired
ArticleMapper articleMapper;
diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java
similarity index 80%
rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java
rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java
index 9298714cb2..9c677ecee5 100644
--- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperIntegrationTest.java
+++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperUnitTest.java
@@ -6,6 +6,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = PersistenceConfig.class)
-public class ArticleMapperIntegrationTest extends ArticleMapperCommonTest {
+public class ArticleMapperUnitTest extends ArticleMapperCommonUnitTest {
}
diff --git a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java
similarity index 79%
rename from persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java
rename to persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java
index de8974d4ad..96c7c162c5 100644
--- a/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLIntegrationTest.java
+++ b/persistence-modules/spring-mybatis/src/test/java/com/baeldung/mybatis/spring/ArticleMapperXMLUnitTest.java
@@ -6,6 +6,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/beans.xml")
-public class ArticleMapperXMLIntegrationTest extends ArticleMapperCommonTest {
+public class ArticleMapperXMLUnitTest extends ArticleMapperCommonUnitTest {
}
diff --git a/pom.xml b/pom.xml
index 686db65e8f..f56006713a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -402,7 +402,7 @@
dropwizard
dubbo
- ethereum
+
feign
flyway-cdi-extension
@@ -416,7 +416,7 @@
graphql/graphql-java
grpc
- gson
+ gson
guava-modules
guice
@@ -428,7 +428,6 @@
httpclient-simple
hystrix
- image-processing
immutables
jackson-modules
@@ -473,6 +472,7 @@
jsoup
jta
kubernetes
+ ksqldb
language-interop
libraries-2
@@ -616,7 +616,7 @@
spring-apache-camel
spring-batch
- spring-batch-2
+ spring-batch-2
spring-bom
spring-boot-modules
spring-boot-rest
@@ -646,7 +646,7 @@
spring-ejb
spring-exceptions
- spring-freemarker
+ spring-freemarker
spring-integration
@@ -660,6 +660,7 @@
spring-mobile
spring-mockito
+ spring-native
spring-protobuf
spring-quartz
@@ -671,7 +672,7 @@
spring-security-modules
spring-shell
spring-sleuth
- spring-soap
+ spring-soap
spring-spel
spring-state-machine
spring-static-resources
@@ -750,6 +751,8 @@
parent-spring-5
parent-java
+ image-processing
+
jenkins/plugins
jhipster
jws
@@ -868,7 +871,7 @@
dropwizard
dubbo
- ethereum
+
feign
flyway-cdi-extension
@@ -882,7 +885,7 @@
graphql/graphql-java
grpc
- gson
+ gson
guava-modules
guice
@@ -894,7 +897,6 @@
httpclient-simple
hystrix
- image-processing
immutables
jackson-modules
@@ -939,6 +941,8 @@
jsoup
jta
+ ksqldb
+
libraries-2
libraries-3
@@ -1103,7 +1107,7 @@
spring-ejb
spring-exceptions
- spring-freemarker
+ spring-freemarker
spring-integration
@@ -1117,7 +1121,7 @@
spring-mobile
spring-mockito
-
+ spring-native
spring-protobuf
spring-quartz
@@ -1199,6 +1203,8 @@
parent-spring-5
parent-java
+ image-processing
+
jenkins/plugins
jhipster
jws
@@ -1283,16 +1289,20 @@
core-java-modules/core-java-9-streams
core-java-modules/core-java-10
+ core-java-modules/core-java-11
core-java-modules/core-java-11-2
+
core-java-modules/core-java-collections-set
core-java-modules/core-java-date-operations-1
core-java-modules/core-java-datetime-conversion
core-java-modules/core-java-datetime-string
+ core-java-modules/core-java-io-conversions-2
core-java-modules/core-java-jpms
core-java-modules/core-java-os
+ core-java-modules/core-java-string-operations-3
core-java-modules/core-java-time-measurements
core-java-modules/multimodulemavenproject
@@ -1332,12 +1342,15 @@
+
core-java-modules/core-java-collections-set
core-java-modules/core-java-date-operations-1
core-java-modules/core-java-datetime-conversion
core-java-modules/core-java-datetime-string
+ core-java-modules/core-java-io-conversions-2
core-java-modules/core-java-jpms
core-java-modules/core-java-os
+ core-java-modules/core-java-string-operations-3
core-java-modules/core-java-time-measurements
core-java-modules/multimodulemavenproject
core-java-modules/core-java-strings
@@ -1362,7 +1375,7 @@
true
false
false
- false
+ false
true
4.12
diff --git a/quarkus-extension/quarkus-app/pom.xml b/quarkus-extension/quarkus-app/pom.xml
index f609261c5a..041b6f1762 100644
--- a/quarkus-extension/quarkus-app/pom.xml
+++ b/quarkus-extension/quarkus-app/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
com.baeldung.quarkus.app
quarkus-app
diff --git a/quarkus/pom.xml b/quarkus/pom.xml
index 6e250a2858..e88fd4dc5a 100644
--- a/quarkus/pom.xml
+++ b/quarkus/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
com.baeldung.quarkus
quarkus
diff --git a/rabbitmq/README.md b/rabbitmq/README.md
index 218b1a4b14..7fea2e85a0 100644
--- a/rabbitmq/README.md
+++ b/rabbitmq/README.md
@@ -4,4 +4,6 @@ This module contains articles about RabbitMQ.
### Relevant articles
- [Introduction to RabbitMQ](https://www.baeldung.com/rabbitmq)
+- [Exchanges, Queues, and Bindings in RabbitMQ](https://www.baeldung.com/java-rabbitmq-exchanges-queues-bindings)
+- [Pub-Sub vs. Message Queues](https://www.baeldung.com/pub-sub-vs-message-queues)
diff --git a/rabbitmq/pom.xml b/rabbitmq/pom.xml
index 8a707a15dd..8befd36ab6 100644
--- a/rabbitmq/pom.xml
+++ b/rabbitmq/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
rabbitmq
0.1-SNAPSHOT
@@ -9,20 +9,42 @@
com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
com.rabbitmq
amqp-client
${amqp-client.version}
+
+ org.springframework.boot
+ spring-boot
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
- 3.6.6
+ 5.12.0
+ 2020.0.3
\ No newline at end of file
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java
new file mode 100644
index 0000000000..e8acb90f00
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java
@@ -0,0 +1,43 @@
+package com.baeldung.pubsubmq.client;
+
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
+import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+
+@SpringBootApplication
+public class ClientApplication {
+ private static final String MESSAGE_QUEUE = "pizza-message-queue";
+
+ @Bean
+ public Queue queue() {
+ return new Queue(MESSAGE_QUEUE);
+ }
+
+ @Bean
+ public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
+ SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
+ container.setConnectionFactory(connectionFactory);
+ container.setQueueNames(MESSAGE_QUEUE);
+ container.setMessageListener(listenerAdapter);
+ return container;
+ }
+
+ @Bean
+ public Consumer consumer() {
+ return new Consumer();
+ }
+
+ @Bean
+ public MessageListenerAdapter listenerAdapter(Consumer consumer) {
+ return new MessageListenerAdapter(consumer, "receiveOrder");
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(ClientApplication.class, args);
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java
new file mode 100644
index 0000000000..fadc60572d
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java
@@ -0,0 +1,7 @@
+package com.baeldung.pubsubmq.client;
+
+public class Consumer {
+ public void receiveOrder(String message) {
+ System.out.printf("Order received: %s%n", message);
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java
new file mode 100644
index 0000000000..24c4be0a70
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java
@@ -0,0 +1,28 @@
+package com.baeldung.pubsubmq.server;
+
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+
+import javax.annotation.PostConstruct;
+
+public class Publisher {
+
+ private RabbitTemplate rabbitTemplate;
+ private String queue;
+ private String topic;
+
+ public Publisher(RabbitTemplate rabbitTemplate, String queue, String topic) {
+ this.rabbitTemplate = rabbitTemplate;
+ this.queue = queue;
+ this.topic = topic;
+ }
+
+ @PostConstruct
+ public void postMessages() {
+ rabbitTemplate.convertAndSend(queue, "1 Pepperoni");
+ rabbitTemplate.convertAndSend(queue, "3 Margarita");
+ rabbitTemplate.convertAndSend(queue, "1 Ham and Pineapple (yuck)");
+
+ rabbitTemplate.convertAndSend(topic, "notification", "New Deal on T-Shirts: 95% off!");
+ rabbitTemplate.convertAndSend(topic, "notification", "2 for 1 on all Jeans!");
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java
new file mode 100644
index 0000000000..f2387f6ae5
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java
@@ -0,0 +1,57 @@
+package com.baeldung.pubsubmq.server;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.TopicExchange;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class ServerApplication {
+ private static final String MESSAGE_QUEUE = "pizza-message-queue";
+ private static final String PUB_SUB_TOPIC = "notification-topic";
+ private static final String PUB_SUB_EMAIL_QUEUE = "email-queue";
+ private static final String PUB_SUB_TEXT_QUEUE = "text-queue";
+
+ @Bean
+ public Queue queue() {
+ return new Queue(MESSAGE_QUEUE);
+ }
+
+ @Bean
+ public Queue emailQueue() {
+ return new Queue(PUB_SUB_EMAIL_QUEUE);
+ }
+
+ @Bean
+ public Queue textQueue() {
+ return new Queue(PUB_SUB_TEXT_QUEUE);
+ }
+
+ @Bean
+ public TopicExchange exchange() {
+ return new TopicExchange(PUB_SUB_TOPIC);
+ }
+
+ @Bean
+ public Binding emailBinding(Queue emailQueue, TopicExchange exchange) {
+ return BindingBuilder.bind(emailQueue).to(exchange).with("notification");
+ }
+
+ @Bean
+ public Binding textBinding(Queue textQueue, TopicExchange exchange) {
+ return BindingBuilder.bind(textQueue).to(exchange).with("notification");
+ }
+
+ @Bean
+ public Publisher publisher(RabbitTemplate rabbitTemplate) {
+ return new Publisher(rabbitTemplate, MESSAGE_QUEUE, PUB_SUB_TOPIC);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(ServerApplication.class, args);
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/setup/Setup.java b/rabbitmq/src/main/java/com/baeldung/setup/Setup.java
new file mode 100644
index 0000000000..5ec7f3b5ee
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/setup/Setup.java
@@ -0,0 +1,38 @@
+package com.baeldung.setup;
+
+import com.rabbitmq.client.BuiltinExchangeType;
+import com.rabbitmq.client.Channel;
+import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ConnectionFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+public class Setup {
+
+ private static final String ORDERS_QUEUE_NAME = "orders-queue";
+ private static final String ORDERS_EXCHANGE_NAME = "orders-direct-exchange";
+ private static final String ORDERS_ALTERNATE_EXCHANGE_NAME = "orders-alternate-exchange";
+ private static final String ORDERS_ROUTING_KEY = "orders-routing-key";
+
+ public static void main(String[] args) throws IOException, TimeoutException {
+ ConnectionFactory factory = new ConnectionFactory();
+ factory.setHost("localhost");
+ Connection connection = factory.newConnection();
+ Channel channel = connection.createChannel();
+
+ Map exchangeArguments = new HashMap<>();
+ exchangeArguments.put("alternate-exchange", ORDERS_ALTERNATE_EXCHANGE_NAME);
+ channel.exchangeDeclare(ORDERS_EXCHANGE_NAME, BuiltinExchangeType.DIRECT, true, false, exchangeArguments);
+
+ Map queueArguments = new HashMap<>();
+ queueArguments.put("x-message-ttl", 60000);
+ queueArguments.put("x-max-priority", 10);
+ channel.queueDeclare(ORDERS_QUEUE_NAME, true, false, false, queueArguments);
+
+ channel.queueBind(ORDERS_QUEUE_NAME, ORDERS_EXCHANGE_NAME, ORDERS_ROUTING_KEY);
+ }
+
+}
diff --git a/reactor-core/README.md b/reactor-core/README.md
index 08dac354ab..f7c36f5897 100644
--- a/reactor-core/README.md
+++ b/reactor-core/README.md
@@ -9,3 +9,5 @@ This module contains articles about Reactor Core.
- [Programmatically Creating Sequences with Project Reactor](https://www.baeldung.com/flux-sequences-reactor)
- [How to Extract a Mono’s Content in Java](https://www.baeldung.com/java-string-from-mono)
- [How to Convert Mono> Into Flux](https://www.baeldung.com/java-mono-list-to-flux)
+- [Project Reactor: map() vs flatMap()](https://www.baeldung.com/java-reactor-map-flatmap)
+- [What Does Mono.defer() Do?](https://www.baeldung.com/java-mono-defer)
diff --git a/reactor-core/pom.xml b/reactor-core/pom.xml
index 6eead97bc2..6bd725b3b5 100644
--- a/reactor-core/pom.xml
+++ b/reactor-core/pom.xml
@@ -32,6 +32,12 @@
${assertj.version}
test
+
+ org.projectlombok
+ lombok
+ RELEASE
+ test
+
diff --git a/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java b/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java
index 0c6e0c07ef..fc42620e8e 100644
--- a/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java
+++ b/reactor-core/src/test/java/com/baeldung/mono/MonoUnitTest.java
@@ -1,5 +1,6 @@
package com.baeldung.mono;
+import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -13,6 +14,7 @@ import java.util.Optional;
import static org.junit.Assert.assertEquals;
+@Slf4j
public class MonoUnitTest {
@Test
public void whenMonoProducesString_thenBlockAndConsume() {
@@ -80,4 +82,84 @@ public class MonoUnitTest {
return Mono.just(list);
}
+
+ @Test
+ public void whenEmptyList_thenMonoDeferExecuted() {
+
+ Mono> emptyList = Mono.defer(() -> monoOfEmptyList());
+
+ //Empty list, hence Mono publisher in switchIfEmpty executed after condition evaluation
+ Flux emptyListElements = emptyList.flatMapIterable(l -> l)
+ .switchIfEmpty(Mono.defer(() -> sampleMsg("EmptyList")))
+ .log();
+
+ StepVerifier.create(emptyListElements)
+ .expectNext("EmptyList")
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenNonEmptyList_thenMonoDeferNotExecuted() {
+
+ Mono> nonEmptyist = Mono.defer(() -> monoOfList());
+
+ //Non empty list, hence Mono publisher in switchIfEmpty won't evaluated.
+ Flux listElements = nonEmptyist.flatMapIterable(l -> l)
+ .switchIfEmpty(Mono.defer(() -> sampleMsg("NonEmptyList")))
+ .log();
+
+ StepVerifier.create(listElements)
+ .expectNext("one", "two", "three", "four")
+ .verifyComplete();
+ }
+
+ private Mono> monoOfEmptyList() {
+ List list = new ArrayList<>();
+ return Mono.just(list);
+ }
+
+ @Test
+ public void whenUsingMonoJust_thenEagerEvaluation() throws InterruptedException {
+
+ Mono msg = sampleMsg("Eager Publisher");
+
+ log.debug("Intermediate Test Message....");
+
+ StepVerifier.create(msg)
+ .expectNext("Eager Publisher")
+ .verifyComplete();
+
+ Thread.sleep(5000);
+
+ StepVerifier.create(msg)
+ .expectNext("Eager Publisher")
+ .verifyComplete();
+ }
+
+ @Test
+ public void whenUsingMonoDefer_thenLazyEvaluation() throws InterruptedException {
+
+ Mono deferMsg = Mono.defer(() -> sampleMsg("Lazy Publisher"));
+
+ log.debug("Intermediate Test Message....");
+
+ StepVerifier.create(deferMsg)
+ .expectNext("Lazy Publisher")
+ .verifyComplete();
+
+ Thread.sleep(5000);
+
+ StepVerifier.create(deferMsg)
+ .expectNext("Lazy Publisher")
+ .verifyComplete();
+
+ }
+
+ private Mono sampleMsg(String str) {
+ log.debug("Call to Retrieve Sample Message!! --> {} at: {}", str, System.currentTimeMillis());
+ return Mono.just(str);
+ }
}
+
+
+
diff --git a/spark-java/pom.xml b/spark-java/pom.xml
index b5538b4ec3..0ecdd02f6a 100644
--- a/spark-java/pom.xml
+++ b/spark-java/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
spark-java
0.1.0-SNAPSHOT
diff --git a/spf4j/spf4j-aspects-app/pom.xml b/spf4j/spf4j-aspects-app/pom.xml
index 4a4c7cb804..a44ee805fb 100644
--- a/spf4j/spf4j-aspects-app/pom.xml
+++ b/spf4j/spf4j-aspects-app/pom.xml
@@ -20,17 +20,34 @@
org.spf4j
spf4j-aspects
${spf4j.version}
+
+
+ org.apache.avro
+ avro
+
+
org.spf4j
spf4j-ui
${spf4j.version}
+
+
+ org.apache.avro
+ avro
+
+
org.slf4j
slf4j-api
${org.slf4j.version}
+
+ org.apache.avro
+ avro
+ 1.10.2
+
@@ -81,7 +98,7 @@
- 8.6.10
+ 8.9.0
1.7.21
3.8.0
3.1.1
diff --git a/spf4j/spf4j-core-app/pom.xml b/spf4j/spf4j-core-app/pom.xml
index 2961174a35..1f9be97854 100644
--- a/spf4j/spf4j-core-app/pom.xml
+++ b/spf4j/spf4j-core-app/pom.xml
@@ -20,16 +20,39 @@
org.spf4j
spf4j-core
${spf4j.version}
+
+
+ org.apache.avro
+ avro
+
+
org.spf4j
spf4j-ui
${spf4j.version}
+
+
+ org.apache.avro
+ avro
+
+
org.slf4j
slf4j-api
${org.slf4j.version}
+
+
+ org.apache.avro
+ avro
+
+
+
+
+ org.apache.avro
+ avro
+ 1.10.2
@@ -81,7 +104,7 @@
- 8.6.10
+ 8.9.0
1.7.21
3.8.0
3.1.1
diff --git a/spring-4/pom.xml b/spring-4/pom.xml
index 21de925d99..41f55ea13b 100644
--- a/spring-4/pom.xml
+++ b/spring-4/pom.xml
@@ -39,6 +39,7 @@
com.zaxxer
HikariCP
+ ${hikaricp.version}
org.springframework.boot
@@ -113,6 +114,7 @@
1.0.1
3.6
2.4.0
+ 4.0.3
\ No newline at end of file
diff --git a/spring-5-data-reactive/pom.xml b/spring-5-data-reactive/pom.xml
index 5f12636280..94a3c47809 100644
--- a/spring-5-data-reactive/pom.xml
+++ b/spring-5-data-reactive/pom.xml
@@ -83,11 +83,6 @@
h2
${h2.version}
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
org.apache.httpcomponents
httpclient
@@ -152,6 +147,10 @@
1.4.200
1.5.23
3.3.1.RELEASE
+
2.2.6.RELEASE
diff --git a/spring-5/pom.xml b/spring-5/pom.xml
index 5ebeb4dadf..5799f3bc8f 100644
--- a/spring-5/pom.xml
+++ b/spring-5/pom.xml
@@ -96,6 +96,11 @@
spring-restdocs-restassured
test
+
+ com.zaxxer
+ HikariCP
+ ${hikaricp.version}
+
@@ -139,6 +144,7 @@
1.5.6
4.1
${project.build.directory}/generated-snippets
+ 4.0.3
\ No newline at end of file
diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml
index 464a830383..da981987fe 100644
--- a/spring-aop/pom.xml
+++ b/spring-aop/pom.xml
@@ -71,4 +71,5 @@
1.11
+
\ No newline at end of file
diff --git a/spring-batch/pom.xml b/spring-batch/pom.xml
index b195ff8d13..9879a4777f 100644
--- a/spring-batch/pom.xml
+++ b/spring-batch/pom.xml
@@ -17,6 +17,11 @@
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson-datatype.version}
+
@@ -91,6 +96,7 @@
3.15.1
4.1
2.3.1
+ 2.12.3
3.1.1
diff --git a/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java b/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java
index 2d7d3ff63f..62cc74abbd 100644
--- a/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java
+++ b/spring-batch/src/main/java/com/baeldung/taskletsvschunks/model/Line.java
@@ -1,5 +1,10 @@
package com.baeldung.taskletsvschunks.model;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@@ -7,6 +12,8 @@ import java.time.format.DateTimeFormatter;
public class Line implements Serializable {
private String name;
+ @JsonDeserialize(using = LocalDateDeserializer.class)
+ @JsonSerialize(using = LocalDateSerializer.class)
private LocalDate dob;
private Long age;
diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index 53af0de315..7a93cabb82 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -73,6 +73,7 @@
spring-boot-actuator
spring-boot-data-2
spring-boot-react
+ spring-boot-validation
diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml
index 1865974ab0..eaeb3b4ac7 100644
--- a/spring-boot-modules/spring-boot-actuator/pom.xml
+++ b/spring-boot-modules/spring-boot-actuator/pom.xml
@@ -9,10 +9,9 @@
0.0.1-SNAPSHOT
- org.springframework.boot
- spring-boot-starter-parent
- 2.3.2.RELEASE
-
+ com.baeldung.spring-boot-modules
+ spring-boot-modules
+ 1.0.0-SNAPSHOT
diff --git a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml
index 63bc286b45..a1daa3fa19 100644
--- a/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml
+++ b/spring-boot-modules/spring-boot-admin/spring-boot-admin-server/pom.xml
@@ -81,5 +81,5 @@
1.5.7
2.0.4.RELEASE
-
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties b/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties
deleted file mode 100644
index 709574239b..0000000000
--- a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java
index 8a7a3d6e45..be27f099db 100644
--- a/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java
+++ b/spring-boot-modules/spring-boot-basic-customization/src/main/java/com/baeldung/errorhandling/controllers/MyErrorController.java
@@ -11,8 +11,6 @@ import javax.servlet.http.HttpServletRequest;
@Controller
public class MyErrorController implements ErrorController {
- public MyErrorController() {}
-
@GetMapping(value = "/error")
public String handleError(HttpServletRequest request) {
@@ -31,10 +29,4 @@ public class MyErrorController implements ErrorController {
}
return "error";
}
-
- @Override
- public String getErrorPath() {
- return null;
- }
-
}
diff --git a/spring-boot-modules/spring-boot-cassandre/pom.xml b/spring-boot-modules/spring-boot-cassandre/pom.xml
index 75163d2c2b..14849e50d7 100644
--- a/spring-boot-modules/spring-boot-cassandre/pom.xml
+++ b/spring-boot-modules/spring-boot-cassandre/pom.xml
@@ -1,68 +1,50 @@
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.4.5
-
-
- com.example
- demo
- 0.0.1-SNAPSHOT
- Cassandre trading bot tutorial
- Cassandre trading bot tutorial
-
- 11
-
-
-
- org.springframework.boot
- spring-boot-starter
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ spring-boot-cassandre
+ jar
+ spring-boot-cassandre
+ Cassandre trading bot tutorial
-
-
- tech.cassandre.trading.bot
- cassandre-trading-bot-spring-boot-starter
- 4.2.1
-
-
- org.knowm.xchange
- xchange-kucoin
- 5.0.7
-
-
- org.hsqldb
- hsqldb
- 2.5.1
-
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- tech.cassandre.trading.bot
- cassandre-trading-bot-spring-boot-starter-test
- 4.2.1
- test
-
+
+
+
+ tech.cassandre.trading.bot
+ cassandre-trading-bot-spring-boot-starter
+ ${cassandre.trading.bot.version}
+
+
+ org.knowm.xchange
+ xchange-kucoin
+ ${xchange-kucoin.version}
+
+
+ org.hsqldb
+ hsqldb
+ ${hsqldb.version}
+
+
+ tech.cassandre.trading.bot
+ cassandre-trading-bot-spring-boot-starter-test
+ ${cassandre.trading.bot.version}
+ test
+
+
-
+
+ 11
+ 4.2.1
+ 5.0.7
+ 2.5.1
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- 2.4.5
-
-
-
-
-
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
similarity index 98%
rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java
rename to spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
index ea8ae74aa6..92ef379a66 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java
+++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
@@ -1,4 +1,4 @@
-package com.example.demo;
+package com.baeldung.trading;
import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.CLOSED;
import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.OPENED;
diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java
new file mode 100644
index 0000000000..021c6c1d3b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.trading;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TradingBotApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TradingBotApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
similarity index 94%
rename from spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java
rename to spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
index bf7c353821..74986df971 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java
+++ b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
@@ -1,4 +1,4 @@
-package com.example.demo;
+package com.baeldung.trading;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -22,9 +22,9 @@ import tech.cassandre.trading.bot.test.mock.TickerFluxMock;
@SpringBootTest
@Import(TickerFluxMock.class)
@DisplayName("Simple strategy test")
-public class MyFirstStrategyUnitTest {
+public class MyFirstStrategyLiveTest {
- private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyUnitTest.class);
+ private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyLiveTest.class);
@Autowired
private MyFirstStrategy strategy;
diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java
deleted file mode 100644
index eaa99696e2..0000000000
--- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.example.demo;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class DemoApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}
diff --git a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
index 6378a48506..969464a41c 100644
--- a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
@@ -6,7 +6,6 @@ spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
-spring.main.allow-bean-definition-overriding=true
javers.mappingStyle=FIELD
javers.algorithm=SIMPLE
javers.commitIdGenerator=synchronized_sequence
diff --git a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
index 27b7915cff..2bf15543f0 100644
--- a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
@@ -3,5 +3,4 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
index 3d6f37230c..4ffb414e92 100644
--- a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
@@ -3,6 +3,5 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.tomcat.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
spring.config.import=file:./additional.properties,optional:file:/Users/home/config/jdbc.properties
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-exceptions/README.md b/spring-boot-modules/spring-boot-exceptions/README.md
index 33ae193fb8..97a51203c7 100644
--- a/spring-boot-modules/spring-boot-exceptions/README.md
+++ b/spring-boot-modules/spring-boot-exceptions/README.md
@@ -5,3 +5,4 @@ This module contains articles about Spring Boot Exceptions
### Relevant Articles:
- [The BeanDefinitionOverrideException in Spring Boot](https://www.baeldung.com/spring-boot-bean-definition-override-exception)
+- [Spring Boot Error ApplicationContextException](https://www.baeldung.com/spring-boot-application-context-exception)
diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml
index 37d57d6556..505d486509 100644
--- a/spring-boot-modules/spring-boot-keycloak/pom.xml
+++ b/spring-boot-modules/spring-boot-keycloak/pom.xml
@@ -76,7 +76,9 @@
- 11.0.2
+ 13.0.1
+
+ 2.5.3
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-2/pom.xml b/spring-boot-modules/spring-boot-libraries-2/pom.xml
index 669c07c23a..04c09754b4 100644
--- a/spring-boot-modules/spring-boot-libraries-2/pom.xml
+++ b/spring-boot-modules/spring-boot-libraries-2/pom.xml
@@ -91,7 +91,7 @@
- 1.1.0
+ 3.1.1
4.0.3
5.1.0
2.4.5
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java
index 77297feb92..061dba15bf 100644
--- a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/JobRunrSpringBootApp.java
@@ -32,6 +32,6 @@ public class JobRunrSpringBootApp {
@PostConstruct
public void scheduleRecurrently() {
- jobScheduler.scheduleRecurrently(x -> x.executeSampleJob("a recurring job"), Cron.every5minutes());
+ jobScheduler.scheduleRecurrently(Cron.every5minutes(), x -> x.executeSampleJob("a recurring job"));
}
}
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java
index af5f0b1196..610314222c 100644
--- a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/jobrunr/controller/JobRunrController.java
@@ -33,7 +33,7 @@ public class JobRunrController {
public ResponseEntity schedule(
@PathVariable("input") @DefaultValue("default-input") String input,
@RequestParam("scheduleAt") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime scheduleAt) {
- jobScheduler.schedule(() -> sampleJobService.executeSampleJob(input), scheduleAt);
+ jobScheduler.schedule(scheduleAt, () -> sampleJobService.executeSampleJob(input));
return okResponse("job scheduled successfully");
}
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/README.md b/spring-boot-modules/spring-boot-logging-log4j2/README.md
index aa6bb9b6e1..9688f8f83c 100644
--- a/spring-boot-modules/spring-boot-logging-log4j2/README.md
+++ b/spring-boot-modules/spring-boot-logging-log4j2/README.md
@@ -6,3 +6,4 @@ This module contains articles about logging in Spring Boot projects with Log4j 2
- [Logging in Spring Boot](https://www.baeldung.com/spring-boot-logging)
- [Logging to Graylog with Spring Boot](https://www.baeldung.com/graylog-with-spring-boot)
- [Log Groups in Spring Boot 2.1](https://www.baeldung.com/spring-boot-log-groups)
+- [Writing Log Data to Syslog Using Log4j2](https://www.baeldung.com/log4j-to-syslog)
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java
new file mode 100644
index 0000000000..56c3b62c5d
--- /dev/null
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java
@@ -0,0 +1,23 @@
+package com.baeldung.syslog;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringBootSyslogApplication {
+
+ private static final Logger logger = LogManager.getLogger(SpringBootSyslogApplication.class);
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootSyslogApplication.class, args);
+
+ logger.debug("Debug log message");
+ logger.info("Info log message");
+ logger.error("Error log message");
+ logger.warn("Warn log message");
+ logger.fatal("Fatal log message");
+ logger.trace("Trace log message");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
index b08cd2d22d..77a2074b30 100644
--- a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
@@ -22,6 +22,10 @@
+
+
+
@@ -29,6 +33,7 @@
+
diff --git a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
index 7070d4c2f0..3ee7660e8b 100644
--- a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
@@ -1,4 +1,3 @@
-spring.main.allow-bean-definition-overriding=true
spring.mvc.static-path-pattern=/content/**
spring.webflux.static-path-pattern=/content/**
spring.resources.static-locations=classpath:/files/,classpath:/static-files
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml
index e0b261510c..a16c8b0a83 100644
--- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/pom.xml
@@ -8,10 +8,9 @@
0.0.1-SNAPSHOT
- org.springframework.boot
- spring-boot-starter-parent
- 2.4.2
-
+ com.baeldung.spring-boot-modules
+ spring-boot-mvc-jersey
+ 0.0.1-SNAPSHOT
diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java
similarity index 82%
rename from spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java
rename to spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java
index 9bd6bbf029..d9018efc4a 100644
--- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTests.java
+++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-jersey/src/test/java/com/baeldung/boot/jersey/JerseyApplicationIntegrationTest.java
@@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
-class JerseyApplicationIntegrationTests {
+class JerseyApplicationIntegrationTest {
@Test
void contextLoads() {
diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml
index 77c8027974..8521ab25ac 100644
--- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/pom.xml
@@ -8,10 +8,9 @@
0.0.1-SNAPSHOT
- org.springframework.boot
- spring-boot-starter-parent
- 2.4.2
-
+ com.baeldung.spring-boot-modules
+ spring-boot-mvc-jersey
+ 0.0.1-SNAPSHOT
diff --git a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java
similarity index 83%
rename from spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java
rename to spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java
index d05cb97e47..14b0915fa7 100644
--- a/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTests.java
+++ b/spring-boot-modules/spring-boot-mvc-jersey/spring-boot-mvc/src/test/java/com/baeldung/boot/mvc/MvcApplicationIntegrationTest.java
@@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
-class MvcApplicationIntegrationTests {
+class MvcApplicationIntegrationTest {
@Test
void contextLoads() {
diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
index 6dab470c84..7f399bb11d 100644
--- a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
@@ -1,2 +1 @@
-spring.main.allow-bean-definition-overriding=true
spring.thymeleaf.view-names=thymeleaf/*
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
index 184becb657..5633e4d260 100644
--- a/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
+++ b/spring-boot-modules/spring-boot-parent/spring-boot-with-starter-parent/pom.xml
@@ -39,4 +39,5 @@
2.2.5.RELEASE
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-performance/pom.xml b/spring-boot-modules/spring-boot-performance/pom.xml
index 02cef986d0..71dfb3c437 100644
--- a/spring-boot-modules/spring-boot-performance/pom.xml
+++ b/spring-boot-modules/spring-boot-performance/pom.xml
@@ -51,4 +51,5 @@
com.baeldung.lazyinitialization.Application
2.0.0
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties-3/pom.xml b/spring-boot-modules/spring-boot-properties-3/pom.xml
index 400229a662..b5e6ef8701 100644
--- a/spring-boot-modules/spring-boot-properties-3/pom.xml
+++ b/spring-boot-modules/spring-boot-properties-3/pom.xml
@@ -40,4 +40,5 @@
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties/pom.xml b/spring-boot-modules/spring-boot-properties/pom.xml
index adeb41e976..0ef44e0546 100644
--- a/spring-boot-modules/spring-boot-properties/pom.xml
+++ b/spring-boot-modules/spring-boot-properties/pom.xml
@@ -122,16 +122,8 @@
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/milestone
-
-
-
- 2020.0.0-M5
+ 2020.0.3
1.10
20.0
@
diff --git a/spring-boot-modules/spring-boot-react/pom.xml b/spring-boot-modules/spring-boot-react/pom.xml
index f1e10ae12d..fdefbbdfc7 100644
--- a/spring-boot-modules/spring-boot-react/pom.xml
+++ b/spring-boot-modules/spring-boot-react/pom.xml
@@ -15,28 +15,28 @@
org.springframework.boot
spring-boot-starter-web
- 2.4.4
+ ${spring-boot.version}
org.springframework.boot
spring-boot-starter-data-jpa
- 2.4.4
+ ${spring-boot.version}
com.h2database
h2
- 1.4.200
+ ${h2.version}
runtime
com.github.javafaker
javafaker
- 1.0.2
+ ${javafaker.version}
org.springframework.boot
spring-boot-starter-test
- 2.4.4
+ ${spring-boot.version}
test
@@ -127,6 +127,9 @@
1.6
v10.14.2
v1.12.1
+ 2.4.4
+ 1.4.200
+ 1.0.2
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime-2/pom.xml b/spring-boot-modules/spring-boot-runtime-2/pom.xml
index 2c97f63746..c177529a41 100644
--- a/spring-boot-modules/spring-boot-runtime-2/pom.xml
+++ b/spring-boot-modules/spring-boot-runtime-2/pom.xml
@@ -60,4 +60,5 @@
+
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime/README.md b/spring-boot-modules/spring-boot-runtime/README.md
index a36a17fa8e..072bd16a78 100644
--- a/spring-boot-modules/spring-boot-runtime/README.md
+++ b/spring-boot-modules/spring-boot-runtime/README.md
@@ -11,3 +11,4 @@ This module contains articles about administering a Spring Boot runtime
- [CORS with Spring](https://www.baeldung.com/spring-cors)
- [Spring – Log Incoming Requests](https://www.baeldung.com/spring-http-logging)
- [How to Configure Spring Boot Tomcat](https://www.baeldung.com/spring-boot-configure-tomcat)
+ - [Max-HTTP-Header-Size in Spring Boot 2](https://www.baeldung.com/spring-boot-max-http-header-size)
diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java
new file mode 100644
index 0000000000..ab8c52687b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/MaxHttpHeaderSizeApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.maxhttpheadersize;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+
+@EnableAutoConfiguration
+@ComponentScan("com.baeldung.maxhttpheadersize")
+public class MaxHttpHeaderSizeApplication {
+
+ public static void main(final String[] args) {
+ SpringApplication.run(MaxHttpHeaderSizeApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java
similarity index 64%
rename from spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java
rename to spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java
index 0a71f914db..3f500e0074 100644
--- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/config/MaxHTTPHeaderSizeConfig.java
+++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/config/MaxHTTPHeaderSizeConfig.java
@@ -1,11 +1,13 @@
-package com.baeldung.sampleapp.config;
+package com.baeldung.maxhttpheadersize.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
-@ComponentScan({ "com.baeldung.sampleapp.web" })
+@EnableWebMvc
+@ComponentScan({ "com.baeldung.maxhttpheadersize.*" })
public class MaxHTTPHeaderSizeConfig implements WebMvcConfigurer {
public MaxHTTPHeaderSizeConfig() {
diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java
similarity index 90%
rename from spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java
rename to spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java
index 0c55f88fa1..89444c7875 100644
--- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/sampleapp/web/controller/MaxHttpHeaderSizeController.java
+++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeController.java
@@ -1,4 +1,4 @@
-package com.baeldung.sampleapp.web.controller;
+package com.baeldung.maxhttpheadersize.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
diff --git a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
index 27b7915cff..2bf15543f0 100644
--- a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
@@ -3,5 +3,4 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java
similarity index 73%
rename from spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java
rename to spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java
index 9b839f34a0..dd824491a0 100644
--- a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerIntegrationTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.web.controller;
+package com.baeldung.maxhttpheadersize.controller;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -16,10 +16,10 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-import com.baeldung.sampleapp.config.WebConfig;
+import com.baeldung.maxhttpheadersize.config.MaxHTTPHeaderSizeConfig;
@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes = WebConfig.class)
+@ContextConfiguration(classes = MaxHTTPHeaderSizeConfig.class)
@WebAppConfiguration
public class MaxHttpHeaderSizeControllerIntegrationTest {
@@ -30,19 +30,23 @@ public class MaxHttpHeaderSizeControllerIntegrationTest {
@Before
public void setUp() {
- mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
+ mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
+ .build();
}
@Test
public void givenTokenWithLessThan8KBLegth_whenSendGetRequest_thenReturnsOK() throws Exception {
mockMvc.perform(get("/request-header-test").contentType(MediaType.APPLICATION_JSON_VALUE)
- .with(httpBasic("user", "password")).header("token", "token")).andExpect(status().isOk());
+ .with(httpBasic("user", "password"))
+ .header("token", "token"))
+ .andExpect(status().isOk());
}
@Test
- public void givenTokenIsMissingInHeade_whenSendGetRequest_thenThrowsBadRequest() throws Exception {
+ public void givenTokenIsMissingInHeader_whenSendGetRequest_thenThrowsBadRequest() throws Exception {
mockMvc.perform(get("/request-header-test").contentType(MediaType.APPLICATION_JSON_VALUE)
- .with(httpBasic("user", "password"))).andExpect(status().isBadRequest());
+ .with(httpBasic("user", "password")))
+ .andExpect(status().isBadRequest());
}
}
diff --git a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java
similarity index 81%
rename from spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java
rename to spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java
index 9c7e7c9029..2178be288d 100644
--- a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/MaxHttpHeaderSizeControllerLiveTest.java
+++ b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/maxhttpheadersize/controller/MaxHttpHeaderSizeControllerLiveTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.web.controller;
+package com.baeldung.maxhttpheadersize.controller;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -11,18 +11,13 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
-import com.baeldung.sampleapp.config.MaxHTTPHeaderSizeConfig;
-
@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes = { MaxHTTPHeaderSizeConfig.class }, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
-// Start MaxHttpHeaderSizeController Spring Boot App(MainApplication) first
+// Start MaxHttpHeaderSizeController Spring Boot App(MaxHttpHeaderSizeApplication) first
public class MaxHttpHeaderSizeControllerLiveTest {
@Test(expected = HttpClientErrorException.class)
diff --git a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
index cf0f0ab74c..7bf9450088 100644
--- a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
+++ b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
@@ -8,5 +8,4 @@ endpoints.shutdown.enabled=true
management.endpoint.restart.enabled=true
-spring.main.allow-bean-definition-overriding=true
spring.jmx.unique-names=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java
new file mode 100644
index 0000000000..0495e0f716
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.annotations.globalmethod;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+
+@SpringBootApplication
+@EnableWebSecurity
+public class AnnotationSecuredApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AnnotationSecuredApplication.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java
new file mode 100644
index 0000000000..4687299ae5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredController.java
@@ -0,0 +1,50 @@
+package com.baeldung.annotations.globalmethod;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.security.RolesAllowed;
+
+@RestController
+@EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
+public class AnnotationSecuredController {
+
+ @Autowired
+ DifferentClass differentClass;
+
+ @GetMapping("/public")
+ public String publicHello() {
+ return "Hello Public";
+ }
+
+ @RolesAllowed("ADMIN")
+ @GetMapping("/admin")
+ public String adminHello() {
+ return "Hello Admin";
+ }
+
+ @RolesAllowed("USER")
+ @GetMapping("/protected")
+ public String jsr250Hello() {
+ return "Hello Jsr250";
+ }
+
+ @GetMapping("/indirect")
+ public String indirectHello() {
+ return jsr250Hello();
+ }
+
+ @GetMapping("/differentclass")
+ public String differentClassHello() {
+ return differentClass.differentJsr250Hello();
+ }
+
+ @PreAuthorize("hasRole('USER')")
+ public String preAuthorizeHello() {
+ return "Hello PreAuthorize";
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java
new file mode 100644
index 0000000000..467285adfa
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/AnnotationSecuredStaticResourceConfig.java
@@ -0,0 +1,22 @@
+package com.baeldung.annotations.globalmethod;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+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.config.annotation.web.configuration.WebSecurityCustomizer;
+
+
+@Configuration
+@EnableWebSecurity
+public class AnnotationSecuredStaticResourceConfig extends WebSecurityConfigurerAdapter {
+
+ @Bean
+ public WebSecurityCustomizer ignoreResources() {
+ return (webSecurity) -> webSecurity
+ .ignoring()
+ .antMatchers("/hello/*");
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java
new file mode 100644
index 0000000000..9bcc352d9a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/globalmethod/DifferentClass.java
@@ -0,0 +1,13 @@
+package com.baeldung.annotations.globalmethod;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.security.RolesAllowed;
+
+@Component
+public class DifferentClass {
+ @RolesAllowed("USER")
+ public String differentJsr250Hello() {
+ return "Hello Jsr250";
+ }
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java
new file mode 100644
index 0000000000..13e405ee22
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredApplication.java
@@ -0,0 +1,14 @@
+package com.baeldung.annotations.websecurity;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+
+@SpringBootApplication
+@EnableWebSecurity
+public class ConfigSecuredApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ConfigSecuredApplication.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java
new file mode 100644
index 0000000000..198efb8353
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/ConfigSecuredController.java
@@ -0,0 +1,30 @@
+package com.baeldung.annotations.websecurity;
+
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@RestController
+@EnableWebSecurity
+public class ConfigSecuredController {
+
+ @GetMapping("/public")
+ public String publicHello() {
+ return "Hello Public";
+ }
+
+ @GetMapping("/protected")
+ public String protectedHello() {
+ return "Hello from protected";
+ }
+
+ @GetMapping("/admin")
+ public String adminHello() {
+ return "Hello from admin";
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java
new file mode 100644
index 0000000000..ce874e313e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/annotations/websecurity/CustomWebSecurityConfig.java
@@ -0,0 +1,29 @@
+package com.baeldung.annotations.websecurity;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableWebSecurity
+public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .antMatchers("/admin/**")
+ .hasRole("ADMIN")
+ .antMatchers("/protected/**")
+ .hasRole("USER");
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web
+ .ignoring()
+ .antMatchers("/public/*");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt b/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt
new file mode 100644
index 0000000000..b58fe6d244
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/main/resources/public/hello/baeldung.txt
@@ -0,0 +1 @@
+Hello From Baeldung
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java
new file mode 100644
index 0000000000..be9dff714b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/globalmethod/GlobalMethodSpringBootIntegrationTest.java
@@ -0,0 +1,104 @@
+package com.baeldung.annotations.globalmethod;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.test.context.support.WithAnonymousUser;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+public class GlobalMethodSpringBootIntegrationTest {
+ public static final String HELLO_JSR_250 = "Hello Jsr250";
+ public static final String HELLO_PUBLIC = "Hello Public";
+ public static final String HELLO_PRE_AUTHORIZE = "Hello PreAuthorize";
+ public static final String PUBLIC_RESOURCE = "/hello/baeldung.txt";
+ public static final String HELLO_FROM_PUBLIC_RESOURCE = "Hello From Baeldung";
+ private static final String PROTECTED_METHOD = "/protected";
+
+ @Autowired
+ private TestRestTemplate template;
+
+ @Autowired
+ private AnnotationSecuredController api;
+
+ @WithMockUser(username="baeldung", roles = "USER")
+ @Test
+ public void givenUserWithRole_whenJsr250_thenOk() {
+ assertThat(api.jsr250Hello()).isEqualTo(HELLO_JSR_250);
+ }
+
+ @WithMockUser(username="baeldung", roles = "NOT-USER")
+ @Test(expected = AccessDeniedException.class)
+ public void givenWrongRole_whenJsr250_thenAccessDenied() {
+ api.jsr250Hello();
+ }
+
+ @Test
+ @WithAnonymousUser
+ public void givenAnonymousUser_whenPublic_thenOk() {
+ assertThat(api.publicHello()).isEqualTo(HELLO_PUBLIC);
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ @WithAnonymousUser
+ public void givenAnonymousUser_whenJsr250_thenAccessDenied() {
+ api.jsr250Hello();
+ }
+
+ // Tests for indirect calling of method
+ @Test
+ @WithAnonymousUser
+ public void givenAnonymousUser_whenIndirectCall_thenNoSecurity() {
+ assertThat(api.indirectHello()).isEqualTo(HELLO_JSR_250);
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ @WithAnonymousUser
+ public void givenAnonymousUser_whenIndirectToDifferentClass_thenAccessDenied() {
+ api.differentClassHello();
+ }
+
+ // Tests for static resource
+ @Test
+ public void givenPublicResource_whenGetViaWeb_thenOk() {
+ ResponseEntity result = template.getForEntity(PUBLIC_RESOURCE, String.class);
+ assertEquals(HELLO_FROM_PUBLIC_RESOURCE, result.getBody());
+ }
+
+ @Test
+ public void givenProtectedMethod_whenGetViaWeb_thenRedirectToLogin() {
+ ResponseEntity result = template.getForEntity(PROTECTED_METHOD, String.class);
+ assertEquals(HttpStatus.FOUND, result.getStatusCode());
+ }
+
+ // Tests for preAuthorize annotations
+ @WithMockUser(username="baeldung", roles = "USER")
+ @Test
+ public void givenUserWithRole_whenCallPreAuthorize_thenOk() {
+ assertThat(api.preAuthorizeHello()).isEqualTo(HELLO_PRE_AUTHORIZE);
+ }
+
+ @WithMockUser(username="baeldung", roles = "NOT-USER")
+ @Test(expected = AccessDeniedException.class)
+ public void givenWrongRole_whenCallPreAuthorize_thenAccessDenied() {
+ api.preAuthorizeHello();
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ @WithAnonymousUser
+ public void givenAnonymousUser_whenCallPreAuthorize_thenAccessDenied() {
+ api.preAuthorizeHello();
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java
new file mode 100644
index 0000000000..1360ae939d
--- /dev/null
+++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/annotations/websecurity/WebSecuritySpringBootIntegrationTest.java
@@ -0,0 +1,62 @@
+package com.baeldung.annotations.websecurity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+public class WebSecuritySpringBootIntegrationTest {
+ private static final String PUBLIC_RESOURCE = "/hello/baeldung.txt";
+ private static final String HELLO_FROM_PUBLIC_RESOURCE = "Hello From Baeldung";
+
+ @Autowired
+ private ConfigSecuredController api;
+
+ @Autowired
+ private TestRestTemplate template;
+
+ @Test
+ public void whenCallPublicDirectly_thenOk() {
+ assertThat(api.publicHello()).isEqualTo("Hello Public");
+ }
+
+ @Test
+ public void whenCallProtectedDirectly_thenNoSecurity() {
+ assertThat(api.protectedHello()).isEqualTo("Hello from protected");
+ }
+
+ @Test
+ public void whenGetProtectedViaWeb_thenForbidden() {
+ ResponseEntity result = template.getForEntity("/protected", String.class);
+ assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode());
+ }
+
+ @Test
+ public void whenGetAdminViaWeb_thenForbidden() {
+ ResponseEntity result = template.getForEntity("/admin", String.class);
+ assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode());
+ }
+
+ @Test
+ public void whenGetPublicViaWeb_thenSuccess() {
+ ResponseEntity result = template.getForEntity("/public", String.class);
+ assertEquals(HttpStatus.OK, result.getStatusCode());
+ }
+
+ @Test
+ public void givenPublicResource_whenGetViaWeb_thenOk() {
+ ResponseEntity result = template.getForEntity(PUBLIC_RESOURCE, String.class);
+ assertEquals(HELLO_FROM_PUBLIC_RESOURCE, result.getBody());
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-springdoc/README.md b/spring-boot-modules/spring-boot-springdoc/README.md
index 608e4afa2e..4ac4147da6 100644
--- a/spring-boot-modules/spring-boot-springdoc/README.md
+++ b/spring-boot-modules/spring-boot-springdoc/README.md
@@ -3,3 +3,4 @@
- [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation)
- [Spring REST Docs vs OpenAPI](https://www.baeldung.com/spring-rest-docs-vs-openapi)
- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)
+- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated)
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java
new file mode 100644
index 0000000000..3141428514
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.tagopenapi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication()
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java
new file mode 100644
index 0000000000..780b202d3c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java
@@ -0,0 +1,20 @@
+package com.baeldung.tagopenapi.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+
+public class OpenApi {
+
+ @Bean
+ public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) {
+ return new OpenAPI().info(new Info().title("Controller API")
+ .version(appVersion)
+ .description("This is a sample server created using springdocs - a library for OpenAPI 3 with spring boot.")
+ .termsOfService("http://swagger.io/terms/")
+ .license(new License().name("Apache 2.0")
+ .url("http://springdoc.org")));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java
new file mode 100644
index 0000000000..3c81c1cee7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java
@@ -0,0 +1,20 @@
+package com.baeldung.tagopenapi.controller;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/book")
+@Tag(name = "book service", description = "the book API with description tag annotation")
+public class BookController {
+
+ @GetMapping("/")
+ public List getBooks() {
+ return Arrays.asList("book1", "book2");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-swagger/README.md b/spring-boot-modules/spring-boot-swagger/README.md
index 1038031210..f94ae75c41 100644
--- a/spring-boot-modules/spring-boot-swagger/README.md
+++ b/spring-boot-modules/spring-boot-swagger/README.md
@@ -1,3 +1,4 @@
## Relevant Articles:
- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)
+- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated)
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java
new file mode 100644
index 0000000000..9232152b5b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.apiswagger;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication()
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java
new file mode 100644
index 0000000000..d9f51698c3
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java
@@ -0,0 +1,26 @@
+package com.baeldung.apiswagger.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Tag;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+@Configuration
+public class SwaggerConfiguration {
+
+ public static final String BOOK_TAG = "book service";
+
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any())
+ .build()
+ .tags(new Tag(BOOK_TAG, "the book API with description api tag"));
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java
new file mode 100644
index 0000000000..394c640751
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java
@@ -0,0 +1,21 @@
+package com.baeldung.apiswagger.controller;
+
+import com.baeldung.apiswagger.config.SwaggerConfiguration;
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/book")
+@Api(tags = {SwaggerConfiguration.BOOK_TAG})
+public class BookController {
+
+ @GetMapping("/")
+ public List getBooks() {
+ return Arrays.asList("book1", "book2");
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing/pom.xml b/spring-boot-modules/spring-boot-testing/pom.xml
index 1860818b7b..f70e77b31a 100644
--- a/spring-boot-modules/spring-boot-testing/pom.xml
+++ b/spring-boot-modules/spring-boot-testing/pom.xml
@@ -80,11 +80,6 @@
${spock.version}
test
-
- io.rest-assured
- rest-assured
- test
-
@@ -147,6 +142,7 @@
1.2-groovy-2.4
1.6
0.7.2
+ 2.5.0
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
index daab3e8d2c..70cae370a4 100644
--- a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
@@ -6,4 +6,3 @@ spring.redis.port= 6379
spring.security.user.name=john
spring.security.user.password=123
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-validation/.gitignore b/spring-boot-modules/spring-boot-validation/.gitignore
new file mode 100644
index 0000000000..8f5ee06047
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/.gitignore
@@ -0,0 +1,63 @@
+# Created by https://www.gitignore.io/api/eclipse
+
+### Eclipse ###
+
+.metadata
+target/
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+### Eclipse Patch ###
+# Eclipse Core
+.project
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# End of https://www.gitignore.io/api/eclipse
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-validation/README.md b/spring-boot-modules/spring-boot-validation/README.md
new file mode 100644
index 0000000000..8c3c8b9305
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles
+
+- [Spring Validation in the Service Layer](https://www.baeldung.com/spring-service-layer-validation)
diff --git a/spring-boot-modules/spring-boot-validation/pom.xml b/spring-boot-modules/spring-boot-validation/pom.xml
new file mode 100644
index 0000000000..795d506dc0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/pom.xml
@@ -0,0 +1,37 @@
+
+
+ 4.0.0
+ com.baeldung
+ spring-boot-validation
+ 0.0.1-SNAPSHOT
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java
new file mode 100644
index 0000000000..b976342dda
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/SpringServiceLayerValidationApp.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.servicevalidation;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringServiceLayerValidationApp {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringServiceLayerValidationApp.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java
new file mode 100644
index 0000000000..33d9966e42
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/controller/UserAccountController.java
@@ -0,0 +1,22 @@
+package com.baeldung.spring.servicevalidation.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.spring.servicevalidation.domain.UserAccount;
+import com.baeldung.spring.servicevalidation.service.UserAccountService;
+
+@RestController
+public class UserAccountController {
+
+ @Autowired
+ private UserAccountService service;
+
+ @PostMapping("/addUserAccount")
+ public Object addUserAccount(@RequestBody UserAccount userAccount) {
+ return service.addUserAccount(userAccount);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java
new file mode 100644
index 0000000000..c49c5220f4
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/dao/UserAccountDao.java
@@ -0,0 +1,31 @@
+package com.baeldung.spring.servicevalidation.dao;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.stereotype.Service;
+
+import com.baeldung.spring.servicevalidation.domain.UserAccount;
+
+@Service
+public class UserAccountDao {
+
+ private Map DB = new HashMap();
+
+ public String addUserAccount(UserAccount useraccount) {
+ DB.put(useraccount.getName(), useraccount);
+ return "success";
+ }
+
+ public Collection getAllUserAccounts() {
+
+ Collection list = DB.values();
+ if (list.isEmpty()) {
+ list.addAll(DB.values());
+ }
+ return list;
+
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java
new file mode 100644
index 0000000000..5b0e795a8a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAccount.java
@@ -0,0 +1,77 @@
+package com.baeldung.spring.servicevalidation.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;
+
+public class UserAccount {
+
+ @NotNull(message = "Password must be between 4 to 15 characters")
+ @Size(min = 4, max = 15)
+ private String password;
+
+ @NotBlank(message = "Name must not be blank")
+ private String name;
+
+ @Min(value = 18, message = "Age should not be less than 18")
+ private int age;
+
+ @NotBlank(message = "Phone must not be blank")
+ private String phone;
+
+ @Valid
+ @NotNull(message = "UserAddress must not be blank")
+ 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-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java
new file mode 100644
index 0000000000..cd149dc5f5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/domain/UserAddress.java
@@ -0,0 +1,18 @@
+package com.baeldung.spring.servicevalidation.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-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java
new file mode 100644
index 0000000000..9d79e4e226
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java
@@ -0,0 +1,41 @@
+package com.baeldung.spring.servicevalidation.service;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.spring.servicevalidation.dao.UserAccountDao;
+import com.baeldung.spring.servicevalidation.domain.UserAccount;
+
+@Service
+public class UserAccountService {
+
+ @Autowired
+ private Validator validator;
+
+ @Autowired
+ private UserAccountDao dao;
+
+ public String addUserAccount(UserAccount useraccount) {
+
+ Set> violations = validator.validate(useraccount);
+
+ if (!violations.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ for (ConstraintViolation constraintViolation : violations) {
+ sb.append(constraintViolation.getMessage());
+ }
+
+ throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
+ }
+
+ dao.addUserAccount(useraccount);
+ return "Account for " + useraccount.getName() + " Added!";
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java b/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java
new file mode 100644
index 0000000000..f4076a7756
--- /dev/null
+++ b/spring-boot-modules/spring-boot-validation/src/test/java/com/baeldung/SpringContextTest.java
@@ -0,0 +1,14 @@
+package com.baeldung;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import com.baeldung.spring.servicevalidation.SpringServiceLayerValidationApp;
+
+@SpringBootTest(classes = SpringServiceLayerValidationApp.class)
+public class SpringContextTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java b/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java
index 373ae8f745..55543b0ba7 100644
--- a/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java
+++ b/spring-boot-modules/spring-boot/src/main/java/com/baeldung/common/error/MyCustomErrorController.java
@@ -7,18 +7,9 @@ public class MyCustomErrorController implements ErrorController {
private static final String PATH = "/error";
- public MyCustomErrorController() {
- // TODO Auto-generated constructor stub
- }
-
@GetMapping(value = PATH)
public String error() {
return "Error haven";
}
- @Override
- public String getErrorPath() {
- return PATH;
- }
-
}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java
new file mode 100644
index 0000000000..42bf50ac5f
--- /dev/null
+++ b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java
@@ -0,0 +1,29 @@
+package com.baeldung.spring;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
+import org.springframework.oxm.xstream.XStreamMarshaller;
+
+/**
+ * Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations.
+ *
+ * ATTENTION: Multiple converter registration of the same type most likely causes problem (serialize twice, etc.)
+ * Therefore, be sure to remove manually added XML message converter first then uncomment
+ * this @{@link org.springframework.context.annotation.Configuration} to use
+ */
+//@Configuration
+public class ConverterExtensionsConfig {
+
+ @Bean
+ public HttpMessageConverter
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-config/pom.xml b/spring-cloud/spring-cloud-config/pom.xml
index 1e46b9accb..4eda3dda0a 100644
--- a/spring-cloud/spring-cloud-config/pom.xml
+++ b/spring-cloud/spring-cloud-config/pom.xml
@@ -34,7 +34,7 @@
- 2020.0.0
+ 2020.0.3
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-config/server/pom.xml b/spring-cloud/spring-cloud-config/server/pom.xml
index c59b6583f2..36d1b5b3aa 100644
--- a/spring-cloud/spring-cloud-config/server/pom.xml
+++ b/spring-cloud/spring-cloud-config/server/pom.xml
@@ -51,4 +51,5 @@
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-contract/pom.xml b/spring-cloud/spring-cloud-contract/pom.xml
index 1d12656f98..8546e76586 100644
--- a/spring-cloud/spring-cloud-contract/pom.xml
+++ b/spring-cloud/spring-cloud-contract/pom.xml
@@ -31,7 +31,6 @@
spring-boot-starter-data-rest
${spring-boot.version}
-
org.springframework.cloud
spring-cloud-contract-wiremock
diff --git a/spring-cloud/spring-cloud-dapr/README.md b/spring-cloud/spring-cloud-dapr/README.md
new file mode 100644
index 0000000000..8f6ccfd9b6
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [An Intro to Dapr with Spring Cloud Gateway](https://www.baeldung.com/dapr-spring-cloud-gateway)
diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml
new file mode 100644
index 0000000000..d80655f0a2
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/dapr-config/basic-config.yaml
@@ -0,0 +1,5 @@
+apiVersion: dapr.io/v1alpha1
+kind: Configuration
+metadata:
+ name: daprConfig
+spec: {}
diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml
new file mode 100644
index 0000000000..638af7cf0d
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/dapr-config/consul-config.yaml
@@ -0,0 +1,9 @@
+apiVersion: dapr.io/v1alpha1
+kind: Configuration
+metadata:
+ name: daprConfig
+spec:
+ nameResolution:
+ component: "consul"
+ configuration:
+ selfRegister: true
diff --git a/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml b/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml
new file mode 100644
index 0000000000..8e50decf03
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/dapr-config/consul-zipkin-config.yaml
@@ -0,0 +1,13 @@
+apiVersion: dapr.io/v1alpha1
+kind: Configuration
+metadata:
+ name: daprConfig
+spec:
+ nameResolution:
+ component: "consul"
+ configuration:
+ selfRegister: true
+ tracing:
+ samplingRate: "1"
+ zipkin:
+ endpointAddress: "http://localhost:9411/api/v2/spans"
diff --git a/spring-cloud/spring-cloud-dapr/gateway/pom.xml b/spring-cloud/spring-cloud-dapr/gateway/pom.xml
new file mode 100644
index 0000000000..13c1556cfe
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/gateway/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+ com.baeldung.spring.cloud.spring-cloud-dapr
+ gateway
+ 1.0-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.2
+
+
+
+ 11
+ 11
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ 2020.0.3
+ pom
+ import
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java b/spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java
similarity index 62%
rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java
rename to spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java
index 094d95b93f..7166b5b31f 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java
+++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/java/org/example/gateway/GatewayApp.java
@@ -1,13 +1,11 @@
-package com.example.demo;
+package org.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
-public class DemoApplication {
-
+public class GatewayApp {
public static void main(String[] args) {
- SpringApplication.run(DemoApplication.class, args);
+ SpringApplication.run(GatewayApp.class, args);
}
-
}
diff --git a/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml
new file mode 100644
index 0000000000..f25d7f0128
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-no-dapr.yml
@@ -0,0 +1,14 @@
+server:
+ port: 3000
+
+spring:
+ cloud:
+ gateway:
+ routes:
+ - id: greeting-service
+ uri: http://localhost:3001/
+ predicates:
+ - Path=/**
+ filters:
+ - RewritePath=/?(?.*), /$\{segment}
+
diff --git a/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml
new file mode 100644
index 0000000000..ee67c76339
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/gateway/src/main/resources/application-with-dapr.yml
@@ -0,0 +1,13 @@
+server:
+ port: 3000
+
+spring:
+ cloud:
+ gateway:
+ routes:
+ - id: greeting-service
+ uri: http://localhost:4000/
+ predicates:
+ - Path=/**
+ filters:
+ - RewritePath=//?(?.*), /v1.0/invoke/greeting/method/$\{segment}
diff --git a/spring-cloud/spring-cloud-dapr/greeting/pom.xml b/spring-cloud/spring-cloud-dapr/greeting/pom.xml
new file mode 100644
index 0000000000..58de7240a2
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/greeting/pom.xml
@@ -0,0 +1,33 @@
+
+
+ 4.0.0
+ com.baeldung.spring.cloud.spring-cloud-dapr
+ greeting
+ 1.0-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.2
+
+
+
+ 11
+ 11
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java
new file mode 100644
index 0000000000..2feca49daa
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingApp.java
@@ -0,0 +1,11 @@
+package org.example.hello;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GreetingApp {
+ public static void main(String[] args) {
+ SpringApplication.run(GreetingApp.class, args);
+ }
+}
diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java
new file mode 100644
index 0000000000..5210add4e0
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/java/org/example/hello/GreetingController.java
@@ -0,0 +1,22 @@
+package org.example.hello;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class GreetingController {
+ @GetMapping(value = "/")
+ public String getGreeting() {
+ return "Welcome to the greeting service.";
+ }
+
+ @GetMapping(value = "/hello")
+ public String getHello() {
+ return "Hello world!";
+ }
+
+ @GetMapping(value = "/goodbye")
+ public String getGoodbye() {
+ return "Goodbye, cruel world!";
+ }
+}
diff --git a/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml b/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml
new file mode 100644
index 0000000000..7147f91d26
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/greeting/src/main/resources/application.yml
@@ -0,0 +1,2 @@
+server:
+ port: 3001
diff --git a/spring-cloud/spring-cloud-dapr/pom.xml b/spring-cloud/spring-cloud-dapr/pom.xml
new file mode 100644
index 0000000000..76d3cbdaf8
--- /dev/null
+++ b/spring-cloud/spring-cloud-dapr/pom.xml
@@ -0,0 +1,19 @@
+
+
+ 4.0.0
+ spring-cloud-dapr
+ pom
+
+
+ com.baeldung.spring.cloud
+ spring-cloud
+ 1.0.0-SNAPSHOT
+
+
+
+ gateway
+ greeting
+
+
diff --git a/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java
new file mode 100644
index 0000000000..89221943f6
--- /dev/null
+++ b/spring-cloud/spring-cloud-eureka/spring-cloud-eureka-feign-client/src/main/java/com/baeldung/spring/cloud/feign/client/NoFeignClientController.java
@@ -0,0 +1,41 @@
+package com.baeldung.spring.cloud.feign.client;
+
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.discovery.EurekaClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URI;
+
+@Controller
+public class NoFeignClientController {
+
+ private static final String SERVICE_NAME = "spring-cloud-eureka-client";
+
+ @Autowired
+ private EurekaClient eurekaClient;
+
+ @RequestMapping("/get-greeting-no-feign")
+ public String greeting(Model model) {
+
+ InstanceInfo service = eurekaClient
+ .getApplication(SERVICE_NAME)
+ .getInstances()
+ .get(0);
+
+ String hostName = service.getHostName();
+ int port = service.getPort();
+
+ URI url = URI.create("http://" + hostName + ":" + port + "/greeting");
+
+ ResponseEntity response = new RestTemplate().getForEntity(url, String.class);
+
+ model.addAttribute("greeting", response.getBody());
+
+ return "greeting-view";
+ }
+}
diff --git a/spring-cloud/spring-cloud-kubernetes/pom.xml b/spring-cloud/spring-cloud-kubernetes/pom.xml
index 1a3e3826d4..d8894eb36f 100644
--- a/spring-cloud/spring-cloud-kubernetes/pom.xml
+++ b/spring-cloud/spring-cloud-kubernetes/pom.xml
@@ -38,6 +38,7 @@
- 2020.0.1
+ 2020.0.3
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-netflix-feign/pom.xml b/spring-cloud/spring-cloud-netflix-feign/pom.xml
index 6180ee7575..8ff09d3070 100644
--- a/spring-cloud/spring-cloud-netflix-feign/pom.xml
+++ b/spring-cloud/spring-cloud-netflix-feign/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.baeldung.cloud
- spring-cloud-netlix-feign
+ spring-cloud-netflix-feign
0.0.1-SNAPSHOT
spring-cloud-netflix-feign
Netflix Feign project for Spring Boot
diff --git a/spring-cloud/spring-cloud-openfeign/README.md b/spring-cloud/spring-cloud-openfeign/README.md
index bcfd769d0c..5d3dc060c7 100644
--- a/spring-cloud/spring-cloud-openfeign/README.md
+++ b/spring-cloud/spring-cloud-openfeign/README.md
@@ -3,3 +3,4 @@
- [Introduction to Spring Cloud OpenFeign](https://www.baeldung.com/spring-cloud-openfeign)
- [Differences Between Netflix Feign and OpenFeign](https://www.baeldung.com/netflix-feign-vs-openfeign)
- [File Upload With Open Feign](https://www.baeldung.com/java-feign-file-upload)
+- [Feign Logging Configuration](https://www.baeldung.com/java-feign-logging)
diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java
new file mode 100644
index 0000000000..9416bd30f0
--- /dev/null
+++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java
@@ -0,0 +1,13 @@
+package com.baeldung.cloud.openfeign.client;
+
+import com.baeldung.cloud.openfeign.config.FeignConfig;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@FeignClient(name = "user-client", url="https://jsonplaceholder.typicode.com", configuration = FeignConfig.class)
+public interface UserClient {
+
+ @RequestMapping(value = "/users", method = RequestMethod.GET)
+ String getUsers();
+}
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java
new file mode 100644
index 0000000000..d51e97b9e4
--- /dev/null
+++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java
@@ -0,0 +1,12 @@
+package com.baeldung.cloud.openfeign.config;
+
+import feign.Logger;
+import org.springframework.context.annotation.Bean;
+
+public class FeignConfig {
+
+ @Bean
+ Logger.Level feignLoggerLevel() {
+ return Logger.Level.FULL;
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml b/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml
index 2fa003a634..c7ff472655 100644
--- a/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml
+++ b/spring-cloud/spring-cloud-rest/spring-cloud-rest-books-api/pom.xml
@@ -75,4 +75,4 @@
-
+
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-ribbon-client/pom.xml b/spring-cloud/spring-cloud-ribbon-client/pom.xml
index 77c25b2f6b..2c2bce4bcd 100644
--- a/spring-cloud/spring-cloud-ribbon-client/pom.xml
+++ b/spring-cloud/spring-cloud-ribbon-client/pom.xml
@@ -47,7 +47,7 @@
- 2020.0.1
+ 2020.0.3
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-security/pom.xml b/spring-cloud/spring-cloud-security/pom.xml
index 0227989f99..d9072af91b 100644
--- a/spring-cloud/spring-cloud-security/pom.xml
+++ b/spring-cloud/spring-cloud-security/pom.xml
@@ -34,7 +34,7 @@
- 2020.0.1
+ 2020.0.3
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-sentinel/pom.xml b/spring-cloud/spring-cloud-sentinel/pom.xml
index a36dcc51bc..f26a13f6f8 100644
--- a/spring-cloud/spring-cloud-sentinel/pom.xml
+++ b/spring-cloud/spring-cloud-sentinel/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
spring-cloud-sentinel
spring-cloud-sentinel
diff --git a/spring-cloud/spring-cloud-task/pom.xml b/spring-cloud/spring-cloud-task/pom.xml
index bf43799d2d..0cfc62bdbb 100644
--- a/spring-cloud/spring-cloud-task/pom.xml
+++ b/spring-cloud/spring-cloud-task/pom.xml
@@ -48,8 +48,8 @@
- Hoxton.SR4
- 2.2.3.RELEASE
+ 2020.0.3
+ 2.3.3
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
index cfee3ce656..0ba93282b9 100644
--- a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
@@ -42,6 +42,7 @@
org.springframework.cloud
spring-cloud-starter-netflix-zuul
+ ${spring-cloud-netflix-zuul.version}
org.springframework.boot
diff --git a/spring-cloud/spring-cloud-zuul-fallback/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/pom.xml
index 1c426503e5..1aa29e867d 100644
--- a/spring-cloud/spring-cloud-zuul-fallback/pom.xml
+++ b/spring-cloud/spring-cloud-zuul-fallback/pom.xml
@@ -21,7 +21,8 @@
- Finchley.SR2
+ 2020.0.3
+ 2.2.7.RELEASE
3.1.1
diff --git a/spring-cloud/spring-cloud-zuul/pom.xml b/spring-cloud/spring-cloud-zuul/pom.xml
index fc3a127e90..0590774bef 100644
--- a/spring-cloud/spring-cloud-zuul/pom.xml
+++ b/spring-cloud/spring-cloud-zuul/pom.xml
@@ -81,8 +81,8 @@
- 2020.0.0
- 2.2.2.RELEASE
+ 2020.0.3
+ 2.2.7.RELEASE
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml b/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml
index e09fcd3711..d2b28643fa 100644
--- a/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml
+++ b/spring-cloud/spring-cloud-zuul/spring-zuul-rate-limiting/pom.xml
@@ -42,6 +42,7 @@
2.2.0.RELEASE
+ 2.4.7
\ No newline at end of file
diff --git a/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java b/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java
index 1c6f04331e..8d9ab5952c 100644
--- a/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java
+++ b/spring-data-rest/src/main/java/com/baeldung/books/config/RestConfig.java
@@ -2,18 +2,19 @@ package com.baeldung.books.config;
import com.baeldung.books.models.WebsiteUser;
import com.baeldung.books.projections.CustomBook;
-
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.mapping.ExposureConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
import org.springframework.http.HttpMethod;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
@Configuration
public class RestConfig implements RepositoryRestConfigurer {
@Override
- public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration) {
+ public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration,
+ CorsRegistry cors) {
repositoryRestConfiguration.getProjectionConfiguration().addProjection(CustomBook.class);
ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration();
config.forDomainType(WebsiteUser.class).withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH));
diff --git a/spring-ejb/ejb-beans/pom.xml b/spring-ejb/ejb-beans/pom.xml
index cdc28ef5d7..d820819a78 100644
--- a/spring-ejb/ejb-beans/pom.xml
+++ b/spring-ejb/ejb-beans/pom.xml
@@ -94,7 +94,6 @@
test
-
diff --git a/spring-jenkins-pipeline/pom.xml b/spring-jenkins-pipeline/pom.xml
index f2fd4191fd..6f00dd5820 100644
--- a/spring-jenkins-pipeline/pom.xml
+++ b/spring-jenkins-pipeline/pom.xml
@@ -81,7 +81,7 @@
2.17
- 2.0.0
+ 3.0.0
\ No newline at end of file
diff --git a/spring-kafka/README.md b/spring-kafka/README.md
index ddb086c3bd..2b71beaac9 100644
--- a/spring-kafka/README.md
+++ b/spring-kafka/README.md
@@ -6,6 +6,8 @@ This module contains articles about Spring with Kafka
- [Intro to Apache Kafka with Spring](https://www.baeldung.com/spring-kafka)
- [Testing Kafka and Spring Boot](https://www.baeldung.com/spring-boot-kafka-testing)
+- [Monitor the Consumer Lag in Apache Kafka](https://www.baeldung.com/java-kafka-consumer-lag)
+- [Send Large Messages With Kafka](https://www.baeldung.com/java-kafka-send-large-message)
### Intro
diff --git a/spring-kafka/pom.xml b/spring-kafka/pom.xml
index 235dd75966..2db62044b2 100644
--- a/spring-kafka/pom.xml
+++ b/spring-kafka/pom.xml
@@ -40,11 +40,16 @@
${testcontainers-kafka.version}
test
+
+ commons-collections
+ commons-collections
+ 3.2.1
+
- 2.5.8.RELEASE
- 1.15.0
+ 2.7.2
+ 1.15.3
\ No newline at end of file
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java b/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java
new file mode 100644
index 0000000000..9275151d50
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/LagAnalyzerApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.monitoring;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableScheduling
+public class LagAnalyzerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(LagAnalyzerApplication.class, args);
+ while (true) ;
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile b/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile
new file mode 100644
index 0000000000..f85334057d
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/Makefile
@@ -0,0 +1,10 @@
+build:
+ mvn clean install -B -U
+start-kafka:
+ docker-compose up -d
+check-kafka:
+ nc -z localhost 2181
+ nc -z localhost 9092
+ docker-compose logs kafka | grep -i 'started'
+stop-kafka:
+ docker-compose down --remove-orphans
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/README.md b/spring-kafka/src/main/java/com/baeldung/monitoring/README.md
new file mode 100644
index 0000000000..1b3f638ae2
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/README.md
@@ -0,0 +1,16 @@
+## Monitoring Consumer Lag
+
+## Spin Up Local Kafka Container
+```
+$ make start-kafka
+```
+
+## Verify that Kafka is Up
+```
+$ make check-kafka
+```
+
+## Stop Local Kafka Container
+```
+$ make stop-kafka
+```
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml b/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml
new file mode 100644
index 0000000000..507b8e2f3c
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/docker-compose.yml
@@ -0,0 +1,23 @@
+version: '2'
+services:
+ zookeeper:
+ image: confluentinc/cp-zookeeper:latest
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2181
+ ZOOKEEPER_TICK_TIME: 2000
+ ports:
+ - 22181:2181
+
+ kafka:
+ image: confluentinc/cp-kafka:latest
+ depends_on:
+ - zookeeper
+ ports:
+ - 9092:9092
+ environment:
+ KAFKA_BROKER_ID: 1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
+ KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
\ No newline at end of file
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java
new file mode 100644
index 0000000000..b046f0b2c4
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LagAnalyzerService.java
@@ -0,0 +1,105 @@
+package com.baeldung.monitoring.service;
+
+import com.baeldung.monitoring.util.MonitoringUtil;
+import org.apache.kafka.clients.admin.AdminClient;
+import org.apache.kafka.clients.admin.AdminClientConfig;
+import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.clients.consumer.OffsetAndMetadata;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+
+@Service
+public class LagAnalyzerService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LagAnalyzerService.class);
+
+ private final AdminClient adminClient;
+ private final KafkaConsumer consumer;
+
+ @Autowired
+ public LagAnalyzerService(
+ @Value("${monitor.kafka.bootstrap.config}") String bootstrapServerConfig) {
+ adminClient = getAdminClient(bootstrapServerConfig);
+ consumer = getKafkaConsumer(bootstrapServerConfig);
+ }
+
+ public Map analyzeLag(
+ String groupId)
+ throws ExecutionException, InterruptedException {
+ Map consumerGrpOffsets = getConsumerGrpOffsets(groupId);
+ Map producerOffsets = getProducerOffsets(consumerGrpOffsets);
+ Map lags = computeLags(consumerGrpOffsets, producerOffsets);
+ for (Map.Entry lagEntry : lags.entrySet()) {
+ String topic = lagEntry.getKey().topic();
+ int partition = lagEntry.getKey().partition();
+ Long lag = lagEntry.getValue();
+ LOGGER.info("Time={} | Lag for topic = {}, partition = {} is {}",
+ MonitoringUtil.time(),
+ topic,
+ partition,
+ lag);
+ }
+ return lags;
+ }
+
+ public Map getConsumerGrpOffsets(String groupId)
+ throws ExecutionException, InterruptedException {
+ ListConsumerGroupOffsetsResult info = adminClient.listConsumerGroupOffsets(groupId);
+ Map metadataMap
+ = info.partitionsToOffsetAndMetadata().get();
+ Map groupOffset = new HashMap<>();
+ for (Map.Entry entry : metadataMap.entrySet()) {
+ TopicPartition key = entry.getKey();
+ OffsetAndMetadata metadata = entry.getValue();
+ groupOffset.putIfAbsent(new TopicPartition(key.topic(), key.partition()), metadata.offset());
+ }
+ return groupOffset;
+ }
+
+ private Map getProducerOffsets(
+ Map consumerGrpOffset) {
+ List topicPartitions = new LinkedList<>();
+ for (Map.Entry entry : consumerGrpOffset.entrySet()) {
+ TopicPartition key = entry.getKey();
+ topicPartitions.add(new TopicPartition(key.topic(), key.partition()));
+ }
+ return consumer.endOffsets(topicPartitions);
+ }
+
+ public Map computeLags(
+ Map consumerGrpOffsets,
+ Map producerOffsets) {
+ Map lags = new HashMap<>();
+ for (Map.Entry entry : consumerGrpOffsets.entrySet()) {
+ Long producerOffset = producerOffsets.get(entry.getKey());
+ Long consumerOffset = consumerGrpOffsets.get(entry.getKey());
+ long lag = Math.abs(Math.max(0, producerOffset) - Math.max(0, consumerOffset));
+ lags.putIfAbsent(entry.getKey(), lag);
+ }
+ return lags;
+ }
+
+ private AdminClient getAdminClient(String bootstrapServerConfig) {
+ Properties config = new Properties();
+ config.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServerConfig);
+ return AdminClient.create(config);
+ }
+
+ private KafkaConsumer getKafkaConsumer(String bootstrapServerConfig) {
+ Properties properties = new Properties();
+ properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServerConfig);
+ properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+ properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+ return new KafkaConsumer<>(properties);
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java
new file mode 100644
index 0000000000..a20b9e9a0c
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/service/LiveLagAnalyzerService.java
@@ -0,0 +1,28 @@
+package com.baeldung.monitoring.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.ExecutionException;
+
+@Service
+public class LiveLagAnalyzerService {
+
+ private final LagAnalyzerService lagAnalyzerService;
+ private final String groupId;
+
+ @Autowired
+ public LiveLagAnalyzerService(
+ LagAnalyzerService lagAnalyzerService,
+ @Value(value = "${monitor.kafka.consumer.groupid}") String groupId) {
+ this.lagAnalyzerService = lagAnalyzerService;
+ this.groupId = groupId;
+ }
+
+ @Scheduled(fixedDelay = 5000L)
+ public void liveLagAnalysis() throws ExecutionException, InterruptedException {
+ lagAnalyzerService.analyzeLag(groupId);
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java
new file mode 100644
index 0000000000..2d376432e5
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ConsumerSimulator.java
@@ -0,0 +1,17 @@
+package com.baeldung.monitoring.simulation;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ConsumerSimulator {
+
+ @KafkaListener(
+ topics = "${monitor.topic.name}",
+ containerFactory = "kafkaListenerContainerFactory",
+ autoStartup = "${monitor.consumer.simulate}")
+ public void listenGroup(String message) throws InterruptedException {
+ Thread.sleep(10L);
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java
new file mode 100644
index 0000000000..a4a8847bcf
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaConsumerConfig.java
@@ -0,0 +1,54 @@
+package com.baeldung.monitoring.simulation;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@EnableKafka
+@Configuration
+public class KafkaConsumerConfig {
+
+ @Value(value = "${monitor.kafka.bootstrap.config}")
+ private String bootstrapAddress;
+ @Value(value = "${monitor.kafka.consumer.groupid}")
+ private String groupId;
+ @Value(value = "${monitor.kafka.consumer.groupid.simulate}")
+ private String simulateGroupId;
+ @Value(value = "${monitor.producer.simulate}")
+ private boolean enabled;
+
+ public ConsumerFactory consumerFactory(String groupId) {
+ Map props = new HashMap<>();
+ props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
+ if (enabled) {
+ props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
+ } else {
+ props.put(ConsumerConfig.GROUP_ID_CONFIG, simulateGroupId);
+ }
+ props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 0);
+ return new DefaultKafkaConsumerFactory<>(props);
+ }
+
+ @Bean
+ public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() {
+ ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>();
+ if (enabled) {
+ factory.setConsumerFactory(consumerFactory(groupId));
+ } else {
+ factory.setConsumerFactory(consumerFactory(simulateGroupId));
+ }
+ return factory;
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java
new file mode 100644
index 0000000000..80048a47ee
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/KafkaProducerConfig.java
@@ -0,0 +1,29 @@
+package com.baeldung.monitoring.simulation;
+
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+public class KafkaProducerConfig {
+
+ @Value("${monitor.kafka.bootstrap.config}")
+ private String bootstrapAddress;
+
+ @Bean
+ public KafkaTemplate kafkaTemplate() {
+ Map configProps = new HashMap<>();
+ configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
+ configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+ configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+ DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(configProps);
+ return new KafkaTemplate<>(producerFactory);
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java
new file mode 100644
index 0000000000..30476ff7ec
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/simulation/ProducerSimulator.java
@@ -0,0 +1,44 @@
+package com.baeldung.monitoring.simulation;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.support.SendResult;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.concurrent.ExecutionException;
+
+import static com.baeldung.monitoring.util.MonitoringUtil.endTime;
+import static com.baeldung.monitoring.util.MonitoringUtil.time;
+
+@Service
+public class ProducerSimulator {
+
+ private final KafkaTemplate kafkaTemplate;
+ private final String topicName;
+ private final boolean enabled;
+
+ @Autowired
+ public ProducerSimulator(
+ KafkaTemplate kafkaTemplate,
+ @Value(value = "${monitor.topic.name}") String topicName,
+ @Value(value = "${monitor.producer.simulate}") String enabled) {
+ this.kafkaTemplate = kafkaTemplate;
+ this.topicName = topicName;
+ this.enabled = BooleanUtils.toBoolean(enabled);
+ }
+
+ @Scheduled(fixedDelay = 1L, initialDelay = 5L)
+ public void sendMessage() throws ExecutionException, InterruptedException {
+ if (enabled) {
+ if (endTime.after(new Date())) {
+ String message = "msg-" + time();
+ SendResult result = kafkaTemplate.send(topicName, message).get();
+ }
+ }
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java b/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java
new file mode 100644
index 0000000000..a7ba78b1e3
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/monitoring/util/MonitoringUtil.java
@@ -0,0 +1,17 @@
+package com.baeldung.monitoring.util;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class MonitoringUtil {
+ public static final Date startTime = new Date();
+ public static final Date endTime = new Date(startTime.getTime() + 30 * 1000);
+
+ public static String time() {
+ DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
+ LocalDateTime now = LocalDateTime.now();
+ String date = dtf.format(now);
+ return date;
+ }
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java
new file mode 100644
index 0000000000..0af0a4b091
--- /dev/null
+++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java
@@ -0,0 +1,75 @@
+package com.baeldung.spring.kafka;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.core.KafkaTemplate;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+@SpringBootApplication
+public class KafkaApplicationLongMessage {
+
+ public static void main(String[] args) throws Exception {
+
+ ConfigurableApplicationContext context = SpringApplication.run(KafkaApplicationLongMessage.class, args);
+
+ LongMessageProducer producer = context.getBean(LongMessageProducer.class);
+
+ String fileData = readLongMessage();
+ producer.sendMessage(fileData);
+
+ //Deliberate delay to let listener consume produced message before main thread stops
+ Thread.sleep(5000);
+ context.close();
+ }
+
+ private static String readLongMessage() throws IOException {
+ String data = "";
+
+ //update complete location of large message here
+ data = new String(Files.readAllBytes(Paths.get("RandomTextFile.txt")));
+ return data;
+ }
+
+ @Bean
+ public LongMessageProducer longMessageProducer() {
+ return new LongMessageProducer();
+ }
+
+ @Bean
+ public LongMessageListener longMessageListener() {
+ return new LongMessageListener();
+ }
+
+ public static class LongMessageProducer {
+
+ @Autowired
+ private KafkaTemplate kafkaTemplate;
+
+ @Value(value = "${long.message.topic.name}")
+ private String topicName;
+
+ public void sendMessage(String message) {
+ kafkaTemplate.send(topicName, message);
+ System.out.println("Long message Sent");
+ }
+
+ }
+
+ public static class LongMessageListener {
+
+ @KafkaListener(topics = "${long.message.topic.name}", groupId = "longMessage", containerFactory = "longMessageKafkaListenerContainerFactory")
+ public void listenGroupLongMessage(String message) {
+ System.out.println("Received Message in group 'longMessage'");
+ }
+
+ }
+
+}
diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java
index abaa431eec..9495fcf508 100644
--- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java
+++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java
@@ -27,6 +27,8 @@ public class KafkaConsumerConfig {
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "20971520");
+ props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, "20971520");
return new DefaultKafkaConsumerFactory<>(props);
}
@@ -56,6 +58,11 @@ public class KafkaConsumerConfig {
return kafkaListenerContainerFactory("partitions");
}
+ @Bean
+ public ConcurrentKafkaListenerContainerFactory longMessageKafkaListenerContainerFactory() {
+ return kafkaListenerContainerFactory("longMessage");
+ }
+
@Bean
public ConcurrentKafkaListenerContainerFactory filterKafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory factory = kafkaListenerContainerFactory("filter");
diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java
index 0223bab0fe..9dff81a09d 100644
--- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java
+++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java
@@ -25,6 +25,8 @@ public class KafkaProducerConfig {
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+ configProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, "20971520");
+
return new DefaultKafkaProducerFactory<>(configProps);
}
diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java
index 00e4147cd0..8a006a72bc 100644
--- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java
+++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java
@@ -19,6 +19,9 @@ public class KafkaTopicConfig {
@Value(value = "${message.topic.name}")
private String topicName;
+ @Value(value = "${long.message.topic.name}")
+ private String longMsgTopicName;
+
@Value(value = "${partitioned.topic.name}")
private String partitionedTopicName;
@@ -54,4 +57,13 @@ public class KafkaTopicConfig {
public NewTopic topic4() {
return new NewTopic(greetingTopicName, 1, (short) 1);
}
+
+ @Bean
+ public NewTopic topic5() {
+ NewTopic newTopic = new NewTopic(longMsgTopicName, 1, (short) 1);
+ Map configs = new HashMap<>();
+ configs.put("max.message.bytes", "20971520");
+ newTopic.configs(configs);
+ return newTopic;
+ }
}
diff --git a/spring-kafka/src/main/resources/application.properties b/spring-kafka/src/main/resources/application.properties
index eaf113191e..e1a983339b 100644
--- a/spring-kafka/src/main/resources/application.properties
+++ b/spring-kafka/src/main/resources/application.properties
@@ -1,5 +1,16 @@
kafka.bootstrapAddress=localhost:9092
message.topic.name=baeldung
+long.message.topic.name=longMessage
greeting.topic.name=greeting
filtered.topic.name=filtered
-partitioned.topic.name=partitioned
\ No newline at end of file
+partitioned.topic.name=partitioned
+
+# monitoring - lag analysis
+monitor.kafka.bootstrap.config=localhost:9092
+monitor.kafka.consumer.groupid=baeldungGrp
+monitor.topic.name=baeldung
+
+# monitoring - simulation
+monitor.producer.simulate=true
+monitor.consumer.simulate=true
+monitor.kafka.consumer.groupid.simulate=baeldungGrpSimulate
\ No newline at end of file
diff --git a/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java b/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java
new file mode 100644
index 0000000000..9dcbbc4837
--- /dev/null
+++ b/spring-kafka/src/test/java/com/baeldung/monitoring/LiveLagAnalyzerServiceLiveTest.java
@@ -0,0 +1,174 @@
+package com.baeldung.monitoring;
+
+import com.baeldung.monitoring.service.LagAnalyzerService;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.clients.producer.RecordMetadata;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.test.context.EmbeddedKafka;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+
+@RunWith(SpringRunner.class)
+@DirtiesContext
+@EmbeddedKafka(partitions = 1, brokerProperties = {"listeners=PLAINTEXT://localhost:9085", "port=9085"})
+@EnableKafka
+public class LiveLagAnalyzerServiceLiveTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LiveLagAnalyzerServiceLiveTest.class);
+
+ private static KafkaConsumer consumer;
+ private static KafkaProducer producer;
+ private static LagAnalyzerService lagAnalyzerService;
+ private static final String GROUP_ID = "baeldungGrp";
+ private static final String TOPIC = "baeldung";
+ private static final int PARTITION = 0;
+ private static final TopicPartition TOPIC_PARTITION = new TopicPartition(TOPIC, PARTITION);
+ private static final String BOOTSTRAP_SERVER_CONFIG = "localhost:9085";
+ private static final int BATCH_SIZE = 100;
+ private static final long POLL_DURATION = 1000L;
+
+ @Before
+ public void setup() throws Exception {
+ initProducer();
+ initConsumer();
+ lagAnalyzerService = new LagAnalyzerService(BOOTSTRAP_SERVER_CONFIG);
+ consume();
+ }
+
+ @Test
+ public void givenEmbeddedKafkaBroker_whenAllProducedMessagesAreConsumed_thenLagBecomesZero()
+ throws ExecutionException, InterruptedException {
+ produce();
+ long consumeLag = 0L;
+ consume();
+ Map lag = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lag);
+ Assert.assertEquals(1, lag.size());
+ consumeLag = lag.get(TOPIC_PARTITION);
+ Assert.assertEquals(0L, consumeLag);
+ produce();
+ produce();
+ lag = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lag);
+ Assert.assertEquals(1, lag.size());
+ consumeLag = lag.get(TOPIC_PARTITION);
+ Assert.assertEquals(200L, consumeLag);
+
+ produce();
+ produce();
+
+ lag = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lag);
+ Assert.assertEquals(1, lag.size());
+ consumeLag = lag.get(TOPIC_PARTITION);
+ Assert.assertEquals(400L, consumeLag);
+
+ produce();
+ lag = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lag);
+ Assert.assertEquals(1, lag.size());
+ consumeLag = lag.get(TOPIC_PARTITION);
+ Assert.assertEquals(500L, consumeLag);
+
+ consume();
+ lag = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lag);
+ Assert.assertEquals(1, lag.size());
+ consumeLag = lag.get(TOPIC_PARTITION);
+ Assert.assertEquals(consumeLag, 0L);
+ }
+
+ @Test
+ public void givenEmbeddedKafkaBroker_whenMessageNotConsumed_thenLagIsEqualToProducedMessage()
+ throws ExecutionException, InterruptedException {
+ produce();
+ Map lagByTopicPartition = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lagByTopicPartition);
+ Assert.assertEquals(1, lagByTopicPartition.size());
+ long LAG = lagByTopicPartition.get(TOPIC_PARTITION);
+ Assert.assertEquals(BATCH_SIZE, LAG);
+ }
+
+ @Test
+ public void givenEmbeddedKafkaBroker_whenMessageConsumedLessThanProduced_thenLagIsNonZero()
+ throws ExecutionException, InterruptedException {
+ produce();
+ consume();
+ produce();
+ produce();
+ Map lagByTopicPartition = lagAnalyzerService.analyzeLag(GROUP_ID);
+ Assert.assertNotNull(lagByTopicPartition);
+ Assert.assertEquals(1, lagByTopicPartition.size());
+ long LAG = lagByTopicPartition.get(TOPIC_PARTITION);
+ Assert.assertEquals(2 * BATCH_SIZE, LAG);
+ }
+
+ private static void consume() {
+ try {
+ ConsumerRecords record = consumer.poll(Duration.ofMillis(POLL_DURATION));
+ consumer.commitSync();
+ Thread.sleep(POLL_DURATION);
+ } catch (Exception ex) {
+ LOGGER.error("Exception caught in consume", ex);
+ }
+ }
+
+ private static void produce() {
+ try {
+ int count = BATCH_SIZE;
+ while (count > 0) {
+ String messageKey = UUID.randomUUID().toString();
+ String messageValue = UUID.randomUUID().toString() + "_" + count;
+ ProducerRecord producerRecord = new ProducerRecord<>(TOPIC, messageKey, messageValue);
+ RecordMetadata recordMetadata = producer.send(producerRecord).get();
+ LOGGER.info("Message with key = {}, value = {} sent to partition = {}, offset = {}, topic = {}",
+ messageKey,
+ messageValue,
+ recordMetadata.partition(),
+ recordMetadata.offset(),
+ recordMetadata.topic());
+ count--;
+ }
+ } catch (Exception ex) {
+ LOGGER.error("Exception caught in produce", ex);
+ }
+ }
+
+ private static void initConsumer() {
+ Properties props = new Properties();
+ props.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVER_CONFIG);
+ props.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+ props.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+ props.setProperty(ConsumerConfig.GROUP_ID_CONFIG, GROUP_ID);
+ consumer = new KafkaConsumer<>(props);
+ consumer.assign(Arrays.asList(TOPIC_PARTITION));
+ consumer.poll(Duration.ofMillis(1L));
+ consumer.commitSync();
+ }
+
+ private static void initProducer() {
+ Map configProps = new HashMap<>();
+ configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVER_CONFIG);
+ configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+ configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+ producer = new KafkaProducer<>(configProps);
+ }
+}
diff --git a/spring-kafka/src/test/resources/application.yml b/spring-kafka/src/test/resources/application.yml
index 7d7997c6fd..8b245f08b1 100644
--- a/spring-kafka/src/test/resources/application.yml
+++ b/spring-kafka/src/test/resources/application.yml
@@ -4,4 +4,11 @@ spring:
auto-offset-reset: earliest
group-id: baeldung
test:
- topic: embedded-test-topic
\ No newline at end of file
+ topic: embedded-test-topic
+
+monitor:
+ kafka:
+ bootstrap:
+ config: "PLAINTEXT://localhost:9085"
+ consumer:
+ groupid: "baeldungGrp"
diff --git a/spring-mobile/pom.xml b/spring-mobile/pom.xml
index 1593de27a8..7f715c8735 100644
--- a/spring-mobile/pom.xml
+++ b/spring-mobile/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
spring-mobile
1.0-SNAPSHOT
diff --git a/spring-native/README.md b/spring-native/README.md
new file mode 100644
index 0000000000..0f193252d0
--- /dev/null
+++ b/spring-native/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Introduction to Spring Native](https://www.baeldung.com/spring-native-intro)
diff --git a/spring-native/pom-nativeimage.xml b/spring-native/pom-nativeimage.xml
new file mode 100644
index 0000000000..1b2cc3944a
--- /dev/null
+++ b/spring-native/pom-nativeimage.xml
@@ -0,0 +1,122 @@
+
+
+ 4.0.0
+ baeldung-spring-native
+ baeldung-spring-native
+ jar
+ Intro to Spring Native
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ spring-release
+ Spring release
+ https://repo.spring.io/release
+
+
+
+
+
+ spring-release
+ Spring release
+ https://repo.spring.io/release
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ ${spring-boot.version}
+ pom
+
+
+ org.springframework.experimental
+ spring-native
+ ${spring-native.version}
+
+
+ org.springframework.experimental
+ spring-aot
+ ${spring-native.version}
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.springframework.experimental
+ spring-aot-maven-plugin
+ ${spring-native.version}
+
+
+ test-generate
+
+ test-generate
+
+
+
+ generate
+
+ generate
+
+
+
+
+
+
+
+
+
+ native
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${native-maven-plugin.version}
+
+
+ build-native
+
+ build
+
+ package
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ exec
+
+
+
+
+
+
+
+
+ 2.5.1
+ 0.10.0
+ 0.9.0
+ 1.8
+ 1.8
+ 1.8
+
+
+
\ No newline at end of file
diff --git a/spring-native/pom.xml b/spring-native/pom.xml
new file mode 100644
index 0000000000..4455e050f2
--- /dev/null
+++ b/spring-native/pom.xml
@@ -0,0 +1,80 @@
+
+
+ 4.0.0
+ spring-native
+ spring-native
+ jar
+ Intro to Spring Native
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ spring-release
+ Spring release
+ https://repo.spring.io/release
+
+
+
+
+
+ spring-release
+ Spring release
+ https://repo.spring.io/release
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ ${spring-boot.version}
+ pom
+
+
+ org.springframework.experimental
+ spring-native
+ ${spring-native.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ ${builder}
+
+ true
+
+ IF_NOT_PRESENT
+
+
+
+
+ org.springframework.experimental
+ spring-aot-maven-plugin
+ ${spring-native.version}
+
+
+
+
+
+ paketobuildpacks/builder:tiny
+ 2.5.1
+ 0.10.0
+ 1.8
+ 1.8
+ 1.8
+
+
+
\ No newline at end of file
diff --git a/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java
new file mode 100644
index 0000000000..fa54d34f9f
--- /dev/null
+++ b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java
@@ -0,0 +1,9 @@
+package com.baeldung.springnativeintro;
+
+public class SpringNativeApp {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, World! This is a Baledung Spring Native Application");
+ }
+
+}
diff --git a/spring-roo/pom.xml b/spring-roo/pom.xml
index d1cf3e6412..8730ef7a33 100644
--- a/spring-roo/pom.xml
+++ b/spring-roo/pom.xml
@@ -2,7 +2,7 @@
-
4.0.0
com.baeldung
@@ -230,6 +230,11 @@
org.webjars.bower
ie10-viewport-bug-workaround
+
+ org.olap4j
+ olap4j
+ ${olap4j.version}
+
@@ -619,6 +624,7 @@
3.7.3
1.0.3
2.0.0.RELEASE
+ 1.2.0
-
\ No newline at end of file
+
diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 0f4ba872ba..917360dc3e 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -37,7 +37,7 @@
spring-security-web-login
spring-security-web-mvc-custom
spring-security-web-mvc
- spring-security-web-persisted-remember-me
+ spring-security-web-persistent-login
spring-security-web-react
spring-security-web-rest-basic-auth
spring-security-web-rest-custom
@@ -49,4 +49,4 @@
spring-social-login
-
\ No newline at end of file
+
diff --git a/spring-security-modules/spring-5-security/pom.xml b/spring-security-modules/spring-5-security/pom.xml
index 73b956d1e7..b487e8b282 100644
--- a/spring-security-modules/spring-5-security/pom.xml
+++ b/spring-security-modules/spring-5-security/pom.xml
@@ -55,12 +55,12 @@
org.jsoup
jsoup
- 1.13.1
+ ${jsoup.version}
commons-io
commons-io
- 2.8.0
+ ${commons-io.version}
@@ -77,4 +77,8 @@
+
+ 1.13.1
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-config/cors/pom.xml b/spring-security-modules/spring-security-config/cors/pom.xml
index 2b8efb9add..b45f1b43e6 100644
--- a/spring-security-modules/spring-security-config/cors/pom.xml
+++ b/spring-security-modules/spring-security-config/cors/pom.xml
@@ -20,7 +20,7 @@
org.springframework.boot
spring-boot-dependencies
- 2.1.2.RELEASE
+ ${spring-boot.version}
pom
import
@@ -57,4 +57,8 @@
+
+ 2.1.2.RELEASE
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-config/pom.xml b/spring-security-modules/spring-security-config/pom.xml
index 860a602aeb..8f82dcd40e 100644
--- a/spring-security-modules/spring-security-config/pom.xml
+++ b/spring-security-modules/spring-security-config/pom.xml
@@ -5,7 +5,7 @@
4.0.0
spring-security-config
0.0.1-SNAPSHOT
- spring-security-conf
+ spring-security-config
pom
diff --git a/spring-security-modules/spring-security-core/src/main/resources/application.properties b/spring-security-modules/spring-security-core/src/main/resources/application.properties
deleted file mode 100644
index 709574239b..0000000000
--- a/spring-security-modules/spring-security-core/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-legacy-oidc/pom.xml b/spring-security-modules/spring-security-legacy-oidc/pom.xml
index 148b836137..ca54a6765d 100644
--- a/spring-security-modules/spring-security-legacy-oidc/pom.xml
+++ b/spring-security-modules/spring-security-legacy-oidc/pom.xml
@@ -49,6 +49,7 @@
2.2.1.RELEASE
1.0.9.RELEASE
0.3.0
+ 2.4.7
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-angular/server/pom.xml b/spring-security-modules/spring-security-web-angular/server/pom.xml
index 2e9ff9969d..9f4f0a5caa 100644
--- a/spring-security-modules/spring-security-web-angular/server/pom.xml
+++ b/spring-security-modules/spring-security-web-angular/server/pom.xml
@@ -21,7 +21,7 @@
org.springframework.boot
spring-boot-dependencies
- 1.5.9.RELEASE
+ ${spring-boot.version}
pom
import
@@ -62,4 +62,8 @@
+
+ 1.5.9.RELEASE
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/.gitignore b/spring-security-modules/spring-security-web-persistent-login/.gitignore
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/.gitignore
rename to spring-security-modules/spring-security-web-persistent-login/.gitignore
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/README.md b/spring-security-modules/spring-security-web-persistent-login/README.md
similarity index 71%
rename from spring-security-modules/spring-security-web-persisted-remember-me/README.md
rename to spring-security-modules/spring-security-web-persistent-login/README.md
index e1208330a3..2ffffec267 100644
--- a/spring-security-modules/spring-security-web-persisted-remember-me/README.md
+++ b/spring-security-modules/spring-security-web-persistent-login/README.md
@@ -1,6 +1,6 @@
-## Spring Security Persisted Remember Me
+## Spring Security Persistent Login
-This module contains articles about 'remember me' with Spring Security
+This module contains articles about persistent login with Spring Security
### The Course
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/pom.xml b/spring-security-modules/spring-security-web-persistent-login/pom.xml
similarity index 95%
rename from spring-security-modules/spring-security-web-persisted-remember-me/pom.xml
rename to spring-security-modules/spring-security-web-persistent-login/pom.xml
index 66a2b3866b..2d931c416c 100644
--- a/spring-security-modules/spring-security-web-persisted-remember-me/pom.xml
+++ b/spring-security-modules/spring-security-web-persistent-login/pom.xml
@@ -3,16 +3,16 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- spring-security-web-persisted-remember-me
+ spring-security-web-persistent-login
0.1-SNAPSHOT
- spring-security-web-persisted-remember-me
+ spring-security-web-persistent-login
war
com.baeldung
- parent-spring-4
+ parent-spring-5
0.0.1-SNAPSHOT
- ../../parent-spring-4
+ ../../parent-spring-5
@@ -136,7 +136,7 @@
- spring-security-web-persisted-remember-me
+ spring-security-web-persistent-login
src/main/resources
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/controller/MyController.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/controller/MyController.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/controller/MyController.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/controller/MyController.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityRole.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityRole.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityRole.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityRole.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/security/SecurityWebApplicationInitializer.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/service/MyUserDetailsService.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/service/MyUserDetailsService.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/service/MyUserDetailsService.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/service/MyUserDetailsService.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/MvcConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/MvcConfig.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/MvcConfig.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/MvcConfig.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/PersistenceConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/PersistenceConfig.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/PersistenceConfig.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/PersistenceConfig.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/SecurityConfig.java b/spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/SecurityConfig.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/java/com/baeldung/spring/SecurityConfig.java
rename to spring-security-modules/spring-security-web-persistent-login/src/main/java/com/baeldung/spring/SecurityConfig.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/logback.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/logback.xml
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/logback.xml
rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/logback.xml
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persisted_logins_create_table.sql b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persisted_logins_create_table.sql
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persisted_logins_create_table.sql
rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persisted_logins_create_table.sql
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-h2.properties b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-h2.properties
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-h2.properties
rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-h2.properties
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-postgres.properties b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-postgres.properties
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/persistence-postgres.properties
rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/persistence-postgres.properties
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/webSecurityConfig.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/resources/webSecurityConfig.xml
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/resources/webSecurityConfig.xml
rename to spring-security-modules/spring-security-web-persistent-login/src/main/resources/webSecurityConfig.xml
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml
similarity index 73%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml
index 4c74be8912..1dbff70b83 100644
--- a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/mvc-servlet.xml
+++ b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/mvc-servlet.xml
@@ -1,6 +1,6 @@
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/anonymous.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/anonymous.jsp
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/anonymous.jsp
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/anonymous.jsp
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/console.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/console.jsp
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/console.jsp
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/console.jsp
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/homepage.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/homepage.jsp
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/homepage.jsp
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/homepage.jsp
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/login.jsp b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/login.jsp
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/view/login.jsp
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/view/login.jsp
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/web.xml b/spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/web.xml
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/main/webapp/WEB-INF/web.xml
rename to spring-security-modules/spring-security-web-persistent-login/src/main/webapp/WEB-INF/web.xml
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/test/java/com/baeldung/SpringContextTest.java b/spring-security-modules/spring-security-web-persistent-login/src/test/java/com/baeldung/SpringContextTest.java
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/test/java/com/baeldung/SpringContextTest.java
rename to spring-security-modules/spring-security-web-persistent-login/src/test/java/com/baeldung/SpringContextTest.java
diff --git a/spring-security-modules/spring-security-web-persisted-remember-me/src/test/resources/.gitignore b/spring-security-modules/spring-security-web-persistent-login/src/test/resources/.gitignore
similarity index 100%
rename from spring-security-modules/spring-security-web-persisted-remember-me/src/test/resources/.gitignore
rename to spring-security-modules/spring-security-web-persistent-login/src/test/resources/.gitignore
diff --git a/spring-security-modules/spring-security-web-react/pom.xml b/spring-security-modules/spring-security-web-react/pom.xml
index e8f74413ff..e24d657409 100644
--- a/spring-security-modules/spring-security-web-react/pom.xml
+++ b/spring-security-modules/spring-security-web-react/pom.xml
@@ -80,7 +80,7 @@
org.springframework.boot
spring-boot-starter-test
- 1.5.10.RELEASE
+ ${spring-boot-starter-test.version}
test
diff --git a/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json b/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json
index 6b183d2e5c..75dc571cad 100644
--- a/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json
+++ b/spring-security-modules/spring-security-web-react/src/main/webapp/WEB-INF/view/react/package-lock.json
@@ -34,9 +34,9 @@
}
},
"acorn": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
- "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
+ "version": "5.7.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
+ "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="
},
"acorn-dynamic-import": {
"version": "2.0.2",
@@ -129,11 +129,6 @@
"resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
},
- "amdefine": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
- "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
- },
"ansi-align": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
@@ -1351,15 +1346,24 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU="
},
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"bn.js": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"body-parser": {
"version": "1.19.0",
@@ -1769,51 +1773,77 @@
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
},
"chokidar": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
- "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+ "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "optional": true,
"requires": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.0",
- "braces": "^2.3.0",
- "fsevents": "^1.2.2",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.1",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "lodash.debounce": "^4.0.8",
- "normalize-path": "^2.1.1",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.0.0",
- "upath": "^1.0.5"
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
},
"dependencies": {
"anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "optional": true,
"requires": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
}
},
- "glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "optional": true
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "optional": true,
"requires": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- },
- "dependencies": {
- "is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
- "requires": {
- "is-extglob": "^2.1.0"
- }
- }
+ "fill-range": "^7.0.1"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "optional": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "optional": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "optional": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
}
},
"is-extglob": {
@@ -1822,12 +1852,41 @@
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-glob": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
- "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"requires": {
"is-extglob": "^2.1.1"
}
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "optional": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "optional": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "optional": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
}
}
},
@@ -2154,11 +2213,6 @@
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
},
- "core-js": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
- "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
- },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -2176,13 +2230,6 @@
"os-homedir": "^1.0.1",
"parse-json": "^2.2.0",
"require-from-string": "^1.1.0"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
- }
}
},
"create-ecdh": {
@@ -2710,9 +2757,9 @@
"integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0="
},
"dns-packet": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
- "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+ "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
"requires": {
"ip": "^1.1.0",
"safe-buffer": "^5.0.1"
@@ -2801,9 +2848,9 @@
}
},
"dot-prop": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
- "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
+ "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
"requires": {
"is-obj": "^1.0.0"
}
@@ -2848,17 +2895,24 @@
"integrity": "sha1-dDi3b5K0G5GfP73TUPvQdX2s3fc="
},
"elliptic": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
- "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"requires": {
- "bn.js": "^4.4.0",
- "brorand": "^1.0.1",
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
"hash.js": "^1.0.0",
- "hmac-drbg": "^1.0.0",
- "inherits": "^2.0.1",
- "minimalistic-assert": "^1.0.0",
- "minimalistic-crypto-utils": "^1.0.0"
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ }
}
},
"emoji-regex": {
@@ -2876,14 +2930,6 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
- "encoding": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
- "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
- "requires": {
- "iconv-lite": "~0.4.13"
- }
- },
"enhanced-resolve": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
@@ -3121,9 +3167,9 @@
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
},
"js-yaml": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
- "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -3570,9 +3616,9 @@
}
},
"extend": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
- "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extend-shallow": {
"version": "3.0.2",
@@ -3714,20 +3760,6 @@
"bser": "^2.0.0"
}
},
- "fbjs": {
- "version": "0.8.17",
- "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
- "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
- "requires": {
- "core-js": "^1.0.0",
- "isomorphic-fetch": "^2.1.1",
- "loose-envify": "^1.0.0",
- "object-assign": "^4.1.0",
- "promise": "^7.1.1",
- "setimmediate": "^1.0.5",
- "ua-parser-js": "^0.7.18"
- }
- },
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -3754,6 +3786,12 @@
"schema-utils": "^0.3.0"
}
},
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "optional": true
+ },
"filename-regex": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@@ -3932,465 +3970,13 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
- "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true,
"requires": {
- "nan": "^2.9.2",
- "node-pre-gyp": "^0.10.0"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.1",
- "bundled": true,
- "optional": true
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true
- },
- "aproba": {
- "version": "1.2.0",
- "bundled": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.4",
- "bundled": true,
- "optional": true,
- "requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "bundled": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "bundled": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "chownr": {
- "version": "1.0.1",
- "bundled": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "debug": {
- "version": "2.6.9",
- "bundled": true,
- "optional": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "deep-extend": {
- "version": "0.5.1",
- "bundled": true,
- "optional": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "detect-libc": {
- "version": "1.0.3",
- "bundled": true,
- "optional": true
- },
- "fs-minipass": {
- "version": "1.2.5",
- "bundled": true,
- "optional": true,
- "requires": {
- "minipass": "^2.2.1"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "optional": true,
- "requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
- }
- },
- "glob": {
- "version": "7.1.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "optional": true
- },
- "iconv-lite": {
- "version": "0.4.21",
- "bundled": true,
- "optional": true,
- "requires": {
- "safer-buffer": "^2.1.0"
- }
- },
- "ignore-walk": {
- "version": "3.0.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "optional": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "bundled": true
- },
- "ini": {
- "version": "1.3.5",
- "bundled": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "optional": true
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "bundled": true
- },
- "minipass": {
- "version": "2.2.4",
- "bundled": true,
- "requires": {
- "safe-buffer": "^5.1.1",
- "yallist": "^3.0.0"
- }
- },
- "minizlib": {
- "version": "1.1.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "minipass": "^2.2.1"
- }
- },
- "mkdirp": {
- "version": "0.5.1",
- "bundled": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.0.0",
- "bundled": true,
- "optional": true
- },
- "needle": {
- "version": "2.2.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "debug": "^2.1.2",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
- }
- },
- "node-pre-gyp": {
- "version": "0.10.0",
- "bundled": true,
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.0",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.1.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "npm-bundled": {
- "version": "1.0.3",
- "bundled": true,
- "optional": true
- },
- "npm-packlist": {
- "version": "1.1.10",
- "bundled": true,
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1"
- }
- },
- "npmlog": {
- "version": "4.1.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.5",
- "bundled": true,
- "optional": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.0"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "bundled": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.7",
- "bundled": true,
- "optional": true,
- "requires": {
- "deep-extend": "^0.5.1",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "optional": true
- }
- }
- },
- "readable-stream": {
- "version": "2.3.6",
- "bundled": true,
- "optional": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "rimraf": {
- "version": "2.6.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "glob": "^7.0.5"
- }
- },
- "safe-buffer": {
- "version": "5.1.1",
- "bundled": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "bundled": true,
- "optional": true
- },
- "sax": {
- "version": "1.2.4",
- "bundled": true,
- "optional": true
- },
- "semver": {
- "version": "5.5.0",
- "bundled": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "optional": true
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "optional": true
- },
- "tar": {
- "version": "4.4.1",
- "bundled": true,
- "optional": true,
- "requires": {
- "chownr": "^1.0.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.2.4",
- "minizlib": "^1.1.0",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.1",
- "yallist": "^3.0.2"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "optional": true
- },
- "wide-align": {
- "version": "1.1.2",
- "bundled": true,
- "optional": true,
- "requires": {
- "string-width": "^1.0.2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true
- },
- "yallist": {
- "version": "3.0.2",
- "bundled": true
- }
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
}
},
"function-bind": {
@@ -4551,60 +4137,15 @@
"integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ="
},
"handlebars": {
- "version": "4.0.11",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
- "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
"requires": {
- "async": "^1.4.0",
- "optimist": "^0.6.1",
- "source-map": "^0.4.4",
- "uglify-js": "^2.6"
- },
- "dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- },
- "source-map": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
- "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
- "requires": {
- "amdefine": ">=0.0.4"
- }
- },
- "uglify-js": {
- "version": "2.8.29",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
- "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
- "optional": true,
- "requires": {
- "source-map": "~0.5.1",
- "uglify-to-browserify": "~1.0.0",
- "yargs": "~3.10.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "optional": true
- }
- }
- },
- "yargs": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
- "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
- "optional": true,
- "requires": {
- "camelcase": "^1.0.2",
- "cliui": "^2.1.0",
- "decamelize": "^1.0.0",
- "window-size": "0.1.0"
- }
- }
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
}
},
"har-schema": {
@@ -4727,9 +4268,9 @@
}
},
"hosted-git-info": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.1.tgz",
- "integrity": "sha512-Ba4+0M4YvIDUUsprMjhVTU1yN9F2/LJSAl69ZpzaLT4l4j5mwTS6jqqW9Ojvj6lKz/veqPzpJBqGbXspOb533A=="
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
},
"hpack.js": {
"version": "2.1.6",
@@ -4872,9 +4413,9 @@
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
},
"http-proxy": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
@@ -5094,9 +4635,9 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inquirer": {
"version": "3.3.0",
@@ -5503,15 +5044,6 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
- "isomorphic-fetch": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
- "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
- "requires": {
- "node-fetch": "^1.0.1",
- "whatwg-fetch": ">=0.10.0"
- }
- },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -5657,6 +5189,48 @@
"integrity": "sha1-PdJgwpidba1nix6cxNkZRPbWAqw=",
"requires": {
"jest-cli": "^20.0.4"
+ }
+ },
+ "jest-changed-files": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz",
+ "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g="
+ },
+ "jest-cli": {
+ "version": "20.0.4",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz",
+ "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=",
+ "requires": {
+ "ansi-escapes": "^1.4.0",
+ "callsites": "^2.0.0",
+ "chalk": "^1.1.3",
+ "graceful-fs": "^4.1.11",
+ "is-ci": "^1.0.10",
+ "istanbul-api": "^1.1.1",
+ "istanbul-lib-coverage": "^1.0.1",
+ "istanbul-lib-instrument": "^1.4.2",
+ "istanbul-lib-source-maps": "^1.1.0",
+ "jest-changed-files": "^20.0.3",
+ "jest-config": "^20.0.4",
+ "jest-docblock": "^20.0.3",
+ "jest-environment-jsdom": "^20.0.3",
+ "jest-haste-map": "^20.0.4",
+ "jest-jasmine2": "^20.0.4",
+ "jest-message-util": "^20.0.3",
+ "jest-regex-util": "^20.0.3",
+ "jest-resolve-dependencies": "^20.0.3",
+ "jest-runtime": "^20.0.4",
+ "jest-snapshot": "^20.0.3",
+ "jest-util": "^20.0.3",
+ "micromatch": "^2.3.11",
+ "node-notifier": "^5.0.2",
+ "pify": "^2.3.0",
+ "slash": "^1.0.0",
+ "string-length": "^1.0.1",
+ "throat": "^3.0.0",
+ "which": "^1.2.12",
+ "worker-farm": "^1.3.1",
+ "yargs": "^7.0.2"
},
"dependencies": {
"ansi-escapes": {
@@ -5708,43 +5282,6 @@
"is-extglob": "^1.0.0"
}
},
- "jest-cli": {
- "version": "20.0.4",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz",
- "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=",
- "requires": {
- "ansi-escapes": "^1.4.0",
- "callsites": "^2.0.0",
- "chalk": "^1.1.3",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "istanbul-api": "^1.1.1",
- "istanbul-lib-coverage": "^1.0.1",
- "istanbul-lib-instrument": "^1.4.2",
- "istanbul-lib-source-maps": "^1.1.0",
- "jest-changed-files": "^20.0.3",
- "jest-config": "^20.0.4",
- "jest-docblock": "^20.0.3",
- "jest-environment-jsdom": "^20.0.3",
- "jest-haste-map": "^20.0.4",
- "jest-jasmine2": "^20.0.4",
- "jest-message-util": "^20.0.3",
- "jest-regex-util": "^20.0.3",
- "jest-resolve-dependencies": "^20.0.3",
- "jest-runtime": "^20.0.4",
- "jest-snapshot": "^20.0.3",
- "jest-util": "^20.0.3",
- "micromatch": "^2.3.11",
- "node-notifier": "^5.0.2",
- "pify": "^2.3.0",
- "slash": "^1.0.0",
- "string-length": "^1.0.1",
- "throat": "^3.0.0",
- "which": "^1.2.12",
- "worker-farm": "^1.3.1",
- "yargs": "^7.0.2"
- }
- },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -5775,11 +5312,6 @@
}
}
},
- "jest-changed-files": {
- "version": "20.0.3",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz",
- "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g="
- },
"jest-config": {
"version": "20.0.4",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.4.tgz",
@@ -6327,9 +5859,9 @@
"integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg=="
},
"kind-of": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
- "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
},
"klaw": {
"version": "1.3.1",
@@ -6387,12 +5919,12 @@
}
},
"loader-fs-cache": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz",
- "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
+ "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==",
"requires": {
"find-cache-dir": "^0.1.1",
- "mkdirp": "0.5.1"
+ "mkdirp": "^0.5.1"
},
"dependencies": {
"find-cache-dir": {
@@ -6457,9 +5989,9 @@
}
},
"lodash": {
- "version": "4.17.10",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
- "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
@@ -6476,11 +6008,6 @@
"resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
"integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU="
},
- "lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
- },
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
@@ -6656,19 +6183,12 @@
"read-pkg-up": "^1.0.1",
"redent": "^1.0.0",
"trim-newlines": "^1.0.0"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
- }
}
},
"merge": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
- "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo="
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
+ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
},
"merge-descriptors": {
"version": "1.0.1",
@@ -6751,14 +6271,14 @@
}
},
"minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mixin-deep": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
- "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"requires": {
"for-in": "^1.0.2",
"is-extendable": "^1.0.1"
@@ -6775,11 +6295,11 @@
}
},
"mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
- "minimist": "0.0.8"
+ "minimist": "^1.2.5"
}
},
"ms": {
@@ -6807,9 +6327,9 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"nan": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
- "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+ "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
"optional": true
},
"nanomatch": {
@@ -6841,9 +6361,9 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"neo-async": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz",
- "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA=="
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"next-tick": {
"version": "1.0.0",
@@ -6858,19 +6378,10 @@
"lower-case": "^1.1.1"
}
},
- "node-fetch": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
- "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
- "requires": {
- "encoding": "^0.1.11",
- "is-stream": "^1.0.1"
- }
- },
"node-forge": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
- "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ=="
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
},
"node-int64": {
"version": "0.4.0",
@@ -7127,22 +6638,6 @@
"is-wsl": "^1.1.0"
}
},
- "optimist": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
- "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
- "requires": {
- "minimist": "~0.0.1",
- "wordwrap": "~0.0.2"
- },
- "dependencies": {
- "wordwrap": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
- "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
- }
- }
- },
"optionator": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
@@ -7301,7 +6796,8 @@
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "optional": true
},
"path-exists": {
"version": "3.0.0",
@@ -7370,6 +6866,11 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
+ "picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
+ },
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -7427,11 +6928,6 @@
"ms": "^2.1.1"
}
},
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
- },
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -8646,14 +8142,6 @@
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8="
},
- "promise": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
- "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
- "requires": {
- "asap": "~2.0.3"
- }
- },
"prop-types": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
@@ -8820,30 +8308,22 @@
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
- }
}
},
"react": {
- "version": "16.4.1",
- "resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz",
- "integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==",
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
+ "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"requires": {
- "fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
- "prop-types": "^15.6.0"
+ "prop-types": "^15.6.2"
}
},
"react-dev-utils": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz",
- "integrity": "sha512-+y92rG6pmXt3cpcg/NGmG4w/W309tWNSmyyPL8hCMxuCSg2UP/hUg3npACj2UZc8UKVSXexyLrCnxowizGoAsw==",
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.3.tgz",
+ "integrity": "sha512-Mvs6ofsc2xTjeZIrMaIfbXfsPVrbdVy/cVqq6SAacnqfMlcBpDuivhWZ1ODGeJ8HgmyWTLH971PYjj/EPCDVAw==",
"requires": {
"address": "1.0.3",
"babel-code-frame": "6.26.0",
@@ -8857,29 +8337,44 @@
"inquirer": "3.3.0",
"is-root": "1.0.0",
"opn": "5.2.0",
- "react-error-overlay": "^4.0.0",
+ "react-error-overlay": "^4.0.1",
"recursive-readdir": "2.2.1",
"shell-quote": "1.6.1",
- "sockjs-client": "1.1.4",
+ "sockjs-client": "1.1.5",
"strip-ansi": "3.0.1",
"text-table": "0.2.0"
+ },
+ "dependencies": {
+ "sockjs-client": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz",
+ "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=",
+ "requires": {
+ "debug": "^2.6.6",
+ "eventsource": "0.1.6",
+ "faye-websocket": "~0.11.0",
+ "inherits": "^2.0.1",
+ "json3": "^3.3.2",
+ "url-parse": "^1.1.8"
+ }
+ }
}
},
"react-dom": {
- "version": "16.4.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.1.tgz",
- "integrity": "sha512-1Gin+wghF/7gl4Cqcvr1DxFX2Osz7ugxSwl6gBqCMpdrxHjIFUS7GYxrFftZ9Ln44FHw0JxCFD9YtZsrbR5/4A==",
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
+ "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
"requires": {
- "fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
- "prop-types": "^15.6.0"
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.19.1"
}
},
"react-error-overlay": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz",
- "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw=="
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.1.tgz",
+ "integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw=="
},
"react-scripts": {
"version": "1.1.4",
@@ -9014,14 +8509,13 @@
}
},
"readdirp": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
- "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
"requires": {
- "graceful-fs": "^4.1.2",
- "minimatch": "^3.0.2",
- "readable-stream": "^2.0.2",
- "set-immediate-shim": "^1.0.1"
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
}
},
"recursive-readdir": {
@@ -9509,11 +9003,6 @@
"requires": {
"bser": "1.0.2"
}
- },
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
}
}
},
@@ -9522,6 +9011,15 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
+ "scheduler": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
+ "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
"schema-utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
@@ -9536,11 +9034,11 @@
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo="
},
"selfsigned": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
- "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+ "version": "1.10.11",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz",
+ "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==",
"requires": {
- "node-forge": "0.9.0"
+ "node-forge": "^0.10.0"
}
},
"semver": {
@@ -9636,15 +9134,10 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
- "set-immediate-shim": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
- "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
- },
"set-value": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
- "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
"requires": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
@@ -10502,11 +9995,6 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
- "ua-parser-js": {
- "version": "0.7.18",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
- "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
- },
"uglify-js": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.2.tgz",
@@ -10568,35 +10056,14 @@
}
},
"union-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
- "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
"requires": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
"is-extendable": "^0.1.1",
- "set-value": "^0.4.3"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "set-value": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
- "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.1",
- "to-object-path": "^0.3.0"
- }
- }
+ "set-value": "^2.0.1"
}
},
"uniq": {
@@ -10669,9 +10136,10 @@
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
},
"upath": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
- "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw=="
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "optional": true
},
"update-notifier": {
"version": "2.5.0",
@@ -10716,9 +10184,9 @@
}
},
"urijs": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz",
- "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg=="
+ "version": "1.19.7",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.7.tgz",
+ "integrity": "sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA=="
},
"urix": {
"version": "0.1.0",
@@ -10752,9 +10220,9 @@
}
},
"url-parse": {
- "version": "1.4.7",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
- "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
+ "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -10855,13 +10323,98 @@
"integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw="
},
"watchpack": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
- "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+ "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
"requires": {
- "chokidar": "^2.0.2",
+ "chokidar": "^3.4.1",
"graceful-fs": "^4.1.2",
- "neo-async": "^2.5.0"
+ "neo-async": "^2.5.0",
+ "watchpack-chokidar2": "^2.0.1"
+ }
+ },
+ "watchpack-chokidar2": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
+ "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
+ "optional": true,
+ "requires": {
+ "chokidar": "^2.1.8"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "optional": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "optional": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "optional": true
+ }
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "optional": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "optional": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "optional": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ }
}
},
"wbuf": {
@@ -11291,9 +10844,9 @@
}
},
"websocket-extensions": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
- "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg=="
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
},
"whatwg-encoding": {
"version": "1.0.3",
@@ -11310,11 +10863,6 @@
}
}
},
- "whatwg-fetch": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
- "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
- },
"whatwg-url": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz",
@@ -11443,9 +10991,9 @@
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ=="
},
"yallist": {
"version": "2.1.2",
@@ -11453,9 +11001,9 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yargs": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
- "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz",
+ "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==",
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
@@ -11469,7 +11017,7 @@
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
- "yargs-parser": "^5.0.0"
+ "yargs-parser": "^5.0.1"
},
"dependencies": {
"camelcase": {
@@ -11508,11 +11056,12 @@
}
},
"yargs-parser": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
- "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz",
+ "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==",
"requires": {
- "camelcase": "^3.0.0"
+ "camelcase": "^3.0.0",
+ "object.assign": "^4.1.0"
},
"dependencies": {
"camelcase": {
diff --git a/spring-security-modules/spring-security-web-sockets/pom.xml b/spring-security-modules/spring-security-web-sockets/pom.xml
index e822b6beda..b1536e88ea 100644
--- a/spring-security-modules/spring-security-web-sockets/pom.xml
+++ b/spring-security-modules/spring-security-web-sockets/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
com.baeldung.springsecuredsockets
spring-security-web-sockets
diff --git a/spring-security-modules/spring-social-login/src/main/resources/application.properties b/spring-security-modules/spring-social-login/src/main/resources/application.properties
index 22e6acf9e2..57348051ac 100644
--- a/spring-security-modules/spring-social-login/src/main/resources/application.properties
+++ b/spring-security-modules/spring-social-login/src/main/resources/application.properties
@@ -1,4 +1,6 @@
spring.social.facebook.appId=1715784745414888
spring.social.facebook.appSecret=abefd6497e9cc01ad03be28509617bf0
spring.thymeleaf.cache=false
-server.servlet.register-default-servlet=true
\ No newline at end of file
+server.servlet.register-default-servlet=true
+spring.sql.init.mode=always
+spring.jpa.defer-datasource-initialization=true
\ No newline at end of file
diff --git a/spring-vault/pom.xml b/spring-vault/pom.xml
index 759de80a6b..68856de2fc 100644
--- a/spring-vault/pom.xml
+++ b/spring-vault/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
spring-vault
0.0.1-SNAPSHOT
diff --git a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties
index aca20f4e3c..fcdaabe007 100644
--- a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties
+++ b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties
@@ -1,5 +1,3 @@
-spring.main.allow-bean-definition-overriding=true
-
spring.mail.host=localhost
spring.mail.port=8025
diff --git a/spring-web-modules/spring-mvc-basics-4/README.md b/spring-web-modules/spring-mvc-basics-4/README.md
index 211564a363..f003f7df01 100644
--- a/spring-web-modules/spring-mvc-basics-4/README.md
+++ b/spring-web-modules/spring-mvc-basics-4/README.md
@@ -9,4 +9,5 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring
- [Spring Web Contexts](https://www.baeldung.com/spring-web-contexts)
- [Spring Optional Path variables](https://www.baeldung.com/spring-optional-path-variables)
- [JSON Parameters with Spring MVC](https://www.baeldung.com/spring-mvc-send-json-parameters)
+- [How to Set JSON Content Type In Spring MVC](https://www.baeldung.com/spring-mvc-set-json-content-type)
- More articles: [[<-- prev]](/spring-mvc-basics-3)
diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java
new file mode 100644
index 0000000000..fbf78b8a0e
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java
@@ -0,0 +1,49 @@
+package com.baeldung.controller.controller;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class GreetingsController {
+
+ @RequestMapping(
+ value = "/greetings-with-response-body",
+ method = RequestMethod.GET,
+ produces="application/json"
+ )
+ @ResponseBody
+ public String getGreetingWhileReturnTypeIsString() {
+ return "{\"test\": \"Hello using @ResponseBody\"}";
+ }
+
+ @RequestMapping(
+ value = "/greetings-with-response-entity",
+ method = RequestMethod.GET,
+ produces = "application/json"
+ )
+ public ResponseEntity getGreetingWithResponseEntity() {
+ final HttpHeaders httpHeaders= new HttpHeaders();
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ return new ResponseEntity("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK);
+ }
+ @RequestMapping(
+ value = "/greetings-with-map-return-type",
+ method = RequestMethod.GET,
+ produces = "application/json"
+ )
+ @ResponseBody
+ public Map getGreetingWhileReturnTypeIsMap() {
+ HashMap map = new HashMap();
+ map.put("test", "Hello from map");
+ return map;
+ }
+}
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties
deleted file mode 100644
index 709574239b..0000000000
--- a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java
new file mode 100644
index 0000000000..ee9a8da8d4
--- /dev/null
+++ b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.baeldung.controller.controller.GreetingsController;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.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.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration(classes = { GreetingsController.class }, loader = AnnotationConfigWebContextLoader.class)
+public class GreetingsControllerUnitTest {
+
+ private MockMvc mockMvc;
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ @Before
+ public void setUp() {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
+ }
+
+ @Test
+ public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() throws Exception {
+
+ // Given
+ String expectedMimeType = "application/json";
+
+ // Then
+ String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1)).andReturn().getResponse().getContentType();
+
+ Assert.assertEquals(expectedMimeType, actualMimeType);
+
+ }
+
+ @Test
+ public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception {
+
+ // Given
+ String expectedMimeType = "application/json";
+
+ // Then
+ String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1)).andReturn().getResponse().getContentType();
+
+ Assert.assertEquals(expectedMimeType, actualMimeType);
+ }
+}
\ No newline at end of file
diff --git a/spring-web-modules/spring-mvc-forms-jsp/pom.xml b/spring-web-modules/spring-mvc-forms-jsp/pom.xml
index 94eb51a32d..707c18fa19 100644
--- a/spring-web-modules/spring-mvc-forms-jsp/pom.xml
+++ b/spring-web-modules/spring-mvc-forms-jsp/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
0.1-SNAPSHOT
spring-mvc-forms-jsp
diff --git a/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml b/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml
index 37bcee0b8d..eb31723798 100644
--- a/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml
+++ b/spring-web-modules/spring-mvc-forms-thymeleaf/pom.xml
@@ -28,7 +28,6 @@
org.projectlombok
lombok
-
org.springframework.boot
diff --git a/spring-web-modules/spring-mvc-java/pom.xml b/spring-web-modules/spring-mvc-java/pom.xml
index b8f5ec7910..68b4c74dce 100644
--- a/spring-web-modules/spring-mvc-java/pom.xml
+++ b/spring-web-modules/spring-mvc-java/pom.xml
@@ -226,7 +226,6 @@
4.4.5
4.5.2
- 3.0.7
2.23
3.2.2
diff --git a/spring-web-modules/spring-rest-angular/pom.xml b/spring-web-modules/spring-rest-angular/pom.xml
index ef14e78198..e7cb5c8664 100644
--- a/spring-web-modules/spring-rest-angular/pom.xml
+++ b/spring-web-modules/spring-rest-angular/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
spring-rest-angular
1.0
diff --git a/spring-web-modules/spring-rest-angular/src/main/resources/application.properties b/spring-web-modules/spring-rest-angular/src/main/resources/application.properties
index cfa27938c9..d6355dd416 100644
--- a/spring-web-modules/spring-rest-angular/src/main/resources/application.properties
+++ b/spring-web-modules/spring-rest-angular/src/main/resources/application.properties
@@ -2,4 +2,6 @@ server.servlet.contextPath=/
spring.h2.console.enabled=true
logging.level.org.hibernate.SQL=info
spring.jpa.generate-ddl=true
-spring.jpa.hibernate.ddl-auto=create
\ No newline at end of file
+spring.jpa.hibernate.ddl-auto=create
+spring.sql.init.mode=always
+spring.jpa.defer-datasource-initialization=true
\ No newline at end of file
diff --git a/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties b/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties
index 4bbf3ed4fc..c432c45970 100644
--- a/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties
+++ b/spring-web-modules/spring-rest-query-language/src/main/resources/application.properties
@@ -1,2 +1,4 @@
server.port=8082
-server.servlet.context-path=/spring-rest-query-language
\ No newline at end of file
+server.servlet.context-path=/spring-rest-query-language
+spring.sql.init.mode=always
+spring.jpa.defer-datasource-initialization=true
\ No newline at end of file
diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java
index 9b01328e45..efab434e31 100644
--- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java
+++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java
@@ -1,4 +1,4 @@
-package main.java.com.baeldung.thymeleaf.articles;
+package com.baeldung.thymeleaf.articles;
public class Article {
diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java
index cfbf0fcaa6..e3972ffb51 100644
--- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java
+++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java
@@ -1,4 +1,4 @@
-package main.java.com.baeldung.thymeleaf.articles;
+package com.baeldung.thymeleaf.articles;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties
index 75770808da..730dded1b7 100644
--- a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties
+++ b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties
@@ -2,5 +2,4 @@ spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
-spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
\ No newline at end of file
diff --git a/testing-modules/junit-5/README.md b/testing-modules/junit-5/README.md
index 984b79c29d..5101f92928 100644
--- a/testing-modules/junit-5/README.md
+++ b/testing-modules/junit-5/README.md
@@ -8,3 +8,4 @@
- [Guide to Dynamic Tests in JUnit 5](https://www.baeldung.com/junit5-dynamic-tests)
- [Determine the Execution Time of JUnit Tests](https://www.baeldung.com/junit-test-execution-time)
- [@BeforeAll and @AfterAll in Non-Static Methods](https://www.baeldung.com/java-beforeall-afterall-non-static)
+- [The java.lang.NoClassDefFoundError in JUnit](https://www.baeldung.com/junit-noclassdeffounderror)
diff --git a/testing-modules/rest-assured/pom.xml b/testing-modules/rest-assured/pom.xml
index bd4c1456c1..3bb351f123 100644
--- a/testing-modules/rest-assured/pom.xml
+++ b/testing-modules/rest-assured/pom.xml
@@ -126,11 +126,6 @@
org.apache.httpcomponents
httpclient
-
- org.codehaus.groovy
- groovy-all
- ${groovy.version}
-
com.github.tomakehurst
wiremock
@@ -180,7 +175,6 @@
2.9.6
1.1
1.2
- 2.4.7
2.4.1
2.5.3
diff --git a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java
index 02138f22e3..3d19d08ba3 100644
--- a/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java
+++ b/testing-modules/rest-assured/src/test/java/com/baeldung/restassured/authentication/BasicPreemtiveAuthenticationLiveTest.java
@@ -25,7 +25,7 @@ public class BasicPreemtiveAuthenticationLiveTest {
get(SVC_URL).then()
.assertThat()
.statusCode(HttpStatus.OK.value())
- .content(containsString("
+
+
\ No newline at end of file
diff --git a/vaadin/src/main/resources/application.properties b/vaadin/src/main/resources/application.properties
deleted file mode 100644
index 1cb7086b82..0000000000
--- a/vaadin/src/main/resources/application.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Vaadin supports spring-boot 2.1 properly from V8 onwards (according to this comment https://github.com/vaadin/spring/issues/331#issuecomment-435128475)
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/vertx/pom.xml b/vertx/pom.xml
index 9a22e2dd72..7608b3e355 100644
--- a/vertx/pom.xml
+++ b/vertx/pom.xml
@@ -1,7 +1,7 @@
-
+
4.0.0
vertx
1.0-SNAPSHOT
diff --git a/wildfly/pom.xml b/wildfly/pom.xml
index c5d9bce37a..eaec4d176c 100644
--- a/wildfly/pom.xml
+++ b/wildfly/pom.xml
@@ -60,7 +60,6 @@
-
org.apache.maven.plugins
maven-war-plugin