diff --git a/aws-app-sync/README.md b/aws-app-sync/README.md
new file mode 100644
index 0000000000..976a999f40
--- /dev/null
+++ b/aws-app-sync/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [AWS AppSync With Spring Boot](https://www.baeldung.com/aws-appsync-spring)
diff --git a/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java b/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java
index 33243c4ecf..7ae2af0857 100644
--- a/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java
+++ b/core-java-modules/core-java-14/src/main/java/com/baeldung/java14/record/Person.java
@@ -4,19 +4,19 @@ import java.util.Objects;
public record Person (String name, String address) {
- public static String UNKWOWN_ADDRESS = "Unknown";
- public static String UNNAMED = "Unnamed";
+ public static String UNKNOWN_ADDRESS = "Unknown";
+ public static String UNNAMED = "Unnamed";
- public Person {
- Objects.requireNonNull(name);
- Objects.requireNonNull(address);
- }
+ public Person {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(address);
+ }
- public Person(String name) {
- this(name, UNKWOWN_ADDRESS);
- }
+ public Person(String name) {
+ this(name, UNKNOWN_ADDRESS);
+ }
- public static Person unnamed(String address) {
- return new Person(UNNAMED, address);
- }
-}
+ public static Person unnamed(String address) {
+ return new Person(UNNAMED, address);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java
index 4a7d4ede5f..9bed3dab8f 100644
--- a/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java
+++ b/core-java-modules/core-java-14/src/test/java/com/baeldung/java14/record/PersonTest.java
@@ -134,7 +134,7 @@ public class PersonTest {
Person person = new Person(name);
assertEquals(name, person.name());
- assertEquals(Person.UNKWOWN_ADDRESS, person.address());
+ assertEquals(Person.UNKNOWN_ADDRESS, person.address());
}
@Test
@@ -147,4 +147,4 @@ public class PersonTest {
assertEquals(Person.UNNAMED, person.name());
assertEquals(address, person.address());
}
-}
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml
index 00579c49b2..48474a5eef 100644
--- a/core-java-modules/core-java-8-2/pom.xml
+++ b/core-java-modules/core-java-8-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-8-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-8-datetime-2/pom.xml b/core-java-modules/core-java-8-datetime-2/pom.xml
index ce98b72781..f66a89ca55 100644
--- a/core-java-modules/core-java-8-datetime-2/pom.xml
+++ b/core-java-modules/core-java-8-datetime-2/pom.xml
@@ -4,16 +4,15 @@
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-8-datetime
+ core-java-8-datetime-2
${project.parent.version}
core-java-8-datetime
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
@@ -42,7 +41,6 @@
- core-java-datetime-java8
src/main/resources
diff --git a/core-java-modules/core-java-8-datetime/pom.xml b/core-java-modules/core-java-8-datetime/pom.xml
index ce98b72781..629ce5234d 100644
--- a/core-java-modules/core-java-8-datetime/pom.xml
+++ b/core-java-modules/core-java-8-datetime/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-8-datetime
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-8/pom.xml b/core-java-modules/core-java-8/pom.xml
index a434be028d..557f9e0dce 100644
--- a/core-java-modules/core-java-8/pom.xml
+++ b/core-java-modules/core-java-8/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-8
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-9-new-features/README.md b/core-java-modules/core-java-9-new-features/README.md
index d547b9a221..c2ef63a530 100644
--- a/core-java-modules/core-java-9-new-features/README.md
+++ b/core-java-modules/core-java-9-new-features/README.md
@@ -12,3 +12,4 @@ This module contains articles about core Java features that have been introduced
- [Introduction to Java 9 StackWalking API](https://www.baeldung.com/java-9-stackwalking-api)
- [Java 9 Platform Logging API](https://www.baeldung.com/java-9-logging-api)
- [Java 9 Reactive Streams](https://www.baeldung.com/java-9-reactive-streams)
+- [Multi-Release JAR Files with Maven](https://www.baeldung.com/maven-multi-release-jars)
diff --git a/core-java-modules/core-java-9-new-features/pom.xml b/core-java-modules/core-java-9-new-features/pom.xml
index b0fb6ab7f9..70fae73bd5 100644
--- a/core-java-modules/core-java-9-new-features/pom.xml
+++ b/core-java-modules/core-java-9-new-features/pom.xml
@@ -28,8 +28,104 @@
${junit.platform.version}
test
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
-
+
+
+ incubator-features
+
+ core-java-9-new-features
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ --add-modules=jdk.incubator.httpclient
+
+
+
+ maven-surefire-plugin
+
+ --add-modules=jdk.incubator.httpclient
+
+
+
+
+
+
+ mrjar-generation
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ compile-java-8
+
+ compile
+
+
+ 1.8
+ 1.8
+
+ ${project.basedir}/src/main/java8
+
+
+
+
+ compile-java-9
+ compile
+
+ compile
+
+
+ 9
+
+ ${project.basedir}/src/main/java9
+
+ ${project.build.outputDirectory}/META-INF/versions/9
+
+
+
+ default-testCompile
+ test-compile
+
+ testCompile
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
+
+
+ true
+
+
+ com.baeldung.multireleaseapp.App
+
+
+
+
+
+
+
+
core-java-9-new-features
@@ -56,8 +152,10 @@
3.10.0
1.2.0
+ 4.0.2
1.9
1.9
+ 3.2.0
diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java
new file mode 100644
index 0000000000..cc00223e05
--- /dev/null
+++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/App.java
@@ -0,0 +1,14 @@
+package com.baeldung.multireleaseapp;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class App {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class);
+
+ public static void main(String[] args) {
+ logger.info(String.format("Running on %s", new DefaultVersion().version()));
+ }
+
+}
diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java
new file mode 100644
index 0000000000..b24be606de
--- /dev/null
+++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/DefaultVersion.java
@@ -0,0 +1,9 @@
+package com.baeldung.multireleaseapp;
+
+public class DefaultVersion implements Version {
+
+ @Override
+ public String version() {
+ return System.getProperty("java.version");
+ }
+}
diff --git a/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java
new file mode 100644
index 0000000000..ef95f08205
--- /dev/null
+++ b/core-java-modules/core-java-9-new-features/src/main/java8/com/baeldung/multireleaseapp/Version.java
@@ -0,0 +1,5 @@
+package com.baeldung.multireleaseapp;
+
+interface Version {
+ public String version();
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java b/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java
new file mode 100644
index 0000000000..0842f578dd
--- /dev/null
+++ b/core-java-modules/core-java-9-new-features/src/main/java9/com/baeldung/multireleaseapp/DefaultVersion.java
@@ -0,0 +1,9 @@
+package com.baeldung.multireleaseapp;
+
+public class DefaultVersion implements Version {
+
+ @Override
+ public String version() {
+ return Runtime.version().toString();
+ }
+}
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java
similarity index 98%
rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java
rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java
index 5cf3b9098f..fc59ae8d8d 100644
--- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientTest.java
+++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpClientIntegrationTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertThat;
/**
* Created by adam.
*/
-public class HttpClientTest {
+public class HttpClientIntegrationTest {
@Test
public void shouldReturnSampleDataContentWhenConnectViaSystemProxy() throws IOException, InterruptedException, URISyntaxException {
@@ -55,7 +55,7 @@ public class HttpClientTest {
.send(request, HttpResponse.BodyHandler.asString());
assertThat(response.statusCode(), equalTo(HttpURLConnection.HTTP_MOVED_PERM));
- assertThat(response.body(), containsString("https://stackoverflow.com/"));
+ assertThat(response.body(), containsString(""));
}
@Test
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java
similarity index 99%
rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java
rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java
index 7c0e9a90e0..17af7bd8ba 100644
--- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestTest.java
+++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpRequestIntegrationTest.java
@@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat;
/**
* Created by adam.
*/
-public class HttpRequestTest {
+public class HttpRequestIntegrationTest {
@Test
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java
similarity index 97%
rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java
rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java
index 80295ff34c..5c6f9c8a52 100644
--- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseTest.java
+++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/httpclient/HttpResponseIntegrationTest.java
@@ -18,7 +18,7 @@ import static org.junit.Assert.assertThat;
/**
* Created by adam.
*/
-public class HttpResponseTest {
+public class HttpResponseIntegrationTest {
@Test
public void shouldReturnStatusOKWhenSendGetRequest() throws IOException, InterruptedException, URISyntaxException {
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java
similarity index 86%
rename from core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java
rename to core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java
index 647557532d..92cdc1c074 100644
--- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams.reactive/ReactiveStreamsTest.java
+++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/streams/reactive/ReactiveStreamsUnitTest.java
@@ -5,10 +5,12 @@ import org.junit.Test;
import java.util.List;
import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.TimeUnit;
-import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
-public class ReactiveStreamsTest {
+public class ReactiveStreamsUnitTest {
@Test
public void givenPublisher_whenSubscribeToIt_thenShouldConsumeAllElements() throws InterruptedException {
@@ -25,7 +27,7 @@ public class ReactiveStreamsTest {
//then
- await().atMost(1000, TimeUnit.MILLISECONDS).until(
+ await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(items)
);
}
@@ -46,7 +48,7 @@ public class ReactiveStreamsTest {
publisher.close();
//then
- await().atMost(1000, TimeUnit.MILLISECONDS).until(
+ await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expectedResult)
);
}
@@ -66,7 +68,7 @@ public class ReactiveStreamsTest {
publisher.close();
//then
- await().atMost(1000, TimeUnit.MILLISECONDS).until(
+ await().atMost(1000, TimeUnit.MILLISECONDS).untilAsserted(
() -> assertThat(subscriber.consumedElements).containsExactlyElementsOf(expected)
);
}
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java
deleted file mode 100644
index 50766502ec..0000000000
--- a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.baeldung.java9.varhandles;
-
-import org.junit.Test;
-
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class VariableHandlesTest {
-
- public int publicTestVariable = 1;
- private int privateTestVariable = 1;
- public int variableToSet = 1;
- public int variableToCompareAndSet = 1;
- public int variableToGetAndAdd = 0;
- public byte variableToBitwiseOr = 0;
-
- @Test
- public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class);
-
- assertThat(publicIntHandle.coordinateTypes().size() == 1);
- assertThat(publicIntHandle.coordinateTypes().get(0) == VariableHandles.class);
-
- }
-
- @Test
- public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
- VarHandle privateIntHandle = MethodHandles
- .privateLookupIn(VariableHandlesTest.class, MethodHandles.lookup())
- .findVarHandle(VariableHandlesTest.class, "privateTestVariable", int.class);
-
- assertThat(privateIntHandle.coordinateTypes().size() == 1);
- assertThat(privateIntHandle.coordinateTypes().get(0) == VariableHandlesTest.class);
-
- }
-
- @Test
- public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
- VarHandle arrayVarHandle = MethodHandles
- .arrayElementVarHandle(int[].class);
-
- assertThat(arrayVarHandle.coordinateTypes().size() == 2);
- assertThat(arrayVarHandle.coordinateTypes().get(0) == int[].class);
- }
-
- @Test
- public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class);
-
- assertThat((int) publicIntHandle.get(this) == 1);
- }
-
- @Test
- public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "variableToSet", int.class);
- publicIntHandle.set(this, 15);
-
- assertThat((int) publicIntHandle.get(this) == 15);
- }
-
- @Test
- public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "variableToCompareAndSet", int.class);
- publicIntHandle.compareAndSet(this, 1, 100);
-
- assertThat((int) publicIntHandle.get(this) == 100);
- }
-
- @Test
- public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "variableToGetAndAdd", int.class);
- int before = (int) publicIntHandle.getAndAdd(this, 200);
-
- assertThat(before == 0);
- assertThat((int) publicIntHandle.get(this) == 200);
- }
-
- @Test
- public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
- VarHandle publicIntHandle = MethodHandles
- .lookup()
- .in(VariableHandlesTest.class)
- .findVarHandle(VariableHandlesTest.class, "variableToBitwiseOr", byte.class);
- byte before = (byte) publicIntHandle.getAndBitwiseOr(this, (byte) 127);
-
- assertThat(before == 0);
- assertThat(variableToBitwiseOr == 127);
- }
-}
diff --git a/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java
new file mode 100644
index 0000000000..a7263f0bb2
--- /dev/null
+++ b/core-java-modules/core-java-9-new-features/src/test/java/com/baeldung/java9/varhandles/VariableHandlesUnitTest.java
@@ -0,0 +1,105 @@
+package com.baeldung.java9.varhandles;
+
+import org.junit.Test;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+import static org.junit.Assert.assertEquals;
+
+public class VariableHandlesUnitTest {
+
+ public int publicTestVariable = 1;
+ private int privateTestVariable = 1;
+ public int variableToSet = 1;
+ public int variableToCompareAndSet = 1;
+ public int variableToGetAndAdd = 0;
+ public byte variableToBitwiseOr = 0;
+
+ @Test
+ public void whenVariableHandleForPublicVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle PUBLIC_TEST_VARIABLE = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class);
+
+ assertEquals(1, PUBLIC_TEST_VARIABLE.coordinateTypes().size());
+ assertEquals(VariableHandlesUnitTest.class, PUBLIC_TEST_VARIABLE.coordinateTypes().get(0));
+ }
+
+ @Test
+ public void whenVariableHandleForPrivateVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle PRIVATE_TEST_VARIABLE = MethodHandles
+ .privateLookupIn(VariableHandlesUnitTest.class, MethodHandles.lookup())
+ .findVarHandle(VariableHandlesUnitTest.class, "privateTestVariable", int.class);
+
+ assertEquals(1, PRIVATE_TEST_VARIABLE.coordinateTypes().size());
+ assertEquals(VariableHandlesUnitTest.class, PRIVATE_TEST_VARIABLE.coordinateTypes().get(0));
+ }
+
+ @Test
+ public void whenVariableHandleForArrayVariableIsCreated_ThenItIsInitializedProperly() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle arrayVarHandle = MethodHandles
+ .arrayElementVarHandle(int[].class);
+
+ assertEquals(2, arrayVarHandle.coordinateTypes().size());
+ assertEquals(int[].class, arrayVarHandle.coordinateTypes().get(0));
+ }
+
+ @Test
+ public void givenVarHandle_whenGetIsInvoked_ThenValueOfVariableIsReturned() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle PUBLIC_TEST_VARIABLE = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "publicTestVariable", int.class);
+
+ assertEquals(1, (int) PUBLIC_TEST_VARIABLE.get(this));
+ }
+
+ @Test
+ public void givenVarHandle_whenSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle VARIABLE_TO_SET = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "variableToSet", int.class);
+
+ VARIABLE_TO_SET.set(this, 15);
+ assertEquals(15, (int) VARIABLE_TO_SET.get(this));
+ }
+
+ @Test
+ public void givenVarHandle_whenCompareAndSetIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle VARIABLE_TO_COMPARE_AND_SET = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "variableToCompareAndSet", int.class);
+
+ VARIABLE_TO_COMPARE_AND_SET.compareAndSet(this, 1, 100);
+ assertEquals(100, (int) VARIABLE_TO_COMPARE_AND_SET.get(this));
+ }
+
+ @Test
+ public void givenVarHandle_whenGetAndAddIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle VARIABLE_TO_GET_AND_ADD = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "variableToGetAndAdd", int.class);
+
+ int before = (int) VARIABLE_TO_GET_AND_ADD.getAndAdd(this, 200);
+
+ assertEquals(0, before);
+ assertEquals(200, (int) VARIABLE_TO_GET_AND_ADD.get(this));
+ }
+
+ @Test
+ public void givenVarHandle_whenGetAndBitwiseOrIsInvoked_ThenValueOfVariableIsChanged() throws NoSuchFieldException, IllegalAccessException {
+ VarHandle VARIABLE_TO_BITWISE_OR = MethodHandles
+ .lookup()
+ .in(VariableHandlesUnitTest.class)
+ .findVarHandle(VariableHandlesUnitTest.class, "variableToBitwiseOr", byte.class);
+ byte before = (byte) VARIABLE_TO_BITWISE_OR.getAndBitwiseOr(this, (byte) 127);
+
+ assertEquals(0, before);
+ assertEquals(127, (byte) VARIABLE_TO_BITWISE_OR.get(this));
+ }
+}
diff --git a/core-java-modules/core-java-9-streams/pom.xml b/core-java-modules/core-java-9-streams/pom.xml
index 7865b336a7..8c1af89b24 100644
--- a/core-java-modules/core-java-9-streams/pom.xml
+++ b/core-java-modules/core-java-9-streams/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-9-streams
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-annotations/pom.xml b/core-java-modules/core-java-annotations/pom.xml
index 8fc4c15cde..92ba4991bb 100644
--- a/core-java-modules/core-java-annotations/pom.xml
+++ b/core-java-modules/core-java-annotations/pom.xml
@@ -10,10 +10,10 @@
jar
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-arrays-convert/pom.xml b/core-java-modules/core-java-arrays-convert/pom.xml
index bd50289f47..67dc645936 100644
--- a/core-java-modules/core-java-arrays-convert/pom.xml
+++ b/core-java-modules/core-java-arrays-convert/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-guides/pom.xml b/core-java-modules/core-java-arrays-guides/pom.xml
index ef718d5117..df8639820d 100644
--- a/core-java-modules/core-java-arrays-guides/pom.xml
+++ b/core-java-modules/core-java-arrays-guides/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-multidimensional/pom.xml b/core-java-modules/core-java-arrays-multidimensional/pom.xml
index 6e49a20521..d90853678c 100644
--- a/core-java-modules/core-java-arrays-multidimensional/pom.xml
+++ b/core-java-modules/core-java-arrays-multidimensional/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-operations-advanced/pom.xml b/core-java-modules/core-java-arrays-operations-advanced/pom.xml
index 8989e91189..d73fdcee28 100644
--- a/core-java-modules/core-java-arrays-operations-advanced/pom.xml
+++ b/core-java-modules/core-java-arrays-operations-advanced/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-operations-basic/pom.xml b/core-java-modules/core-java-arrays-operations-basic/pom.xml
index 4480c14bb2..73588d662a 100644
--- a/core-java-modules/core-java-arrays-operations-basic/pom.xml
+++ b/core-java-modules/core-java-arrays-operations-basic/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-arrays-sorting/pom.xml b/core-java-modules/core-java-arrays-sorting/pom.xml
index 127d921b2a..d5e2beaac4 100644
--- a/core-java-modules/core-java-arrays-sorting/pom.xml
+++ b/core-java-modules/core-java-arrays-sorting/pom.xml
@@ -5,8 +5,8 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
- ../pom.xml
+ 0.0.1-SNAPSHOT
+ ../
4.0.0
diff --git a/core-java-modules/core-java-collections-2/pom.xml b/core-java-modules/core-java-collections-2/pom.xml
index 3a7c70b1a2..d163aabdbc 100644
--- a/core-java-modules/core-java-collections-2/pom.xml
+++ b/core-java-modules/core-java-collections-2/pom.xml
@@ -7,12 +7,11 @@
core-java-collections-2
core-java-collections-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-3/README.md b/core-java-modules/core-java-collections-3/README.md
index 9218384640..fb983d9abc 100644
--- a/core-java-modules/core-java-collections-3/README.md
+++ b/core-java-modules/core-java-collections-3/README.md
@@ -9,3 +9,4 @@
- [Differences Between Collection.clear() and Collection.removeAll()](https://www.baeldung.com/java-collection-clear-vs-removeall)
- [Performance of contains() in a HashSet vs ArrayList](https://www.baeldung.com/java-hashset-arraylist-contains-performance)
- [Fail-Safe Iterator vs Fail-Fast Iterator](https://www.baeldung.com/java-fail-safe-vs-fail-fast-iterator)
+- [Quick Guide to the Java Stack](https://www.baeldung.com/java-stack)
diff --git a/core-java-modules/core-java-collections-3/pom.xml b/core-java-modules/core-java-collections-3/pom.xml
index 1e1695c8bc..bd991bfefa 100644
--- a/core-java-modules/core-java-collections-3/pom.xml
+++ b/core-java-modules/core-java-collections-3/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/stack/StackUnitTest.java b/core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/stack/StackUnitTest.java
similarity index 100%
rename from core-java-modules/core-java/src/test/java/com/baeldung/stack/StackUnitTest.java
rename to core-java-modules/core-java-collections-3/src/test/java/com/baeldung/collections/stack/StackUnitTest.java
diff --git a/core-java-modules/core-java-collections-array-list/pom.xml b/core-java-modules/core-java-collections-array-list/pom.xml
index 74a6513cac..81ee4eff55 100644
--- a/core-java-modules/core-java-collections-array-list/pom.xml
+++ b/core-java-modules/core-java-collections-array-list/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-array-list
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list-2/pom.xml b/core-java-modules/core-java-collections-list-2/pom.xml
index 3184da1294..230787d14d 100644
--- a/core-java-modules/core-java-collections-list-2/pom.xml
+++ b/core-java-modules/core-java-collections-list-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list-3/pom.xml b/core-java-modules/core-java-collections-list-3/pom.xml
index 090e756ac6..373190a130 100644
--- a/core-java-modules/core-java-collections-list-3/pom.xml
+++ b/core-java-modules/core-java-collections-list-3/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-list/pom.xml b/core-java-modules/core-java-collections-list/pom.xml
index e6dce5a0db..509f58ea61 100644
--- a/core-java-modules/core-java-collections-list/pom.xml
+++ b/core-java-modules/core-java-collections-list/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-list
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps-2/pom.xml b/core-java-modules/core-java-collections-maps-2/pom.xml
index a08a4ac072..a64a11c6ea 100644
--- a/core-java-modules/core-java-collections-maps-2/pom.xml
+++ b/core-java-modules/core-java-collections-maps-2/pom.xml
@@ -7,12 +7,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps-3/pom.xml b/core-java-modules/core-java-collections-maps-3/pom.xml
index 95414c12c2..f547968b22 100644
--- a/core-java-modules/core-java-collections-maps-3/pom.xml
+++ b/core-java-modules/core-java-collections-maps-3/pom.xml
@@ -7,12 +7,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-maps/pom.xml b/core-java-modules/core-java-collections-maps/pom.xml
index c0dd705c1c..742e264504 100644
--- a/core-java-modules/core-java-collections-maps/pom.xml
+++ b/core-java-modules/core-java-collections-maps/pom.xml
@@ -6,12 +6,11 @@
0.1.0-SNAPSHOT
core-java-collections-maps
jar
-
-
- com.baeldung
- parent-java
+
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections-set/pom.xml b/core-java-modules/core-java-collections-set/pom.xml
index c89ba0c091..7c55f2ff2a 100644
--- a/core-java-modules/core-java-collections-set/pom.xml
+++ b/core-java-modules/core-java-collections-set/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections-set
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-collections/pom.xml b/core-java-modules/core-java-collections/pom.xml
index 515d19d7fb..e1219c4713 100644
--- a/core-java-modules/core-java-collections/pom.xml
+++ b/core-java-modules/core-java-collections/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-collections
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-2/pom.xml b/core-java-modules/core-java-concurrency-2/pom.xml
index dfb5674c8e..75fd3890b3 100644
--- a/core-java-modules/core-java-concurrency-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-2/pom.xml b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
index 8752e7b7db..2f374bffac 100644
--- a/core-java-modules/core-java-concurrency-advanced-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-3/pom.xml b/core-java-modules/core-java-concurrency-advanced-3/pom.xml
index cf81214125..32267fb800 100644
--- a/core-java-modules/core-java-concurrency-advanced-3/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced-3/pom.xml
@@ -9,12 +9,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced-3
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java
new file mode 100644
index 0000000000..ee1bdcd55b
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/abaproblem/Account.java
@@ -0,0 +1,66 @@
+package com.baeldung.abaproblem;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Account {
+
+ private AtomicInteger balance;
+ private AtomicInteger transactionCount;
+ private ThreadLocal currentThreadCASFailureCount;
+
+ public Account() {
+ this.balance = new AtomicInteger(0);
+ this.transactionCount = new AtomicInteger(0);
+ this.currentThreadCASFailureCount = new ThreadLocal<>();
+ this.currentThreadCASFailureCount.set(0);
+ }
+
+ public int getBalance() {
+ return balance.get();
+ }
+
+ public int getTransactionCount() {
+ return transactionCount.get();
+ }
+
+ public int getCurrentThreadCASFailureCount() {
+ return currentThreadCASFailureCount.get();
+ }
+
+ public boolean withdraw(int amount) {
+ int current = getBalance();
+ maybeWait();
+ boolean result = balance.compareAndSet(current, current - amount);
+ if (result) {
+ transactionCount.incrementAndGet();
+ } else {
+ int currentCASFailureCount = currentThreadCASFailureCount.get();
+ currentThreadCASFailureCount.set(currentCASFailureCount + 1);
+ }
+ return result;
+ }
+
+ private void maybeWait() {
+ if ("thread1".equals(Thread.currentThread().getName())) {
+ try {
+ TimeUnit.SECONDS.sleep(2);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public boolean deposit(int amount) {
+ int current = balance.get();
+ boolean result = balance.compareAndSet(current, current + amount);
+ if (result) {
+ transactionCount.incrementAndGet();
+ } else {
+ int currentCASFailureCount = currentThreadCASFailureCount.get();
+ currentThreadCASFailureCount.set(currentCASFailureCount + 1);
+ }
+ return result;
+ }
+
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java
index 1a46e1ba52..415b24738a 100644
--- a/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java
+++ b/core-java-modules/core-java-concurrency-advanced-3/src/main/java/com/baeldung/atomicstampedreference/StampedAccount.java
@@ -9,13 +9,11 @@ public class StampedAccount {
private AtomicStampedReference account = new AtomicStampedReference<>(0, 0);
public int getBalance() {
- return this.account.get(new int[1]);
+ return account.getReference();
}
public int getStamp() {
- int[] stamps = new int[1];
- this.account.get(stamps);
- return stamps[0];
+ return account.getStamp();
}
public boolean deposit(int funds) {
diff --git a/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java
new file mode 100644
index 0000000000..aa5f0f7997
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-3/src/test/java/com/baeldung/abaproblem/AccountUnitTest.java
@@ -0,0 +1,98 @@
+package com.baeldung.abaproblem;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AccountUnitTest {
+
+ private Account account;
+
+ @BeforeEach
+ public void setUp() {
+ account = new Account();
+ }
+
+ @Test
+ public void zeroBalanceInitializationTest() {
+ assertEquals(0, account.getBalance());
+ assertEquals(0, account.getTransactionCount());
+ assertEquals(0, account.getCurrentThreadCASFailureCount());
+ }
+
+ @Test
+ public void depositTest() {
+ final int moneyToDeposit = 50;
+
+ assertTrue(account.deposit(moneyToDeposit));
+
+ assertEquals(moneyToDeposit, account.getBalance());
+ }
+
+ @Test
+ public void withdrawTest() throws InterruptedException {
+ final int defaultBalance = 50;
+ final int moneyToWithdraw = 20;
+
+ account.deposit(defaultBalance);
+
+ assertTrue(account.withdraw(moneyToWithdraw));
+
+ assertEquals(defaultBalance - moneyToWithdraw, account.getBalance());
+ }
+
+ @Test
+ public void abaProblemTest() throws InterruptedException {
+ final int defaultBalance = 50;
+
+ final int amountToWithdrawByThread1 = 20;
+ final int amountToWithdrawByThread2 = 10;
+ final int amountToDepositByThread2 = 10;
+
+ assertEquals(0, account.getTransactionCount());
+ assertEquals(0, account.getCurrentThreadCASFailureCount());
+ account.deposit(defaultBalance);
+ assertEquals(1, account.getTransactionCount());
+
+ Thread thread1 = new Thread(() -> {
+
+ // this will take longer due to the name of the thread
+ assertTrue(account.withdraw(amountToWithdrawByThread1));
+
+ // thread 1 fails to capture ABA problem
+ assertNotEquals(1, account.getCurrentThreadCASFailureCount());
+
+ }, "thread1");
+
+ Thread thread2 = new Thread(() -> {
+
+ assertTrue(account.deposit(amountToDepositByThread2));
+ assertEquals(defaultBalance + amountToDepositByThread2, account.getBalance());
+
+ // this will be fast due to the name of the thread
+ assertTrue(account.withdraw(amountToWithdrawByThread2));
+
+ // thread 1 didn't finish yet, so the original value will be in place for it
+ assertEquals(defaultBalance, account.getBalance());
+
+ assertEquals(0, account.getCurrentThreadCASFailureCount());
+ }, "thread2");
+
+ thread1.start();
+ thread2.start();
+ thread1.join();
+ thread2.join();
+
+ // compareAndSet operation succeeds for thread 1
+ assertEquals(defaultBalance - amountToWithdrawByThread1, account.getBalance());
+
+ //but there are other transactions
+ assertNotEquals(2, account.getTransactionCount());
+
+ // thread 2 did two modifications as well
+ assertEquals(4, account.getTransactionCount());
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced/pom.xml b/core-java-modules/core-java-concurrency-advanced/pom.xml
index d39712468f..67db486121 100644
--- a/core-java-modules/core-java-concurrency-advanced/pom.xml
+++ b/core-java-modules/core-java-concurrency-advanced/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-advanced
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-basic-2/pom.xml b/core-java-modules/core-java-concurrency-basic-2/pom.xml
index 8c9bbef54c..adc4fd33e3 100644
--- a/core-java-modules/core-java-concurrency-basic-2/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-basic/pom.xml b/core-java-modules/core-java-concurrency-basic/pom.xml
index c15200da1f..29d393805b 100644
--- a/core-java-modules/core-java-concurrency-basic/pom.xml
+++ b/core-java-modules/core-java-concurrency-basic/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-basic
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-concurrency-collections/pom.xml b/core-java-modules/core-java-concurrency-collections/pom.xml
index 5c038639a7..31f5a0fca8 100644
--- a/core-java-modules/core-java-concurrency-collections/pom.xml
+++ b/core-java-modules/core-java-concurrency-collections/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-concurrency-collections
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-console/README.md b/core-java-modules/core-java-console/README.md
new file mode 100644
index 0000000000..725e2482bb
--- /dev/null
+++ b/core-java-modules/core-java-console/README.md
@@ -0,0 +1,5 @@
+#Core Java Console
+
+[Read and Write User Input in Java](http://www.baeldung.com/java-console-input-output)
+[Formatting with printf() in Java](https://www.baeldung.com/java-printstream-printf)
+[ASCII Art in Java](http://www.baeldung.com/ascii-art-in-java)
\ No newline at end of file
diff --git a/core-java-modules/core-java-console/pom.xml b/core-java-modules/core-java-console/pom.xml
new file mode 100644
index 0000000000..1d58d8c253
--- /dev/null
+++ b/core-java-modules/core-java-console/pom.xml
@@ -0,0 +1,142 @@
+
+
+ 4.0.0
+ core-java-console
+ 0.1.0-SNAPSHOT
+ core-java-console
+ jar
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
+
+
+
+ core-java-console
+
+
+ src/main/resources
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ prepare-package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/libs
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+ java
+ com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed
+
+ -Xmx300m
+ -XX:+UseParallelGC
+ -classpath
+
+ com.baeldung.outofmemoryerror.OutOfMemoryGCLimitExceed
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+ ${source.version}
+ ${target.version}
+
+
+
+
+
+
+
+ integration
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ **/*ManualTest.java
+
+
+ **/*IntegrationTest.java
+ **/*IntTest.java
+
+
+
+
+
+
+ json
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+ run-benchmarks
+
+ none
+
+ exec
+
+
+ test
+ java
+
+ -classpath
+
+ org.openjdk.jmh.Main
+ .*
+
+
+
+
+
+
+
+
+
+
+
+ 3.0.0-M1
+ 1.6.0
+ 1.8
+ 1.8
+
+
+
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/asciiart/AsciiArt.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/asciiart/AsciiArt.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/asciiart/AsciiArt.java
rename to core-java-modules/core-java-console/src/main/java/com/baeldung/asciiart/AsciiArt.java
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleConsoleClass.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleConsoleClass.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleConsoleClass.java
rename to core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleConsoleClass.java
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleScannerClass.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleScannerClass.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/console/ConsoleScannerClass.java
rename to core-java-modules/core-java-console/src/main/java/com/baeldung/console/ConsoleScannerClass.java
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/printf/PrintfExamples.java b/core-java-modules/core-java-console/src/main/java/com/baeldung/printf/PrintfExamples.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/printf/PrintfExamples.java
rename to core-java-modules/core-java-console/src/main/java/com/baeldung/printf/PrintfExamples.java
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java b/core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java
similarity index 94%
rename from core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java
rename to core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java
index 8ab1695395..a8c42fbeb1 100644
--- a/core-java-modules/core-java/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java
+++ b/core-java-modules/core-java-console/src/test/java/com/baeldung/asciiart/AsciiArtIntegrationTest.java
@@ -1,10 +1,9 @@
package com.baeldung.asciiart;
-import java.awt.Font;
-
+import com.baeldung.asciiart.AsciiArt.Settings;
import org.junit.Test;
-import com.baeldung.asciiart.AsciiArt.Settings;
+import java.awt.*;
public class AsciiArtIntegrationTest {
@@ -16,5 +15,4 @@ public class AsciiArtIntegrationTest {
asciiArt.drawString(text, "*", settings);
}
-
}
diff --git a/core-java-modules/core-java-date-operations-1/pom.xml b/core-java-modules/core-java-date-operations-1/pom.xml
index 54cbc79678..e12e4aa4ee 100644
--- a/core-java-modules/core-java-date-operations-1/pom.xml
+++ b/core-java-modules/core-java-date-operations-1/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-date-operations-1
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-date-operations-2/README.md b/core-java-modules/core-java-date-operations-2/README.md
index 19c7b98d30..6dc1302d99 100644
--- a/core-java-modules/core-java-date-operations-2/README.md
+++ b/core-java-modules/core-java-date-operations-2/README.md
@@ -8,4 +8,5 @@ This module contains articles about date operations in Java.
- [Converting Java Date to OffsetDateTime](https://www.baeldung.com/java-convert-date-to-offsetdatetime)
- [How to Set the JVM Time Zone](https://www.baeldung.com/java-jvm-time-zone)
- [How to determine day of week by passing specific date in Java?](https://www.baeldung.com/java-get-day-of-week)
+- [Finding Leap Years in Java](https://www.baeldung.com/java-leap-year)
- [[<-- Prev]](/core-java-modules/core-java-date-operations-1)
diff --git a/core-java-modules/core-java-date-operations-2/pom.xml b/core-java-modules/core-java-date-operations-2/pom.xml
index ea5f852b0d..5861a9ab98 100644
--- a/core-java-modules/core-java-date-operations-2/pom.xml
+++ b/core-java-modules/core-java-date-operations-2/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-date-operations-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java b/core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java
similarity index 100%
rename from core-java-modules/core-java/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java
rename to core-java-modules/core-java-date-operations-2/src/test/java/com/baeldung/leapyear/LeapYearUnitTest.java
diff --git a/core-java-modules/core-java-datetime-conversion/pom.xml b/core-java-modules/core-java-datetime-conversion/pom.xml
index e2dd579335..79d1394576 100644
--- a/core-java-modules/core-java-datetime-conversion/pom.xml
+++ b/core-java-modules/core-java-datetime-conversion/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-datetime-conversion
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-datetime-string/pom.xml b/core-java-modules/core-java-datetime-string/pom.xml
index ceb7641320..c1181f670a 100644
--- a/core-java-modules/core-java-datetime-string/pom.xml
+++ b/core-java-modules/core-java-datetime-string/pom.xml
@@ -8,12 +8,11 @@
${project.parent.version}
core-java-datetime-string
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-exceptions-2/pom.xml b/core-java-modules/core-java-exceptions-2/pom.xml
index 915ec1da69..a53bf37b77 100644
--- a/core-java-modules/core-java-exceptions-2/pom.xml
+++ b/core-java-modules/core-java-exceptions-2/pom.xml
@@ -7,12 +7,11 @@
core-java-exceptions-2
core-java-exceptions-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-exceptions/pom.xml b/core-java-modules/core-java-exceptions/pom.xml
index 0778b6b5a3..b708aff6b9 100644
--- a/core-java-modules/core-java-exceptions/pom.xml
+++ b/core-java-modules/core-java-exceptions/pom.xml
@@ -9,12 +9,11 @@
0.1.0-SNAPSHOT
core-java-exceptions
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-function/pom.xml b/core-java-modules/core-java-function/pom.xml
index 1a853d5580..0eb34bed7b 100644
--- a/core-java-modules/core-java-function/pom.xml
+++ b/core-java-modules/core-java-function/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-function
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-2/pom.xml b/core-java-modules/core-java-io-2/pom.xml
index ec27c76435..bdc2ee37f5 100644
--- a/core-java-modules/core-java-io-2/pom.xml
+++ b/core-java-modules/core-java-io-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-apis/pom.xml b/core-java-modules/core-java-io-apis/pom.xml
index 9628027309..9350e4b527 100644
--- a/core-java-modules/core-java-io-apis/pom.xml
+++ b/core-java-modules/core-java-io-apis/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-apis
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io-conversions-2/README.md b/core-java-modules/core-java-io-conversions-2/README.md
index 5cb9c21c54..9ce36e7437 100644
--- a/core-java-modules/core-java-io-conversions-2/README.md
+++ b/core-java-modules/core-java-io-conversions-2/README.md
@@ -5,4 +5,5 @@ This module contains articles about core Java input/output(IO) conversions.
### Relevant Articles:
- [Java InputStream to String](https://www.baeldung.com/convert-input-stream-to-string)
- [Java – Write an InputStream to a File](https://www.baeldung.com/convert-input-stream-to-a-file)
+- [Converting a BufferedReader to a JSONObject](https://www.baeldung.com/java-bufferedreader-to-jsonobject)
- More articles: [[<-- prev]](/core-java-modules/core-java-io-conversions)
diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml
index e95d1f4b67..46bce7988b 100644
--- a/core-java-modules/core-java-io-conversions-2/pom.xml
+++ b/core-java-modules/core-java-io-conversions-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-conversions-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
@@ -22,6 +21,11 @@
commons-lang3
${commons-lang3.version}
+
+ org.json
+ json
+ 20200518
+
diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java
new file mode 100644
index 0000000000..80007e9c2f
--- /dev/null
+++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/bufferedreadertojsonobject/JavaBufferedReaderToJSONObjectUnitTest.java
@@ -0,0 +1,48 @@
+package com.baeldung.bufferedreadertojsonobject;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.Test;
+
+public class JavaBufferedReaderToJSONObjectUnitTest {
+
+ @Test
+ public void givenValidJson_whenUsingBufferedReader_thenJSONTokenerConverts() {
+ byte[] b = "{ \"name\" : \"John\", \"age\" : 18 }".getBytes(StandardCharsets.UTF_8);
+ InputStream is = new ByteArrayInputStream(b);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+ JSONTokener tokener = new JSONTokener(bufferedReader);
+ JSONObject json = new JSONObject(tokener);
+
+ assertNotNull(json);
+ assertEquals("John", json.get("name"));
+ assertEquals(18, json.get("age"));
+ }
+
+ @Test
+ public void givenValidJson_whenUsingString_thenJSONObjectConverts() throws IOException {
+ byte[] b = "{ \"name\" : \"John\", \"age\" : 18 }".getBytes(StandardCharsets.UTF_8);
+ InputStream is = new ByteArrayInputStream(b);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ sb.append(line);
+ }
+ JSONObject json = new JSONObject(sb.toString());
+
+ assertNotNull(json);
+ assertEquals("John", json.get("name"));
+ assertEquals(18, json.get("age"));
+ }
+}
diff --git a/core-java-modules/core-java-io-conversions/pom.xml b/core-java-modules/core-java-io-conversions/pom.xml
index f5ccaa45a3..0012b02d7e 100644
--- a/core-java-modules/core-java-io-conversions/pom.xml
+++ b/core-java-modules/core-java-io-conversions/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io-conversions
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-io/pom.xml b/core-java-modules/core-java-io/pom.xml
index 103a809f90..ccfb57e909 100644
--- a/core-java-modules/core-java-io/pom.xml
+++ b/core-java-modules/core-java-io/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-io
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-jar/pom.xml b/core-java-modules/core-java-jar/pom.xml
index 1d87bcda5f..6e9d713d7c 100644
--- a/core-java-modules/core-java-jar/pom.xml
+++ b/core-java-modules/core-java-jar/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-jar
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-jndi/pom.xml b/core-java-modules/core-java-jndi/pom.xml
index 4a491a1a47..030a5f5d50 100644
--- a/core-java-modules/core-java-jndi/pom.xml
+++ b/core-java-modules/core-java-jndi/pom.xml
@@ -12,7 +12,7 @@
com.baeldung.core-java-modules
core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/core-java-modules/core-java-jpms/pom.xml b/core-java-modules/core-java-jpms/pom.xml
index 4610baab49..5809c0f579 100644
--- a/core-java-modules/core-java-jpms/pom.xml
+++ b/core-java-modules/core-java-jpms/pom.xml
@@ -12,7 +12,7 @@
com.baeldung.core-java-modules
core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/core-java-modules/core-java-jvm/README.md b/core-java-modules/core-java-jvm/README.md
index 2f80ea7372..0dae790ec0 100644
--- a/core-java-modules/core-java-jvm/README.md
+++ b/core-java-modules/core-java-jvm/README.md
@@ -12,3 +12,4 @@ This module contains articles about working with the Java Virtual Machine (JVM).
- [Guide to System.gc()](https://www.baeldung.com/java-system-gc)
- [Runtime.getRuntime().halt() vs System.exit() in Java](https://www.baeldung.com/java-runtime-halt-vs-system-exit)
- [Adding Shutdown Hooks for JVM Applications](https://www.baeldung.com/jvm-shutdown-hooks)
+- [How to Get the Size of an Object in Java](http://www.baeldung.com/java-size-of-object)
diff --git a/core-java-modules/core-java-jvm/pom.xml b/core-java-modules/core-java-jvm/pom.xml
index edf7a4f3c5..f3e5470a61 100644
--- a/core-java-modules/core-java-jvm/pom.xml
+++ b/core-java-modules/core-java-jvm/pom.xml
@@ -10,10 +10,10 @@
jar
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
- ../../
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java
rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationAgent.java
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationExample.java b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationExample.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/InstrumentationExample.java
rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/InstrumentationExample.java
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/objectsize/MANIFEST.MF b/core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/MANIFEST.MF
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/objectsize/MANIFEST.MF
rename to core-java-modules/core-java-jvm/src/main/java/com/baeldung/objectsize/MANIFEST.MF
diff --git a/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java
new file mode 100644
index 0000000000..47bb668727
--- /dev/null
+++ b/core-java-modules/core-java-jvm/src/test/java/com/baeldung/error/oom/ExecutorServiceUnitTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.error.oom;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+import org.junit.jupiter.api.Test;
+
+public class ExecutorServiceUnitTest {
+
+ @Test
+ public void givenAnExecutorService_WhenMoreTasksSubmitted_ThenAdditionalTasksWait() {
+
+ // Given
+ int noOfThreads = 5;
+ ExecutorService executorService = Executors.newFixedThreadPool(noOfThreads);
+
+ Runnable runnableTask = () -> {
+ try {
+ TimeUnit.HOURS.sleep(1);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ };
+
+ // When
+ IntStream.rangeClosed(1, 10)
+ .forEach(i -> executorService.submit(runnableTask));
+
+ // Then
+ assertThat(((ThreadPoolExecutor) executorService).getQueue()
+ .size(), is(equalTo(5)));
+ }
+}
diff --git a/core-java-modules/core-java-lambdas/pom.xml b/core-java-modules/core-java-lambdas/pom.xml
index 421ca2f394..318b04fcf5 100644
--- a/core-java-modules/core-java-lambdas/pom.xml
+++ b/core-java-modules/core-java-lambdas/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-lambdas
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-2/pom.xml b/core-java-modules/core-java-lang-2/pom.xml
index 21a63d8091..5f2d4ec901 100644
--- a/core-java-modules/core-java-lang-2/pom.xml
+++ b/core-java-modules/core-java-lang-2/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-lang-2
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java
new file mode 100644
index 0000000000..8d7c626521
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/main/java/com/baeldung/inttoenum/PizzaStatus.java
@@ -0,0 +1,36 @@
+package com.baeldung.inttoenum;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum PizzaStatus {
+ ORDERED(5),
+ READY(2),
+ DELIVERED(0);
+
+ private int timeToDelivery;
+
+ PizzaStatus(int timeToDelivery) {
+ this.timeToDelivery = timeToDelivery;
+ }
+
+ public int getTimeToDelivery() {
+ return timeToDelivery;
+ }
+
+ private static Map timeToDeliveryToEnumValuesMapping = new HashMap<>();
+
+ static {
+ PizzaStatus[] pizzaStatuses = PizzaStatus.values();
+ for (int pizzaStatusIndex = 0; pizzaStatusIndex < pizzaStatuses.length; pizzaStatusIndex++) {
+ timeToDeliveryToEnumValuesMapping.put(
+ pizzaStatuses[pizzaStatusIndex].getTimeToDelivery(),
+ pizzaStatuses[pizzaStatusIndex]
+ );
+ }
+ }
+
+ public static PizzaStatus castIntToEnum(int timeToDelivery) {
+ return timeToDeliveryToEnumValuesMapping.get(timeToDelivery);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java
new file mode 100644
index 0000000000..876c230827
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/inttoenum/IntToEnumUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.inttoenum;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class IntToEnumUnitTest {
+
+ @Test
+ public void whenIntToEnumUsingValuesMethod_thenReturnEnumObject() {
+ int timeToDeliveryForOrderedPizzaStatus = 5;
+ PizzaStatus[] pizzaStatuses = PizzaStatus.values();
+ PizzaStatus pizzaOrderedStatus = null;
+ for (int pizzaStatusIndex = 0; pizzaStatusIndex < pizzaStatuses.length; pizzaStatusIndex++) {
+ if (pizzaStatuses[pizzaStatusIndex].getTimeToDelivery() == timeToDeliveryForOrderedPizzaStatus) {
+ pizzaOrderedStatus = pizzaStatuses[pizzaStatusIndex];
+ }
+ }
+ assertEquals(pizzaOrderedStatus, PizzaStatus.ORDERED);
+ }
+
+ @Test
+ public void whenIntToEnumUsingMap_thenReturnEnumObject() {
+ int timeToDeliveryForOrderedPizzaStatus = 5;
+ assertEquals(PizzaStatus.castIntToEnum(timeToDeliveryForOrderedPizzaStatus), PizzaStatus.ORDERED);
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-math-2/pom.xml b/core-java-modules/core-java-lang-math-2/pom.xml
index 92ebcc6a94..e2cced4fbf 100644
--- a/core-java-modules/core-java-lang-math-2/pom.xml
+++ b/core-java-modules/core-java-lang-math-2/pom.xml
@@ -5,12 +5,11 @@
core-java-lang-math-2
0.0.1-SNAPSHOT
core-java-lang-math-2
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-math/pom.xml b/core-java-modules/core-java-lang-math/pom.xml
index bcb5cf39d2..81ff0d43ea 100644
--- a/core-java-modules/core-java-lang-math/pom.xml
+++ b/core-java-modules/core-java-lang-math/pom.xml
@@ -8,12 +8,11 @@
0.1.0-SNAPSHOT
core-java-lang-math
jar
-
- com.baeldung
- parent-java
+ com.baeldung.core-java-modules
+ core-java-modules
0.0.1-SNAPSHOT
- ../../parent-java
+ ../
diff --git a/core-java-modules/core-java-lang-oop-constructors/pom.xml b/core-java-modules/core-java-lang-oop-constructors/pom.xml
index 76507103ea..e54286a822 100644
--- a/core-java-modules/core-java-lang-oop-constructors/pom.xml
+++ b/core-java-modules/core-java-lang-oop-constructors/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-lang-oop-generics/pom.xml b/core-java-modules/core-java-lang-oop-generics/pom.xml
index ae141ecda2..65a0aeac59 100644
--- a/core-java-modules/core-java-lang-oop-generics/pom.xml
+++ b/core-java-modules/core-java-lang-oop-generics/pom.xml
@@ -5,7 +5,7 @@
core-java-modules
com.baeldung.core-java-modules
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
4.0.0
diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java
new file mode 100644
index 0000000000..2021f42239
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/supertype/TypeReference.java
@@ -0,0 +1,18 @@
+package com.baeldung.supertype;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public abstract class TypeReference {
+
+ private final Type type;
+
+ public TypeReference() {
+ Type superclass = getClass().getGenericSuperclass();
+ type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ }
+
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java
new file mode 100644
index 0000000000..24e3b698e2
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/supertype/TypeReferenceUnitTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.supertype;
+
+import org.junit.Test;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TypeReferenceUnitTest {
+
+ @Test
+ public void givenGenericToken_whenUsingSuperTypeToken_thenPreservesTheTypeInfo() {
+ TypeReference
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ 0
+
+
+
- 3.8.1
+ UTF-8
+
9
9
- UTF-8
- 3.12.2
+
+ 3.8.1
+ 2.22.2
+
1.0
+
+ 5.6.2
+ 3.12.2
diff --git a/ddd-modules/sharedkernel/pom.xml b/ddd-modules/sharedkernel/pom.xml
index a61f03a494..1afddf1e22 100644
--- a/ddd-modules/sharedkernel/pom.xml
+++ b/ddd-modules/sharedkernel/pom.xml
@@ -11,8 +11,9 @@
com.baeldung.dddmodules
- dddmodules
+ ddd-modules
1.0
+ ../
diff --git a/ddd-modules/shippingcontext/pom.xml b/ddd-modules/shippingcontext/pom.xml
index 2096923f90..25b5882ef1 100644
--- a/ddd-modules/shippingcontext/pom.xml
+++ b/ddd-modules/shippingcontext/pom.xml
@@ -11,8 +11,9 @@
com.baeldung.dddmodules
- dddmodules
+ ddd-modules
1.0
+ ../
diff --git a/ddd/pom.xml b/ddd/pom.xml
index 1253f2ac48..422f9ccd15 100644
--- a/ddd/pom.xml
+++ b/ddd/pom.xml
@@ -17,6 +17,35 @@
../parent-boot-2
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+
+
+
+
+
+ org.junit
+ junit-bom
+ ${junit-jupiter.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
org.springframework.boot
@@ -26,24 +55,6 @@
org.springframework.boot
spring-boot-starter-data-cassandra
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
- org.junit.platform
- junit-platform-launcher
- ${junit-platform.version}
- test
-
org.joda
joda-money
@@ -95,7 +106,10 @@
- 1.0.1
-
+ 2.22.2
+ 1.0.1
+
+ 5.6.2
+
diff --git a/jee-kotlin/pom.xml b/jee-kotlin/pom.xml
index 9191885bd4..45d5d8ece1 100644
--- a/jee-kotlin/pom.xml
+++ b/jee-kotlin/pom.xml
@@ -221,6 +221,7 @@
org.jboss.logmanager.LogManager
${project.basedir}/target/wildfly-${wildfly.version}
+ 8756
${project.basedir}/target/wildfly-${wildfly.version}/modules
false
@@ -278,9 +279,10 @@
2.0.1.Final
1.0.0.Alpha4
+ 1.1.7
+
3.8.0.Final
3.1.3
-
diff --git a/jee-kotlin/src/main/resources/META-INF/persistence.xml b/jee-kotlin/src/main/resources/META-INF/persistence.xml
index daac86868b..0093792810 100644
--- a/jee-kotlin/src/main/resources/META-INF/persistence.xml
+++ b/jee-kotlin/src/main/resources/META-INF/persistence.xml
@@ -7,7 +7,7 @@
java:jboss/datasources/ExampleDS
- com.enpy.entity.Student
+ com.baeldung.jeekotlin.entity.Student
diff --git a/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java
index d5c9e577a6..fd6e04dadc 100644
--- a/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java
+++ b/jhipster/jhipster-uaa/gateway/src/main/java/com/baeldung/jhipster/gateway/web/rest/errors/ExceptionTranslator.java
@@ -14,6 +14,7 @@ import org.zalando.problem.Problem;
import org.zalando.problem.ProblemBuilder;
import org.zalando.problem.Status;
import org.zalando.problem.spring.web.advice.ProblemHandling;
+import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
import org.zalando.problem.violations.ConstraintViolationProblem;
import javax.annotation.Nonnull;
@@ -28,7 +29,7 @@ import java.util.stream.Collectors;
* The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807)
*/
@ControllerAdvice
-public class ExceptionTranslator implements ProblemHandling {
+public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
/**
* Post-process the Problem payload to add the message key for the front-end if needed
diff --git a/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java
index 18baa42736..3bf4995405 100644
--- a/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java
+++ b/jhipster/jhipster-uaa/quotes/src/main/java/com/baeldung/jhipster/quotes/web/rest/errors/ExceptionTranslator.java
@@ -14,6 +14,7 @@ import org.zalando.problem.Problem;
import org.zalando.problem.ProblemBuilder;
import org.zalando.problem.Status;
import org.zalando.problem.spring.web.advice.ProblemHandling;
+import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
import org.zalando.problem.violations.ConstraintViolationProblem;
import javax.annotation.Nonnull;
@@ -28,7 +29,7 @@ import java.util.stream.Collectors;
* The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807)
*/
@ControllerAdvice
-public class ExceptionTranslator implements ProblemHandling {
+public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
/**
* Post-process the Problem payload to add the message key for the front-end if needed
diff --git a/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java
index 320636c51d..6af9fd126c 100644
--- a/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java
+++ b/jhipster/jhipster-uaa/uaa/src/main/java/com/baeldung/jhipster/uaa/web/rest/errors/ExceptionTranslator.java
@@ -14,6 +14,7 @@ import org.zalando.problem.Problem;
import org.zalando.problem.ProblemBuilder;
import org.zalando.problem.Status;
import org.zalando.problem.spring.web.advice.ProblemHandling;
+import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
import org.zalando.problem.violations.ConstraintViolationProblem;
import javax.annotation.Nonnull;
@@ -28,7 +29,7 @@ import java.util.stream.Collectors;
* The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807)
*/
@ControllerAdvice
-public class ExceptionTranslator implements ProblemHandling {
+public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
/**
* Post-process the Problem payload to add the message key for the front-end if needed
diff --git a/jmh/src/main/java/com/baeldung/BenchMark.java b/jmh/src/main/java/com/baeldung/BenchMark.java
index b0e1caf4dc..3c5c840db2 100644
--- a/jmh/src/main/java/com/baeldung/BenchMark.java
+++ b/jmh/src/main/java/com/baeldung/BenchMark.java
@@ -1,14 +1,20 @@
package com.baeldung;
-import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
import java.nio.charset.Charset;
+import java.util.concurrent.TimeUnit;
public class BenchMark {
+ @State(Scope.Benchmark)
+ public static class Log {
+ public int x = 8;
+ }
+
@State(Scope.Benchmark)
public static class ExecutionPlan {
@@ -45,4 +51,44 @@ public class BenchMark {
// Do nothing
}
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.AverageTime)
+ public void doNothing() {
+
+ }
+
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.AverageTime)
+ public void objectCreation() {
+ new Object();
+ }
+
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.AverageTime)
+ public Object pillarsOfCreation() {
+ return new Object();
+ }
+
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.AverageTime)
+ public void blackHole(Blackhole blackhole) {
+ blackhole.consume(new Object());
+ }
+
+ @Benchmark
+ public double foldedLog() {
+ int x = 8;
+
+ return Math.log(x);
+ }
+
+ @Benchmark
+ public double log(Log input) {
+ return Math.log(input.x);
+ }
+
}
diff --git a/core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt b/kotlin-libraries-2/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt
similarity index 100%
rename from core-kotlin-modules/core-kotlin/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt
rename to kotlin-libraries-2/src/test/kotlin/com/baeldung/gson/GsonUnitTest.kt
diff --git a/language-interop/README.md b/language-interop/README.md
index a28c4a5405..f4f2fc0816 100644
--- a/language-interop/README.md
+++ b/language-interop/README.md
@@ -3,3 +3,6 @@
This module contains articles about Java interop with other language integrations.
### Relevant Articles:
+
+- [How to Call Python From Java](https://www.baeldung.com/java-working-with-python)
+- [Introduction to Nashorn](http://www.baeldung.com/java-nashorn)
diff --git a/core-java-modules/core-java/src/main/resources/js/bind.js b/language-interop/src/main/resources/js/bind.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/bind.js
rename to language-interop/src/main/resources/js/bind.js
diff --git a/core-java-modules/core-java/src/main/resources/js/locations.js b/language-interop/src/main/resources/js/locations.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/locations.js
rename to language-interop/src/main/resources/js/locations.js
diff --git a/core-java-modules/core-java/src/main/resources/js/math_module.js b/language-interop/src/main/resources/js/math_module.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/math_module.js
rename to language-interop/src/main/resources/js/math_module.js
diff --git a/core-java-modules/core-java/src/main/resources/js/no_such.js b/language-interop/src/main/resources/js/no_such.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/no_such.js
rename to language-interop/src/main/resources/js/no_such.js
diff --git a/core-java-modules/core-java/src/main/resources/js/script.js b/language-interop/src/main/resources/js/script.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/script.js
rename to language-interop/src/main/resources/js/script.js
diff --git a/core-java-modules/core-java/src/main/resources/js/trim.js b/language-interop/src/main/resources/js/trim.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/trim.js
rename to language-interop/src/main/resources/js/trim.js
diff --git a/core-java-modules/core-java/src/main/resources/js/typed_arrays.js b/language-interop/src/main/resources/js/typed_arrays.js
similarity index 100%
rename from core-java-modules/core-java/src/main/resources/js/typed_arrays.js
rename to language-interop/src/main/resources/js/typed_arrays.js
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java b/language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java
similarity index 94%
rename from core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java
rename to language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java
index 9abe8a927c..a9e4243f9d 100644
--- a/core-java-modules/core-java/src/test/java/com/baeldung/scripting/NashornUnitTest.java
+++ b/language-interop/src/test/java/com/baeldung/language/interop/javascript/NashornUnitTest.java
@@ -1,14 +1,10 @@
-package com.baeldung.scripting;
+package com.baeldung.language.interop.javascript;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import javax.script.Bindings;
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
+import javax.script.*;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
diff --git a/libraries-data-2/pom.xml b/libraries-data-2/pom.xml
index cbb24edd3f..2d27ec2107 100644
--- a/libraries-data-2/pom.xml
+++ b/libraries-data-2/pom.xml
@@ -121,6 +121,11 @@
univocity-parsers
${univocity.version}
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+
org.awaitility
awaitility
@@ -148,6 +153,13 @@
renjin-script-engine
${renjin.version}
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+ test
+ test
+
@@ -184,6 +196,7 @@
RELEASE
3.0
1.8.1
+ 2.5.0
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java
new file mode 100644
index 0000000000..8c1351642f
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulation.java
@@ -0,0 +1,28 @@
+package com.baeldung.kafka.consumer;
+
+class CountryPopulation {
+
+ private String country;
+ private Integer population;
+
+ public CountryPopulation(String country, Integer population) {
+ this.country = country;
+ this.population = population;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Integer getPopulation() {
+ return population;
+ }
+
+ public void setPopulation(Integer population) {
+ this.population = population;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java
new file mode 100644
index 0000000000..ba4dfe6f3b
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/consumer/CountryPopulationConsumer.java
@@ -0,0 +1,60 @@
+package com.baeldung.kafka.consumer;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.stream.StreamSupport;
+
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.errors.WakeupException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CountryPopulationConsumer {
+
+ private static Logger logger = LoggerFactory.getLogger(CountryPopulationConsumer.class);
+
+ private Consumer consumer;
+ private java.util.function.Consumer exceptionConsumer;
+ private java.util.function.Consumer countryPopulationConsumer;
+
+ public CountryPopulationConsumer(
+ Consumer consumer, java.util.function.Consumer exceptionConsumer,
+ java.util.function.Consumer countryPopulationConsumer) {
+ this.consumer = consumer;
+ this.exceptionConsumer = exceptionConsumer;
+ this.countryPopulationConsumer = countryPopulationConsumer;
+ }
+
+ void startBySubscribing(String topic) {
+ consume(() -> consumer.subscribe(Collections.singleton(topic)));
+ }
+
+ void startByAssigning(String topic, int partition) {
+ consume(() -> consumer.assign(Collections.singleton(new TopicPartition(topic, partition))));
+ }
+
+ private void consume(Runnable beforePollingTask) {
+ try {
+ beforePollingTask.run();
+ while (true) {
+ ConsumerRecords records = consumer.poll(Duration.ofMillis(1000));
+ StreamSupport.stream(records.spliterator(), false)
+ .map(record -> new CountryPopulation(record.key(), record.value()))
+ .forEach(countryPopulationConsumer);
+ consumer.commitSync();
+ }
+ } catch (WakeupException e) {
+ logger.info("Shutting down...");
+ } catch (RuntimeException ex) {
+ exceptionConsumer.accept(ex);
+ } finally {
+ consumer.close();
+ }
+ }
+
+ public void stop() {
+ consumer.wakeup();
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java
new file mode 100644
index 0000000000..1c77226037
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/EvenOddPartitioner.java
@@ -0,0 +1,17 @@
+package com.baeldung.kafka.producer;
+
+import org.apache.kafka.clients.producer.internals.DefaultPartitioner;
+import org.apache.kafka.common.Cluster;
+
+public class EvenOddPartitioner extends DefaultPartitioner {
+
+ @Override
+ public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
+
+ if (((String) key).length() % 2 == 0) {
+ return 0;
+ }
+
+ return 1;
+ }
+}
diff --git a/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java
new file mode 100644
index 0000000000..911c9ed3d7
--- /dev/null
+++ b/libraries-data-2/src/main/java/com/baeldung/kafka/producer/KafkaProducer.java
@@ -0,0 +1,40 @@
+package com.baeldung.kafka.producer;
+
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.clients.producer.RecordMetadata;
+
+import java.util.concurrent.Future;
+
+public class KafkaProducer {
+
+ private final Producer producer;
+
+ public KafkaProducer(Producer producer) {
+ this.producer = producer;
+ }
+
+ public Future send(String key, String value) {
+ ProducerRecord record = new ProducerRecord("topic_sports_news",
+ key, value);
+ return producer.send(record);
+ }
+
+ public void flush() {
+ producer.flush();
+ }
+
+ public void beginTransaction() {
+ producer.beginTransaction();
+ }
+
+ public void initTransaction() {
+ producer.initTransactions();
+ }
+
+ public void commitTransaction() {
+ producer.commitTransaction();
+ }
+
+
+}
diff --git a/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java b/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java
new file mode 100644
index 0000000000..1b49c71716
--- /dev/null
+++ b/libraries-data-2/src/test/java/com/baeldung/kafka/consumer/CountryPopulationConsumerUnitTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.kafka.consumer;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.consumer.MockConsumer;
+import org.apache.kafka.clients.consumer.OffsetResetStrategy;
+import org.apache.kafka.common.KafkaException;
+import org.apache.kafka.common.TopicPartition;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CountryPopulationConsumerUnitTest {
+
+ private static final String TOPIC = "topic";
+ private static final int PARTITION = 0;
+
+ private CountryPopulationConsumer countryPopulationConsumer;
+
+ private List updates;
+ private Throwable pollException;
+
+ private MockConsumer consumer;
+
+ @BeforeEach
+ void setUp() {
+ consumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST);
+ updates = new ArrayList<>();
+ countryPopulationConsumer = new CountryPopulationConsumer(consumer, ex -> this.pollException = ex, updates::add);
+ }
+
+ @Test
+ void whenStartingByAssigningTopicPartition_thenExpectUpdatesAreConsumedCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> consumer.addRecord(record(TOPIC, PARTITION, "Romania", 19_410_000)));
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, PARTITION);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startByAssigning(TOPIC, PARTITION);
+
+ // THEN
+ assertThat(updates).hasSize(1);
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ @Test
+ void whenStartingBySubscribingToTopic_thenExpectUpdatesAreConsumedCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> {
+ consumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC, 0)));
+ consumer.addRecord(record(TOPIC, PARTITION, "Romania", 19_410_000));
+ });
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, PARTITION);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startBySubscribing(TOPIC);
+
+ // THEN
+ assertThat(updates).hasSize(1);
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ @Test
+ void whenStartingBySubscribingToTopicAndExceptionOccurs_thenExpectExceptionIsHandledCorrectly() {
+ // GIVEN
+ consumer.schedulePollTask(() -> consumer.setPollException(new KafkaException("poll exception")));
+ consumer.schedulePollTask(() -> countryPopulationConsumer.stop());
+
+ HashMap startOffsets = new HashMap<>();
+ TopicPartition tp = new TopicPartition(TOPIC, 0);
+ startOffsets.put(tp, 0L);
+ consumer.updateBeginningOffsets(startOffsets);
+
+ // WHEN
+ countryPopulationConsumer.startBySubscribing(TOPIC);
+
+ // THEN
+ assertThat(pollException).isInstanceOf(KafkaException.class).hasMessage("poll exception");
+ assertThat(consumer.closed()).isTrue();
+ }
+
+ private ConsumerRecord record(String topic, int partition, String country, int population) {
+ return new ConsumerRecord<>(topic, partition, 0, country, population);
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java b/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java
new file mode 100644
index 0000000000..a7156ed886
--- /dev/null
+++ b/libraries-data-2/src/test/java/com/baeldung/kafka/producer/KafkaProducerUnitTest.java
@@ -0,0 +1,116 @@
+package com.baeldung.kafka.producer;
+
+import com.baeldung.kafka.producer.EvenOddPartitioner;
+import com.baeldung.kafka.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.MockProducer;
+import org.apache.kafka.clients.producer.RecordMetadata;
+import org.apache.kafka.common.Cluster;
+import org.apache.kafka.common.Node;
+import org.apache.kafka.common.PartitionInfo;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import static java.util.Collections.emptySet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class KafkaProducerUnitTest {
+
+ private final String TOPIC_NAME = "topic_sports_news";
+
+ private KafkaProducer kafkaProducer;
+ private MockProducer mockProducer;
+
+ private void buildMockProducer(boolean autoComplete) {
+ this.mockProducer = new MockProducer<>(autoComplete, new StringSerializer(), new StringSerializer());
+ }
+
+ @Test
+ void givenKeyValue_whenSend_thenVerifyHistory() throws ExecutionException, InterruptedException {
+
+ buildMockProducer(true);
+ //when
+ kafkaProducer = new KafkaProducer(mockProducer);
+ Future recordMetadataFuture = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}");
+
+ //then
+ assertTrue(mockProducer.history().size() == 1);
+ assertTrue(mockProducer.history().get(0).key().equalsIgnoreCase("data"));
+ assertTrue(recordMetadataFuture.get().partition() == 0);
+
+ }
+
+ @Test
+ void givenKeyValue_whenSend_thenSendOnlyAfterFlush() {
+
+ buildMockProducer(false);
+ //when
+ kafkaProducer = new KafkaProducer(mockProducer);
+ Future record = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}");
+ assertFalse(record.isDone());
+
+ //then
+ kafkaProducer.flush();
+ assertTrue(record.isDone());
+ }
+
+ @Test
+ void givenKeyValue_whenSend_thenReturnException() {
+
+ buildMockProducer(false);
+ //when
+ kafkaProducer = new KafkaProducer(mockProducer);
+ Future record = kafkaProducer.send("site", "{\"site\" : \"baeldung\"}");
+ RuntimeException e = new RuntimeException();
+ mockProducer.errorNext(e);
+ //then
+ try {
+ record.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ assertEquals(e, ex.getCause());
+ }
+ assertTrue(record.isDone());
+ }
+
+ @Test
+ void givenKeyValue_whenSendWithTxn_thenSendOnlyOnTxnCommit() {
+
+ buildMockProducer(true);
+ //when
+ kafkaProducer = new KafkaProducer(mockProducer);
+ kafkaProducer.initTransaction();
+ kafkaProducer.beginTransaction();
+ Future record = kafkaProducer.send("data", "{\"site\" : \"baeldung\"}");
+
+ //then
+ assertTrue(mockProducer.history().isEmpty());
+ kafkaProducer.commitTransaction();
+ assertTrue(mockProducer.history().size() == 1);
+ }
+
+ @Test
+ void givenKeyValue_whenSendWithPartitioning_thenVerifyPartitionNumber() throws ExecutionException, InterruptedException {
+
+ PartitionInfo partitionInfo0 = new PartitionInfo(TOPIC_NAME, 0, null, null, null);
+ PartitionInfo partitionInfo1 = new PartitionInfo(TOPIC_NAME, 1, null, null, null);
+ List list = new ArrayList<>();
+ list.add(partitionInfo0);
+ list.add(partitionInfo1);
+ Cluster cluster = new Cluster("kafkab", new ArrayList(), list, emptySet(), emptySet());
+ this.mockProducer = new MockProducer<>(cluster, true, new EvenOddPartitioner(), new StringSerializer(), new StringSerializer());
+ //when
+ kafkaProducer = new KafkaProducer(mockProducer);
+ Future recordMetadataFuture = kafkaProducer.send("partition", "{\"site\" : \"baeldung\"}");
+
+ //then
+ assertTrue(recordMetadataFuture.get().partition() == 1);
+
+ }
+
+}
\ No newline at end of file
diff --git a/libraries-data-db/pom.xml b/libraries-data-db/pom.xml
index f028ffe8c3..d51580ccbc 100644
--- a/libraries-data-db/pom.xml
+++ b/libraries-data-db/pom.xml
@@ -211,7 +211,7 @@
5.0.2
5.0.4
3.2.0-m7
- 2.7.2
+ 3.4.5
11.22.4
diff --git a/libraries-rpc/README.md b/libraries-rpc/README.md
new file mode 100644
index 0000000000..472aa883ad
--- /dev/null
+++ b/libraries-rpc/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Introduction to Finagle](https://www.baeldung.com/java-finagle)
diff --git a/libraries-server-2/.gitignore b/libraries-server-2/.gitignore
new file mode 100644
index 0000000000..e594daf27a
--- /dev/null
+++ b/libraries-server-2/.gitignore
@@ -0,0 +1,9 @@
+*.class
+
+# Folders #
+/gensrc
+/target
+
+# Packaged files #
+*.jar
+/bin/
diff --git a/libraries-server-2/README.md b/libraries-server-2/README.md
new file mode 100644
index 0000000000..38166bcd77
--- /dev/null
+++ b/libraries-server-2/README.md
@@ -0,0 +1,8 @@
+## Server
+
+This module contains articles about server libraries.
+
+### Relevant Articles:
+
+- [HTTP/2 in Jetty](https://www.baeldung.com/jetty-http-2)
+- More articles: [[<-- prev]](../libraries-server)
diff --git a/libraries-server-2/pom.xml b/libraries-server-2/pom.xml
new file mode 100644
index 0000000000..5f500a7ced
--- /dev/null
+++ b/libraries-server-2/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+ libraries-server-2
+ 0.0.1-SNAPSHOT
+ libraries-server-2
+ war
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.eclipse.jetty
+ jetty-server
+ ${jetty.version}
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${jetty.version}
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ ${jetty.version}
+
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-maven-plugin
+ ${jetty.version}
+
+ 8888
+ quit
+
+ -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar
+
+ ${basedir}/src/main/config/jetty.xml
+
+ /
+
+
+
+
+ org.eclipse.jetty.http2
+ http2-server
+ ${jetty.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-openjdk8-server
+ ${jetty.version}
+
+
+ org.eclipse.jetty
+ jetty-servlets
+ ${jetty.version}
+
+
+
+
+
+
+
+ 9.4.27.v20200227
+ 8.1.11.v20170118
+
+
+
\ No newline at end of file
diff --git a/libraries-server/src/main/config/jetty.xml b/libraries-server-2/src/main/config/jetty.xml
similarity index 100%
rename from libraries-server/src/main/config/jetty.xml
rename to libraries-server-2/src/main/config/jetty.xml
diff --git a/libraries-server/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java b/libraries-server-2/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java
similarity index 100%
rename from libraries-server/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java
rename to libraries-server-2/src/main/java/com/baeldung/jetty/http2/Http2JettyServlet.java
diff --git a/libraries-server/src/main/resources/keystore.jks b/libraries-server-2/src/main/resources/keystore.jks
similarity index 100%
rename from libraries-server/src/main/resources/keystore.jks
rename to libraries-server-2/src/main/resources/keystore.jks
diff --git a/libraries-server-2/src/main/resources/logback.xml b/libraries-server-2/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/libraries-server-2/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries-server/src/main/resources/truststore.jks b/libraries-server-2/src/main/resources/truststore.jks
similarity index 100%
rename from libraries-server/src/main/resources/truststore.jks
rename to libraries-server-2/src/main/resources/truststore.jks
diff --git a/libraries-server/src/main/webapp/WEB-INF/web.xml b/libraries-server-2/src/main/webapp/WEB-INF/web.xml
similarity index 100%
rename from libraries-server/src/main/webapp/WEB-INF/web.xml
rename to libraries-server-2/src/main/webapp/WEB-INF/web.xml
diff --git a/libraries-server/src/main/webapp/http2.html b/libraries-server-2/src/main/webapp/http2.html
similarity index 100%
rename from libraries-server/src/main/webapp/http2.html
rename to libraries-server-2/src/main/webapp/http2.html
diff --git a/libraries-server/src/main/webapp/images/homepage-latest_articles.jpg b/libraries-server-2/src/main/webapp/images/homepage-latest_articles.jpg
similarity index 100%
rename from libraries-server/src/main/webapp/images/homepage-latest_articles.jpg
rename to libraries-server-2/src/main/webapp/images/homepage-latest_articles.jpg
diff --git a/libraries-server/src/main/webapp/images/homepage-rest_with_spring.jpg b/libraries-server-2/src/main/webapp/images/homepage-rest_with_spring.jpg
similarity index 100%
rename from libraries-server/src/main/webapp/images/homepage-rest_with_spring.jpg
rename to libraries-server-2/src/main/webapp/images/homepage-rest_with_spring.jpg
diff --git a/libraries-server/src/main/webapp/images/homepage-weekly_reviews.jpg b/libraries-server-2/src/main/webapp/images/homepage-weekly_reviews.jpg
similarity index 100%
rename from libraries-server/src/main/webapp/images/homepage-weekly_reviews.jpg
rename to libraries-server-2/src/main/webapp/images/homepage-weekly_reviews.jpg
diff --git a/libraries-server/src/main/webapp/index.html b/libraries-server-2/src/main/webapp/index.html
similarity index 100%
rename from libraries-server/src/main/webapp/index.html
rename to libraries-server-2/src/main/webapp/index.html
diff --git a/libraries-server/README.md b/libraries-server/README.md
index 7e41f33a0c..570806611f 100644
--- a/libraries-server/README.md
+++ b/libraries-server/README.md
@@ -13,4 +13,4 @@ This module contains articles about server libraries.
- [MQTT Client in Java](https://www.baeldung.com/java-mqtt-client)
- [Guide to XMPP Smack Client](https://www.baeldung.com/xmpp-smack-chat-client)
- [A Guide to NanoHTTPD](https://www.baeldung.com/nanohttpd)
-- [HTTP/2 in Jetty](https://www.baeldung.com/jetty-http-2)
+- More articles: [[more -->]](../libraries-server-2)
\ No newline at end of file
diff --git a/libraries-server/pom.xml b/libraries-server/pom.xml
index eb9cb61e56..d9546f1678 100644
--- a/libraries-server/pom.xml
+++ b/libraries-server/pom.xml
@@ -5,7 +5,6 @@
libraries-server
0.0.1-SNAPSHOT
libraries-server
- war
com.baeldung
@@ -107,50 +106,11 @@
-
-
-
- org.eclipse.jetty
- jetty-maven-plugin
- ${jetty.version}
-
- 8888
- quit
-
- -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar
-
- ${basedir}/src/main/config/jetty.xml
-
- /
-
-
-
-
- org.eclipse.jetty.http2
- http2-server
- ${jetty.version}
-
-
- org.eclipse.jetty
- jetty-alpn-openjdk8-server
- ${jetty.version}
-
-
- org.eclipse.jetty
- jetty-servlets
- ${jetty.version}
-
-
-
-
-
-
3.6.2
4.5.3
9.4.27.v20200227
4.1.20.Final
- 8.1.11.v20170118
8.5.24
4.3.1
1.2.0
diff --git a/maven-all/versions-maven-plugin/original/pom.xml b/maven-all/versions-maven-plugin/original/pom.xml
index 54140aec9b..f81596661e 100644
--- a/maven-all/versions-maven-plugin/original/pom.xml
+++ b/maven-all/versions-maven-plugin/original/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
- versions-maven-plugin-example
+ original
0.0.1-SNAPSHOT
diff --git a/netflix-modules/genie/README.md b/netflix-modules/genie/README.md
new file mode 100644
index 0000000000..f6e15ba403
--- /dev/null
+++ b/netflix-modules/genie/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Introduction to Netflix Genie](https://www.baeldung.com/netflix-genie-intro)
diff --git a/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java b/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java
new file mode 100644
index 0000000000..038f559329
--- /dev/null
+++ b/netty/src/main/java/com/baeldung/http/server/CustomHttpServerHandler.java
@@ -0,0 +1,116 @@
+package com.baeldung.http.server;
+
+import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
+import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+import java.util.Set;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpContent;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaderValues;
+import io.netty.handler.codec.http.HttpObject;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpUtil;
+import io.netty.handler.codec.http.LastHttpContent;
+import io.netty.handler.codec.http.cookie.Cookie;
+import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
+import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
+import io.netty.util.CharsetUtil;
+
+public class CustomHttpServerHandler extends SimpleChannelInboundHandler
diff --git a/persistence-modules/spring-data-elasticsearch/pom.xml b/persistence-modules/spring-data-elasticsearch/pom.xml
index 3446528323..6a983145ee 100644
--- a/persistence-modules/spring-data-elasticsearch/pom.xml
+++ b/persistence-modules/spring-data-elasticsearch/pom.xml
@@ -15,13 +15,7 @@
org.springframework
- spring-core
- ${spring.version}
-
-
-
- org.springframework
- spring-context
+ spring-web
${spring.version}
@@ -36,6 +30,7 @@
elasticsearch
${elasticsearch.version}
+
com.alibaba
fastjson
@@ -49,8 +44,8 @@
- com.vividsolutions
- jts
+ org.locationtech.jts
+ jts-core
${jts.version}
@@ -60,41 +55,19 @@
-
- org.apache.logging.log4j
- log4j-core
- ${log4j.version}
-
-
-
- org.elasticsearch.client
- transport
- ${elasticsearch.version}
-
-
org.springframework
spring-test
${spring.version}
test
-
-
- net.java.dev.jna
- jna
- ${jna.version}
- test
-
- 3.0.8.RELEASE
- 4.5.2
- 5.6.0
+ 4.0.0.RELEASE
+ 7.6.2
1.2.47
- 0.6
- 1.13
- 2.9.1
+ 0.7
+ 1.15.0
-
\ No newline at end of file
diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java
index e6ce795b45..51bbe73e9e 100644
--- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java
+++ b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java
@@ -1,19 +1,13 @@
package com.baeldung.spring.data.es.config;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.elasticsearch.client.Client;
-import org.elasticsearch.client.transport.TransportClient;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.transport.InetSocketTransportAddress;
-import org.elasticsearch.transport.client.PreBuiltTransportClient;
-import org.springframework.beans.factory.annotation.Value;
+import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.client.ClientConfiguration;
+import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
-import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
@Configuration
@@ -21,30 +15,18 @@ import org.springframework.data.elasticsearch.repository.config.EnableElasticsea
@ComponentScan(basePackages = { "com.baeldung.spring.data.es.service" })
public class Config {
- @Value("${elasticsearch.home:/usr/local/Cellar/elasticsearch/5.6.0}")
- private String elasticsearchHome;
-
- @Value("${elasticsearch.cluster.name:elasticsearch}")
- private String clusterName;
-
@Bean
- public Client client() {
- TransportClient client = null;
- try {
- final Settings elasticsearchSettings = Settings.builder()
- .put("client.transport.sniff", true)
- .put("path.home", elasticsearchHome)
- .put("cluster.name", clusterName).build();
- client = new PreBuiltTransportClient(elasticsearchSettings);
- client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
- } catch (UnknownHostException e) {
- e.printStackTrace();
- }
- return client;
+ RestHighLevelClient client() {
+ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
+ .connectedTo("localhost:9200")
+ .build();
+
+ return RestClients.create(clientConfiguration)
+ .rest();
}
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
- return new ElasticsearchTemplate(client());
+ return new ElasticsearchRestTemplate(client());
}
}
diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java
index 38f50e1614..1d596cd92b 100644
--- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java
+++ b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/model/Author.java
@@ -1,7 +1,12 @@
package com.baeldung.spring.data.es.model;
+import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
+
+import org.springframework.data.elasticsearch.annotations.Field;
+
public class Author {
+ @Field(type = Text)
private String name;
public Author() {
diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java
deleted file mode 100644
index a0f72aa5f7..0000000000
--- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleService.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.baeldung.spring.data.es.service;
-
-import java.util.Optional;
-
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-
-import com.baeldung.spring.data.es.model.Article;
-
-public interface ArticleService {
- Article save(Article article);
-
- Optional findOne(String id);
-
- Iterable findAll();
-
- Page findByAuthorName(String name, Pageable pageable);
-
- Page findByAuthorNameUsingCustomQuery(String name, Pageable pageable);
-
- Page findByFilteredTagQuery(String tag, Pageable pageable);
-
- Page findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable);
-
- long count();
-
- void delete(Article article);
-}
diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java
deleted file mode 100644
index 5064f16508..0000000000
--- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/service/ArticleServiceImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.baeldung.spring.data.es.service;
-
-import java.util.Optional;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-import org.springframework.stereotype.Service;
-
-import com.baeldung.spring.data.es.model.Article;
-import com.baeldung.spring.data.es.repository.ArticleRepository;
-
-@Service
-public class ArticleServiceImpl implements ArticleService {
-
- private final ArticleRepository articleRepository;
-
- @Autowired
- public ArticleServiceImpl(ArticleRepository articleRepository) {
- this.articleRepository = articleRepository;
- }
-
- @Override
- public Article save(Article article) {
- return articleRepository.save(article);
- }
-
- @Override
- public Optional findOne(String id) {
- return articleRepository.findById(id);
- }
-
- @Override
- public Iterable findAll() {
- return articleRepository.findAll();
- }
-
- @Override
- public Page findByAuthorName(String name, Pageable pageable) {
- return articleRepository.findByAuthorsName(name, pageable);
- }
-
- @Override
- public Page findByAuthorNameUsingCustomQuery(String name, Pageable pageable) {
- return articleRepository.findByAuthorsNameUsingCustomQuery(name, pageable);
- }
-
- @Override
- public Page findByFilteredTagQuery(String tag, Pageable pageable) {
- return articleRepository.findByFilteredTagQuery(tag, pageable);
- }
-
- @Override
- public Page findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable) {
- return articleRepository.findByAuthorsNameAndFilteredTagQuery(name, tag, pageable);
- }
-
- @Override
- public long count() {
- return articleRepository.count();
- }
-
- @Override
- public void delete(Article article) {
- articleRepository.delete(article);
- }
-}
diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java
index c69deeb77c..6572896eca 100644
--- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java
+++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/SpringContextManualTest.java
@@ -8,10 +8,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.baeldung.spring.data.es.config.Config;
/**
+ * This Manual test requires: Elasticsearch instance running on localhost:9200.
*
- * This Manual test requires:
- * * Elasticsearch instance running on host
- *
+ * The following docker command can be used: docker run -d --name es762 -p
+ * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java
index e43dcdf43e..2ca5f28f13 100644
--- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java
+++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java
@@ -3,43 +3,48 @@ package com.baeldung.elasticsearch;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
-import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.DocWriteResponse.Result;
+import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
+import org.elasticsearch.action.get.GetRequest;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
-import org.elasticsearch.client.Client;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.transport.client.PreBuiltTransportClient;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.Before;
import org.junit.Test;
-
-import com.alibaba.fastjson.JSON;
+import org.springframework.data.elasticsearch.client.ClientConfiguration;
+import org.springframework.data.elasticsearch.client.RestClients;
/**
+ * This Manual test requires: Elasticsearch instance running on localhost:9200.
*
- * This Manual test requires:
- * * Elasticsearch instance running on host
- * * with cluster name = elasticsearch
- *
+ * The following docker command can be used: docker run -d --name es762 -p
+ * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
*/
public class ElasticSearchManualTest {
private List listOfPersons = new ArrayList<>();
- private Client client = null;
+ private RestHighLevelClient client = null;
@Before
public void setUp() throws UnknownHostException {
@@ -47,115 +52,122 @@ public class ElasticSearchManualTest {
Person person2 = new Person(25, "Janette Doe", new Date());
listOfPersons.add(person1);
listOfPersons.add(person2);
-
- client = new PreBuiltTransportClient(Settings.builder().put("client.transport.sniff", true)
- .put("cluster.name","elasticsearch").build())
- .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
+
+ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
+ .connectedTo("localhost:9200")
+ .build();
+ client = RestClients.create(clientConfiguration)
+ .rest();
}
@Test
- public void givenJsonString_whenJavaObject_thenIndexDocument() {
+ public void givenJsonString_whenJavaObject_thenIndexDocument() throws Exception {
String jsonObject = "{\"age\":20,\"dateOfBirth\":1471466076564,\"fullName\":\"John Doe\"}";
- IndexResponse response = client
- .prepareIndex("people", "Doe")
- .setSource(jsonObject, XContentType.JSON)
- .get();
+ IndexRequest request = new IndexRequest("people");
+ request.source(jsonObject, XContentType.JSON);
+
+ IndexResponse response = client.index(request, RequestOptions.DEFAULT);
String index = response.getIndex();
- String type = response.getType();
+ long version = response.getVersion();
assertEquals(Result.CREATED, response.getResult());
- assertEquals(index, "people");
- assertEquals(type, "Doe");
+ assertEquals(1, version);
+ assertEquals("people", index);
}
@Test
- public void givenDocumentId_whenJavaObject_thenDeleteDocument() {
+ public void givenDocumentId_whenJavaObject_thenDeleteDocument() throws Exception {
String jsonObject = "{\"age\":10,\"dateOfBirth\":1471455886564,\"fullName\":\"Johan Doe\"}";
- IndexResponse response = client
- .prepareIndex("people", "Doe")
- .setSource(jsonObject, XContentType.JSON)
- .get();
+ IndexRequest indexRequest = new IndexRequest("people");
+ indexRequest.source(jsonObject, XContentType.JSON);
+
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
String id = response.getId();
- DeleteResponse deleteResponse = client
- .prepareDelete("people", "Doe", id)
- .get();
- assertEquals(Result.DELETED,deleteResponse.getResult());
+ GetRequest getRequest = new GetRequest("people");
+ getRequest.id(id);
+
+ GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
+ System.out.println(getResponse.getSourceAsString());
+
+ DeleteRequest deleteRequest = new DeleteRequest("people");
+ deleteRequest.id(id);
+
+ DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
+
+ assertEquals(Result.DELETED, deleteResponse.getResult());
}
@Test
- public void givenSearchRequest_whenMatchAll_thenReturnAllResults() {
- SearchResponse response = client
- .prepareSearch()
- .execute()
- .actionGet();
- SearchHit[] searchHits = response
- .getHits()
- .getHits();
+ public void givenSearchRequest_whenMatchAll_thenReturnAllResults() throws Exception {
+ SearchRequest searchRequest = new SearchRequest();
+ SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
+ SearchHit[] searchHits = response.getHits()
+ .getHits();
List results = Arrays.stream(searchHits)
- .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
- .collect(Collectors.toList());
+ .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
+ .collect(Collectors.toList());
+
+ results.forEach(System.out::println);
}
@Test
- public void givenSearchParameters_thenReturnResults() {
- SearchResponse response = client
- .prepareSearch()
- .setTypes()
- .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
- .setPostFilter(QueryBuilders
- .rangeQuery("age")
+ public void givenSearchParameters_thenReturnResults() throws Exception {
+ SearchSourceBuilder builder = new SearchSourceBuilder().postFilter(QueryBuilders.rangeQuery("age")
.from(5)
- .to(15))
- .setFrom(0)
- .setSize(60)
- .setExplain(true)
- .execute()
- .actionGet();
+ .to(15));
- SearchResponse response2 = client
- .prepareSearch()
- .setTypes()
- .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
- .setPostFilter(QueryBuilders.simpleQueryStringQuery("+John -Doe OR Janette"))
- .setFrom(0)
- .setSize(60)
- .setExplain(true)
- .execute()
- .actionGet();
+ SearchRequest searchRequest = new SearchRequest();
+ searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
+ searchRequest.source(builder);
+
+ SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
+
+ builder = new SearchSourceBuilder().postFilter(QueryBuilders.simpleQueryStringQuery("+John -Doe OR Janette"));
+
+ searchRequest = new SearchRequest();
+ searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
+ searchRequest.source(builder);
+
+ SearchResponse response2 = client.search(searchRequest, RequestOptions.DEFAULT);
+
+ builder = new SearchSourceBuilder().postFilter(QueryBuilders.matchQuery("John", "Name*"));
+ searchRequest = new SearchRequest();
+ searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
+ searchRequest.source(builder);
+
+ SearchResponse response3 = client.search(searchRequest, RequestOptions.DEFAULT);
- SearchResponse response3 = client
- .prepareSearch()
- .setTypes()
- .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
- .setPostFilter(QueryBuilders.matchQuery("John", "Name*"))
- .setFrom(0)
- .setSize(60)
- .setExplain(true)
- .execute()
- .actionGet();
response2.getHits();
response3.getHits();
- final List results = Arrays.stream(response.getHits().getHits())
- .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
- .collect(Collectors.toList());
+ final List results = Stream.of(response.getHits()
+ .getHits(),
+ response2.getHits()
+ .getHits(),
+ response3.getHits()
+ .getHits())
+ .flatMap(Arrays::stream)
+ .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class))
+ .collect(Collectors.toList());
+
+ results.forEach(System.out::println);
}
@Test
public void givenContentBuilder_whenHelpers_thanIndexJson() throws IOException {
- XContentBuilder builder = XContentFactory
- .jsonBuilder()
- .startObject()
- .field("fullName", "Test")
- .field("salary", "11500")
- .field("age", "10")
- .endObject();
- IndexResponse response = client
- .prepareIndex("people", "Doe")
- .setSource(builder)
- .get();
+ XContentBuilder builder = XContentFactory.jsonBuilder()
+ .startObject()
+ .field("fullName", "Test")
+ .field("salary", "11500")
+ .field("age", "10")
+ .endObject();
- assertEquals(Result.CREATED, response.getResult());
+ IndexRequest indexRequest = new IndexRequest("people");
+ indexRequest.source(builder);
+
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
+
+ assertEquals(Result.CREATED, response.getResult());
}
}
diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java
index f9a42050b6..64b2ea2437 100644
--- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java
+++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesManualTest.java
@@ -1,4 +1,5 @@
package com.baeldung.elasticsearch;
+
import static org.junit.Assert.assertTrue;
import java.io.IOException;
@@ -7,162 +8,169 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
-import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import com.baeldung.spring.data.es.config.Config;
+
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
+import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.builders.ShapeBuilders;
+import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.locationtech.jts.geom.Coordinate;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import com.baeldung.spring.data.es.config.Config;
-import com.vividsolutions.jts.geom.Coordinate;
-
/**
+ * This Manual test requires: Elasticsearch instance running on localhost:9200.
*
- * This Manual test requires:
- * * Elasticsearch instance running on host
- * * with cluster name = elasticsearch
- * * and further configurations
- *
+ * The following docker command can be used: docker run -d --name es762 -p
+ * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class GeoQueriesManualTest {
private static final String WONDERS_OF_WORLD = "wonders-of-world";
- private static final String WONDERS = "Wonders";
@Autowired
- private ElasticsearchTemplate elasticsearchTemplate;
-
- @Autowired
- private Client client;
+ private RestHighLevelClient client;
@Before
- public void setUp() {
- String jsonObject = "{\"Wonders\":{\"properties\":{\"name\":{\"type\":\"string\",\"index\":\"not_analyzed\"},\"region\":{\"type\":\"geo_shape\",\"tree\":\"quadtree\",\"precision\":\"1m\"},\"location\":{\"type\":\"geo_point\"}}}}";
+ public void setUp() throws Exception {
+ String jsonObject = "{\"properties\":{\"name\":{\"type\":\"text\",\"index\":false},\"region\":{\"type\":\"geo_shape\"},\"location\":{\"type\":\"geo_point\"}}}";
+
CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD);
- req.mapping(WONDERS, jsonObject, XContentType.JSON);
- client.admin()
- .indices()
- .create(req)
- .actionGet();
+ req.mapping(jsonObject, XContentType.JSON);
+
+ client.indices()
+ .create(req, RequestOptions.DEFAULT);
}
@Test
- public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException{
+ public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException {
String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,30.2],[80.1, 25]]}}";
- IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS)
- .setSource(jsonObject, XContentType.JSON)
- .get();
-
+ IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
+ indexRequest.source(jsonObject, XContentType.JSON);
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
+
String tajMahalId = response.getId();
- client.admin()
- .indices()
- .prepareRefresh(WONDERS_OF_WORLD)
- .get();
- Coordinate topLeft =new Coordinate(74, 31.2);
- Coordinate bottomRight =new Coordinate(81.1, 24);
- QueryBuilder qb = QueryBuilders
- .geoShapeQuery("region", ShapeBuilders.newEnvelope(topLeft, bottomRight))
- .relation(ShapeRelation.WITHIN);
+ RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
+ client.indices()
+ .refresh(refreshRequest, RequestOptions.DEFAULT);
+ Coordinate topLeft = new Coordinate(74, 31.2);
+ Coordinate bottomRight = new Coordinate(81.1, 24);
- SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD)
- .setTypes(WONDERS)
- .setQuery(qb)
- .execute()
- .actionGet();
+ GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region", new EnvelopeBuilder(topLeft, bottomRight).buildGeometry());
+ qb.relation(ShapeRelation.INTERSECTS);
+
+ SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
+ SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
+ searchRequest.source(source);
+
+ SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
List ids = Arrays.stream(searchResponse.getHits()
- .getHits())
- .map(SearchHit::getId)
- .collect(Collectors.toList());
+ .getHits())
+ .map(SearchHit::getId)
+ .collect(Collectors.toList());
assertTrue(ids.contains(tajMahalId));
}
@Test
- public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() {
+ public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() throws Exception {
String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}";
- IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS)
- .setSource(jsonObject, XContentType.JSON)
- .get();
+
+ IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
+ indexRequest.source(jsonObject, XContentType.JSON);
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
+
String pyramidsOfGizaId = response.getId();
- client.admin()
- .indices()
- .prepareRefresh(WONDERS_OF_WORLD)
- .get();
+
+ RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
+ client.indices()
+ .refresh(refreshRequest, RequestOptions.DEFAULT);
QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location")
- .setCorners(31,30,28,32);
-
- SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD)
- .setTypes(WONDERS)
- .setQuery(qb)
- .execute()
- .actionGet();
+ .setCorners(31, 30, 28, 32);
+
+ SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
+ SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
+ searchRequest.source(source);
+
+ SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
+
List ids = Arrays.stream(searchResponse.getHits()
- .getHits())
- .map(SearchHit::getId)
- .collect(Collectors.toList());
+ .getHits())
+ .map(SearchHit::getId)
+ .collect(Collectors.toList());
assertTrue(ids.contains(pyramidsOfGizaId));
}
@Test
- public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() {
+ public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() throws Exception {
String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}";
- IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS)
- .setSource(jsonObject, XContentType.JSON)
- .get();
+
+ IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
+ indexRequest.source(jsonObject, XContentType.JSON);
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
+
String lighthouseOfAlexandriaId = response.getId();
- client.admin()
- .indices()
- .prepareRefresh(WONDERS_OF_WORLD)
- .get();
+
+ RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
+ client.indices()
+ .refresh(refreshRequest, RequestOptions.DEFAULT);
QueryBuilder qb = QueryBuilders.geoDistanceQuery("location")
- .point(29.976, 31.131)
- .distance(10, DistanceUnit.MILES);
+ .point(29.976, 31.131)
+ .distance(10, DistanceUnit.MILES);
+
+ SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
+ SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
+ searchRequest.source(source);
+
+ SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
- SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD)
- .setTypes(WONDERS)
- .setQuery(qb)
- .execute()
- .actionGet();
List ids = Arrays.stream(searchResponse.getHits()
- .getHits())
- .map(SearchHit::getId)
- .collect(Collectors.toList());
+ .getHits())
+ .map(SearchHit::getId)
+ .collect(Collectors.toList());
assertTrue(ids.contains(lighthouseOfAlexandriaId));
}
@Test
- public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() {
+ public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() throws Exception {
String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}";
- IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS)
- .setSource(jsonObject, XContentType.JSON)
- .get();
+
+ IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
+ indexRequest.source(jsonObject, XContentType.JSON);
+ IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
+
String greatRannOfKutchid = response.getId();
- client.admin()
- .indices()
- .prepareRefresh(WONDERS_OF_WORLD)
- .get();
+
+ RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
+ client.indices()
+ .refresh(refreshRequest, RequestOptions.DEFAULT);
List allPoints = new ArrayList();
allPoints.add(new GeoPoint(22.733, 68.859));
@@ -170,20 +178,23 @@ public class GeoQueriesManualTest {
allPoints.add(new GeoPoint(23, 70.859));
QueryBuilder qb = QueryBuilders.geoPolygonQuery("location", allPoints);
- SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD)
- .setTypes(WONDERS)
- .setQuery(qb)
- .execute()
- .actionGet();
+ SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
+ SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
+ searchRequest.source(source);
+
+ SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
+
List ids = Arrays.stream(searchResponse.getHits()
- .getHits())
- .map(SearchHit::getId)
- .collect(Collectors.toList());
+ .getHits())
+ .map(SearchHit::getId)
+ .collect(Collectors.toList());
assertTrue(ids.contains(greatRannOfKutchid));
}
@After
- public void destroy() {
- elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD);
+ public void destroy() throws Exception {
+ DeleteIndexRequest deleteIndex = new DeleteIndexRequest(WONDERS_OF_WORLD);
+ client.indices()
+ .delete(deleteIndex, RequestOptions.DEFAULT);
}
}
diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java
index bed2e2ff25..412cd04e09 100644
--- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java
+++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java
@@ -10,68 +10,71 @@ import static org.junit.Assert.assertNotNull;
import java.util.List;
+import com.baeldung.spring.data.es.config.Config;
+import com.baeldung.spring.data.es.model.Article;
+import com.baeldung.spring.data.es.model.Author;
+import com.baeldung.spring.data.es.repository.ArticleRepository;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
-import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
+import org.springframework.data.elasticsearch.core.SearchHits;
+import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
-import org.springframework.data.elasticsearch.core.query.SearchQuery;
+import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import com.baeldung.spring.data.es.config.Config;
-import com.baeldung.spring.data.es.model.Article;
-import com.baeldung.spring.data.es.model.Author;
-import com.baeldung.spring.data.es.service.ArticleService;
-
/**
+ * This Manual test requires: Elasticsearch instance running on localhost:9200.
*
- * This Manual test requires:
- * * Elasticsearch instance running on host
- * * with cluster name = elasticsearch
- *
+ * The following docker command can be used: docker run -d --name es762 -p
+ * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class ElasticSearchManualTest {
@Autowired
- private ElasticsearchTemplate elasticsearchTemplate;
+ private ElasticsearchRestTemplate elasticsearchTemplate;
@Autowired
- private ArticleService articleService;
+ private ArticleRepository articleRepository;
private final Author johnSmith = new Author("John Smith");
private final Author johnDoe = new Author("John Doe");
@Before
public void before() {
- elasticsearchTemplate.deleteIndex(Article.class);
- elasticsearchTemplate.createIndex(Article.class);
- // don't call putMapping() to test the default mappings
-
Article article = new Article("Spring Data Elasticsearch");
article.setAuthors(asList(johnSmith, johnDoe));
article.setTags("elasticsearch", "spring data");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Search engines");
article.setAuthors(asList(johnDoe));
article.setTags("search engines", "tutorial");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Second Article About Elasticsearch");
article.setAuthors(asList(johnSmith));
article.setTags("elasticsearch", "spring data");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Elasticsearch Tutorial");
article.setAuthors(asList(johnDoe));
article.setTags("elasticsearch");
- articleService.save(article);
+ articleRepository.save(article);
+ }
+
+ @After
+ public void after() {
+ articleRepository.deleteAll();
}
@Test
@@ -81,82 +84,85 @@ public class ElasticSearchManualTest {
Article article = new Article("Making Search Elastic");
article.setAuthors(authors);
- article = articleService.save(article);
+ article = articleRepository.save(article);
assertNotNull(article.getId());
}
@Test
public void givenPersistedArticles_whenSearchByAuthorsName_thenRightFound() {
-
- final Page articleByAuthorName = articleService
- .findByAuthorName(johnSmith.getName(), PageRequest.of(0, 10));
+ final Page articleByAuthorName = articleRepository.findByAuthorsName(johnSmith.getName(), PageRequest.of(0, 10));
assertEquals(2L, articleByAuthorName.getTotalElements());
}
@Test
public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() {
- final Page articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("Smith", PageRequest.of(0, 10));
+ final Page articleByAuthorName = articleRepository.findByAuthorsNameUsingCustomQuery("Smith", PageRequest.of(0, 10));
assertEquals(2L, articleByAuthorName.getTotalElements());
}
@Test
public void givenTagFilterQuery_whenSearchByTag_thenArticleIsFound() {
- final Page articleByAuthorName = articleService.findByFilteredTagQuery("elasticsearch", PageRequest.of(0, 10));
+ final Page articleByAuthorName = articleRepository.findByFilteredTagQuery("elasticsearch", PageRequest.of(0, 10));
assertEquals(3L, articleByAuthorName.getTotalElements());
}
@Test
public void givenTagFilterQuery_whenSearchByAuthorsName_thenArticleIsFound() {
- final Page articleByAuthorName = articleService.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", PageRequest.of(0, 10));
+ final Page articleByAuthorName = articleRepository.findByAuthorsNameAndFilteredTagQuery("Doe", "elasticsearch", PageRequest.of(0, 10));
assertEquals(2L, articleByAuthorName.getTotalElements());
}
@Test
public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() {
+ final Query searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*"))
+ .build();
- final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*"))
- .build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
- assertEquals(1, articles.size());
+ assertEquals(1, articles.getTotalHits());
}
@Test
public void givenSavedDoc_whenTitleUpdated_thenCouldFindByUpdatedTitle() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(fuzzyQuery("title", "serch")).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
+ final Query searchQuery = new NativeSearchQueryBuilder().withQuery(fuzzyQuery("title", "serch"))
+ .build();
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
- assertEquals(1, articles.size());
+ assertEquals(1, articles.getTotalHits());
- final Article article = articles.get(0);
+ final Article article = articles.getSearchHit(0)
+ .getContent();
final String newTitle = "Getting started with Search Engines";
article.setTitle(newTitle);
- articleService.save(article);
+ articleRepository.save(article);
- assertEquals(newTitle, articleService.findOne(article.getId()).get().getTitle());
+ assertEquals(newTitle, articleRepository.findById(article.getId())
+ .get()
+ .getTitle());
}
@Test
public void givenSavedDoc_whenDelete_thenRemovedFromIndex() {
-
final String articleTitle = "Spring Data Elasticsearch";
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
- final long count = articleService.count();
+ final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%"))
+ .build();
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
- articleService.delete(articles.get(0));
+ assertEquals(1, articles.getTotalHits());
+ final long count = articleRepository.count();
- assertEquals(count - 1, articleService.count());
+ articleRepository.delete(articles.getSearchHit(0)
+ .getContent());
+
+ assertEquals(count - 1, articleRepository.count());
}
@Test
public void givenSavedDoc_whenOneTermMatches_thenFindByTitle() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", "Search engines").operator(AND)).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
+ final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND))
+ .build();
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+ assertEquals(1, articles.getTotalHits());
}
}
diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java
index 5e24d8398c..aaf0c80097 100644
--- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java
+++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java
@@ -2,7 +2,6 @@ package com.baeldung.spring.data.es;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
-import static org.elasticsearch.index.query.Operator.AND;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
@@ -14,190 +13,225 @@ import static org.junit.Assert.assertEquals;
import java.util.List;
import java.util.Map;
+import com.baeldung.spring.data.es.config.Config;
+import com.baeldung.spring.data.es.model.Article;
+import com.baeldung.spring.data.es.model.Author;
+import com.baeldung.spring.data.es.repository.ArticleRepository;
+
import org.apache.lucene.search.join.ScoreMode;
+import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
+import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
-import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
-import org.elasticsearch.search.aggregations.bucket.terms.Terms;
+import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
+import org.springframework.data.elasticsearch.core.SearchHits;
+import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
+import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
-import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import com.baeldung.spring.data.es.config.Config;
-import com.baeldung.spring.data.es.model.Article;
-import com.baeldung.spring.data.es.model.Author;
-import com.baeldung.spring.data.es.service.ArticleService;
-
/**
+ * This Manual test requires: Elasticsearch instance running on localhost:9200.
*
- * This Manual test requires:
- * * Elasticsearch instance running on host
- * * with cluster name = elasticsearch
- *
+ * The following docker command can be used: docker run -d --name es762 -p
+ * 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class ElasticSearchQueryManualTest {
@Autowired
- private ElasticsearchTemplate elasticsearchTemplate;
+ private ElasticsearchRestTemplate elasticsearchTemplate;
@Autowired
- private ArticleService articleService;
+ private ArticleRepository articleRepository;
@Autowired
- private Client client;
+ private RestHighLevelClient client;
private final Author johnSmith = new Author("John Smith");
private final Author johnDoe = new Author("John Doe");
@Before
public void before() {
- elasticsearchTemplate.deleteIndex(Article.class);
- elasticsearchTemplate.createIndex(Article.class);
- elasticsearchTemplate.putMapping(Article.class);
- elasticsearchTemplate.refresh(Article.class);
-
Article article = new Article("Spring Data Elasticsearch");
article.setAuthors(asList(johnSmith, johnDoe));
article.setTags("elasticsearch", "spring data");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Search engines");
article.setAuthors(asList(johnDoe));
article.setTags("search engines", "tutorial");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Second Article About Elasticsearch");
article.setAuthors(asList(johnSmith));
article.setTags("elasticsearch", "spring data");
- articleService.save(article);
+ articleRepository.save(article);
article = new Article("Elasticsearch Tutorial");
article.setAuthors(asList(johnDoe));
article.setTags("elasticsearch");
- articleService.save(article);
+ articleRepository.save(article);
+ }
+
+ @After
+ public void after() {
+ articleRepository.deleteAll();
}
@Test
public void givenFullTitle_whenRunMatchQuery_thenDocIsFound() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", "Search engines").operator(AND)).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(Operator.AND))
+ .build();
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+ assertEquals(1, articles.getTotalHits());
}
@Test
public void givenOneTermFromTitle_whenRunMatchQuery_thenDocIsFound() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", "Engines Solutions")).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
- assertEquals("Search engines", articles.get(0).getTitle());
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Engines Solutions"))
+ .build();
+
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(1, articles.getTotalHits());
+ assertEquals("Search engines", articles.getSearchHit(0)
+ .getContent()
+ .getTitle());
}
@Test
public void givenPartTitle_whenRunMatchQuery_thenDocIsFound() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", "elasticsearch data")).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(3, articles.size());
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "elasticsearch data"))
+ .build();
+
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(3, articles.getTotalHits());
}
@Test
public void givenFullTitle_whenRunMatchQueryOnVerbatimField_thenDocIsFound() {
- SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build();
- List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
+ NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch"))
+ .build();
+
+ SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(1, articles.getTotalHits());
searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About"))
- .build();
- articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(0, articles.size());
+ .build();
+
+ articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+ assertEquals(0, articles.getTotalHits());
}
@Test
public void givenNestedObject_whenQueryByAuthorsName_thenFoundArticlesByThatAuthor() {
final QueryBuilder builder = nestedQuery("authors", boolQuery().must(termQuery("authors.name", "smith")), ScoreMode.None);
- final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder)
+ .build();
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
- assertEquals(2, articles.size());
+ assertEquals(2, articles.getTotalHits());
}
@Test
- public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() {
- final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags").field("title");
- final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation)
- .execute().actionGet();
+ public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() throws Exception {
+ final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
+ .field("title");
- final Map results = response.getAggregations().asMap();
- final StringTerms topTags = (StringTerms) results.get("top_tags");
+ final SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation);
+ final SearchRequest searchRequest = new SearchRequest("blog").source(builder);
- final List keys = topTags.getBuckets().stream()
- .map(MultiBucketsAggregation.Bucket::getKeyAsString)
- .sorted()
- .collect(toList());
+ final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
+
+ final Map results = response.getAggregations()
+ .asMap();
+ final ParsedStringTerms topTags = (ParsedStringTerms) results.get("top_tags");
+
+ final List keys = topTags.getBuckets()
+ .stream()
+ .map(MultiBucketsAggregation.Bucket::getKeyAsString)
+ .sorted()
+ .collect(toList());
assertEquals(asList("about", "article", "data", "elasticsearch", "engines", "search", "second", "spring", "tutorial"), keys);
}
@Test
- public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() {
- final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags")
- .order(Terms.Order.count(false));
- final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation)
- .execute().actionGet();
+ public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() throws Exception {
+ final TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
+ .field("tags")
+ .order(BucketOrder.count(false));
- final Map results = response.getAggregations().asMap();
- final StringTerms topTags = (StringTerms) results.get("top_tags");
+ final SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation);
+ final SearchRequest searchRequest = new SearchRequest().indices("blog")
+ .source(builder);
- final List keys = topTags.getBuckets().stream()
- .map(MultiBucketsAggregation.Bucket::getKeyAsString)
- .collect(toList());
+ final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
+
+ final Map results = response.getAggregations()
+ .asMap();
+ final ParsedStringTerms topTags = (ParsedStringTerms) results.get("top_tags");
+
+ final List keys = topTags.getBuckets()
+ .stream()
+ .map(MultiBucketsAggregation.Bucket::getKeyAsString)
+ .collect(toList());
assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);
}
@Test
public void givenNotExactPhrase_whenUseSlop_thenQueryMatches() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1))
+ .build();
+
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(1, articles.getTotalHits());
}
@Test
public void givenPhraseWithType_whenUseFuzziness_thenQueryMatches() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE)
- .prefixLength(3)).build();
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "spring date elasticserch").operator(Operator.AND)
+ .fuzziness(Fuzziness.ONE)
+ .prefixLength(3))
+ .build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(1, articles.size());
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(1, articles.getTotalHits());
}
@Test
public void givenMultimatchQuery_whenDoSearch_thenAllProvidedFieldsMatch() {
- final SearchQuery searchQuery = new NativeSearchQueryBuilder()
- .withQuery(multiMatchQuery("tutorial").field("title").field("tags")
- .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build();
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery("tutorial").field("title")
+ .field("tags")
+ .type(MultiMatchQueryBuilder.Type.BEST_FIELDS))
+ .build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
- assertEquals(2, articles.size());
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
+
+ assertEquals(2, articles.getTotalHits());
}
@Test
@@ -205,10 +239,10 @@ public class ElasticSearchQueryManualTest {
final QueryBuilder builder = boolQuery().must(nestedQuery("authors", boolQuery().must(termQuery("authors.name", "doe")), ScoreMode.None))
.filter(termQuery("tags", "elasticsearch"));
- final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder)
+ final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder)
.build();
- final List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class);
+ final SearchHits articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog"));
- assertEquals(2, articles.size());
+ assertEquals(2, articles.getTotalHits());
}
}
diff --git a/persistence-modules/spring-data-jpa-5/pom.xml b/persistence-modules/spring-data-jpa-5/pom.xml
index 3053384559..6a5cdc86c2 100644
--- a/persistence-modules/spring-data-jpa-5/pom.xml
+++ b/persistence-modules/spring-data-jpa-5/pom.xml
@@ -1,6 +1,7 @@
+
4.0.0
spring-data-jpa-5
spring-data-jpa-5
@@ -11,7 +12,7 @@
0.0.1-SNAPSHOT
../../parent-boot-2
-
+
org.springframework.boot
@@ -28,10 +29,43 @@
spring-boot-starter-data-jdbc
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
com.h2database
h2
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ 1.3.1.Final
+ provided
+
+
-
+
+ src/main/java
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.3.1.Final
+
+
+
+
+
+
+
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java
new file mode 100644
index 0000000000..a750fcadf7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/PartialUpdateApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.partialupdate;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class PartialUpdateApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(PartialUpdateApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java
new file mode 100644
index 0000000000..352e361bd9
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/ContactPhone.java
@@ -0,0 +1,22 @@
+package com.baeldung.partialupdate.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class ContactPhone {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ public long id;
+ @Column(nullable=false)
+ public long customerId;
+ public String phone;
+
+ @Override
+ public String toString() {
+ return phone;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java
new file mode 100644
index 0000000000..b19d0b7952
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/Customer.java
@@ -0,0 +1,23 @@
+package com.baeldung.partialupdate.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ public long id;
+ public String name;
+ public String phone;
+ //...
+ public String phone99;
+
+ @Override public String toString() {
+ return String.format("Customer %s, Phone: %s",
+ this.name, this.phone);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java
new file mode 100644
index 0000000000..0ecf206d9a
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerDto.java
@@ -0,0 +1,31 @@
+package com.baeldung.partialupdate.model;
+
+public class CustomerDto {
+ private long id;
+ public String name;
+ public String phone;
+ //...
+ private String phone99;
+
+ public CustomerDto(long id) {
+ this.id = id;
+ }
+
+ public CustomerDto(Customer c) {
+ this.id = c.id;
+ this.name = c.name;
+ this.phone = c.phone;
+ }
+
+ public long getId() {
+ return this.id;
+ }
+
+ public Customer convertToEntity() {
+ Customer c = new Customer();
+ c.id = id;
+ c.name = name;
+ c.phone = phone;
+ return c;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java
new file mode 100644
index 0000000000..dd053a963d
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/model/CustomerStructured.java
@@ -0,0 +1,27 @@
+package com.baeldung.partialupdate.model;
+
+import java.util.List;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class CustomerStructured {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ public long id;
+ public String name;
+ @OneToMany(fetch = FetchType.EAGER, targetEntity = ContactPhone.class, mappedBy = "customerId")
+ public List contactPhones;
+
+ @Override public String toString() {
+ return String.format("Customer %s, Phone: %s",
+ this.name, this.contactPhones.stream()
+ .map(e -> e.toString()).reduce("", String::concat));
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java
new file mode 100644
index 0000000000..4668181e05
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/ContactPhoneRepository.java
@@ -0,0 +1,12 @@
+package com.baeldung.partialupdate.repository;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.partialupdate.model.ContactPhone;
+
+@Repository
+public interface ContactPhoneRepository extends CrudRepository {
+ ContactPhone findById(long id);
+ ContactPhone findByCustomerId(long id);
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java
new file mode 100644
index 0000000000..43e61df8ab
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerRepository.java
@@ -0,0 +1,18 @@
+package com.baeldung.partialupdate.repository;
+
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.partialupdate.model.Customer;
+
+@Repository
+public interface CustomerRepository extends CrudRepository {
+ Customer findById(long id);
+
+ @Modifying
+ @Query("update Customer u set u.phone = :phone where u.id = :id")
+ void updatePhone(@Param(value = "id") long id, @Param(value = "phone") String phone);
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java
new file mode 100644
index 0000000000..0f9fd1e92e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/repository/CustomerStructuredRepository.java
@@ -0,0 +1,11 @@
+package com.baeldung.partialupdate.repository;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.partialupdate.model.CustomerStructured;
+
+@Repository
+public interface CustomerStructuredRepository extends CrudRepository {
+ CustomerStructured findById(long id);
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java
new file mode 100644
index 0000000000..9da97a7775
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/service/CustomerService.java
@@ -0,0 +1,87 @@
+package com.baeldung.partialupdate.service;
+
+import javax.transaction.Transactional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.partialupdate.model.ContactPhone;
+import com.baeldung.partialupdate.model.Customer;
+import com.baeldung.partialupdate.model.CustomerDto;
+import com.baeldung.partialupdate.model.CustomerStructured;
+import com.baeldung.partialupdate.repository.ContactPhoneRepository;
+import com.baeldung.partialupdate.repository.CustomerRepository;
+import com.baeldung.partialupdate.repository.CustomerStructuredRepository;
+import com.baeldung.partialupdate.util.CustomerMapper;
+
+@Service
+@Transactional
+public class CustomerService {
+
+ @Autowired
+ CustomerRepository repo;
+ @Autowired
+ CustomerStructuredRepository repo2;
+ @Autowired
+ ContactPhoneRepository repo3;
+ @Autowired
+ CustomerMapper mapper;
+
+ public Customer getCustomer(long id) {
+ return repo.findById(id);
+ }
+
+ public void updateCustomerWithCustomQuery(long id, String phone) {
+ repo.updatePhone(id, phone);
+ }
+
+ public Customer addCustomer(String name) {
+ Customer myCustomer = new Customer();
+ myCustomer.name = name;
+ repo.save(myCustomer);
+ return myCustomer;
+ }
+
+ public Customer updateCustomer(long id, String phone) {
+ Customer myCustomer = repo.findById(id);
+ myCustomer.phone = phone;
+ repo.save(myCustomer);
+ return myCustomer;
+ }
+
+ public Customer addCustomer(CustomerDto dto) {
+ Customer myCustomer = new Customer();
+ mapper.updateCustomerFromDto(dto, myCustomer);
+ repo.save(myCustomer);
+ return myCustomer;
+ }
+
+ public Customer updateCustomer(CustomerDto dto) {
+ Customer myCustomer = repo.findById(dto.getId());
+ mapper.updateCustomerFromDto(dto, myCustomer);
+ repo.save(myCustomer);
+ return myCustomer;
+ }
+
+ public CustomerStructured addCustomerStructured(String name) {
+ CustomerStructured myCustomer = new CustomerStructured();
+ myCustomer.name = name;
+ repo2.save(myCustomer);
+ return myCustomer;
+ }
+
+ public void addCustomerPhone(long customerId, String phone) {
+ ContactPhone myPhone = new ContactPhone();
+ myPhone.phone = phone;
+ myPhone.customerId = customerId;
+ repo3.save(myPhone);
+ }
+
+ public CustomerStructured updateCustomerStructured(long id, String name) {
+ CustomerStructured myCustomer = repo2.findById(id);
+ myCustomer.name = name;
+ repo2.save(myCustomer);
+ return myCustomer;
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java
new file mode 100644
index 0000000000..8a666e3e6c
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/main/java/com/baeldung/partialupdate/util/CustomerMapper.java
@@ -0,0 +1,15 @@
+package com.baeldung.partialupdate.util;
+
+import org.mapstruct.BeanMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+
+import com.baeldung.partialupdate.model.Customer;
+import com.baeldung.partialupdate.model.CustomerDto;
+
+@Mapper(componentModel = "spring")
+public interface CustomerMapper {
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateCustomerFromDto(CustomerDto dto, @MappingTarget Customer entity);
+}
diff --git a/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java b/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java
new file mode 100644
index 0000000000..874e18c4ad
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-5/src/test/java/com/baeldung/partialupdate/PartialUpdateUnitTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.partialupdate;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+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 com.baeldung.partialupdate.model.Customer;
+import com.baeldung.partialupdate.model.CustomerDto;
+import com.baeldung.partialupdate.model.CustomerStructured;
+import com.baeldung.partialupdate.service.CustomerService;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = PartialUpdateApplication.class)
+public class PartialUpdateUnitTest {
+
+ @Autowired
+ CustomerService service;
+
+ @Test
+ public void givenCustomer_whenUpdate_thenSuccess() {
+ Customer myCustomer = service.addCustomer("John");
+ myCustomer = service.updateCustomer(myCustomer.id, "+00");
+ assertEquals("+00", myCustomer.phone);
+ }
+
+ @Test
+ public void givenCustomer_whenUpdateWithQuery_thenSuccess() {
+ Customer myCustomer = service.addCustomer("John");
+ service.updateCustomerWithCustomQuery(myCustomer.id, "+88");
+ myCustomer = service.getCustomer(myCustomer.id);
+ assertEquals("+88", myCustomer.phone);
+ }
+
+ @Test
+ public void givenCustomerDto_whenUpdateWithMapper_thenSuccess() {
+ CustomerDto dto = new CustomerDto(new Customer());
+ dto.name = "Johnny";
+ Customer entity = service.addCustomer(dto);
+
+ CustomerDto dto2 = new CustomerDto(entity.id);
+ dto2.phone = "+44";
+ entity = service.updateCustomer(dto2);
+
+ assertEquals("Johnny", entity.name);
+ }
+
+ @Test
+ public void givenCustomerStructured_whenUpdateCustomerPhone_thenSuccess() {
+ CustomerStructured myCustomer = service.addCustomerStructured("John");
+ assertEquals(null, myCustomer.contactPhones);
+
+ service.addCustomerPhone(myCustomer.id, "+44");
+ myCustomer = service.updateCustomerStructured(myCustomer.id, "Mr. John");
+
+ assertNotEquals(null, myCustomer.contactPhones);
+ assertEquals(1, myCustomer.contactPhones.size());
+ }
+}
diff --git a/persistence-modules/spring-data-redis/README.md b/persistence-modules/spring-data-redis/README.md
index 175634376b..95cba2c159 100644
--- a/persistence-modules/spring-data-redis/README.md
+++ b/persistence-modules/spring-data-redis/README.md
@@ -4,7 +4,6 @@
- [Introduction to Spring Data Redis](https://www.baeldung.com/spring-data-redis-tutorial)
- [PubSub Messaging with Spring Data Redis](https://www.baeldung.com/spring-data-redis-pub-sub)
- [An Introduction to Spring Data Redis Reactive](https://www.baeldung.com/spring-data-redis-reactive)
-- [Delete Everything in Redis](https://www.baeldung.com/redis-delete-data)
### Build the Project with Tests Running
```
@@ -15,4 +14,3 @@ mvn clean install
```
mvn test
```
-
diff --git a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java
index fdc279be42..497e1506bd 100644
--- a/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java
+++ b/persistence-modules/spring-data-redis/src/main/java/com/baeldung/spring/data/redis/config/RedisConfig.java
@@ -35,18 +35,6 @@ public class RedisConfig {
template.setValueSerializer(new GenericToStringSerializer
diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml
index 6caa93158a..7992c0ce12 100644
--- a/spring-boot-modules/pom.xml
+++ b/spring-boot-modules/pom.xml
@@ -15,6 +15,7 @@
spring-boot
+ spring-boot-1
spring-boot-admin
spring-boot-angular
spring-boot-annotations
diff --git a/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties b/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..9dda3b659b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
diff --git a/spring-boot-modules/spring-boot-1/README.md b/spring-boot-modules/spring-boot-1/README.md
new file mode 100644
index 0000000000..a818f60fb5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/README.md
@@ -0,0 +1,6 @@
+## Spring Boot 1.x Actuator
+
+This module contains articles about Spring Boot Actuator in Spring Boot version 1.x.
+
+## Relevant articles:
+- [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators)
diff --git a/spring-boot-modules/spring-boot-1/mvnw b/spring-boot-modules/spring-boot-1/mvnw
new file mode 100755
index 0000000000..b74391fdf4
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/mvnw
@@ -0,0 +1,234 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ]; then
+
+ if [ -f /etc/mavenrc ]; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ]; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false
+darwin=false
+mingw=false
+case "$(uname)" in
+CYGWIN*) cygwin=true ;;
+MINGW*) mingw=true ;;
+Darwin*)
+ darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="$(/usr/libexec/java_home)"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ]; then
+ if [ -r /etc/gentoo-release ]; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+if [ -z "$M2_HOME" ]; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ]; do
+ ls=$(ls -ld "$PRG")
+ link=$(expr "$ls" : '.*-> \(.*\)$')
+ if expr "$link" : '/.*' >/dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")/$link"
+ fi
+ done
+
+ saveddir=$(pwd)
+
+ M2_HOME=$(dirname "$PRG")/..
+
+ # make it fully qualified
+ M2_HOME=$(cd "$M2_HOME" && pwd)
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=$(cygpath --unix "$M2_HOME")
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="$( (
+ cd "$M2_HOME"
+ pwd
+ ))"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="$( (
+ cd "$JAVA_HOME"
+ pwd
+ ))"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then
+ if $darwin; then
+ javaHome="$(dirname \"$javaExecutable\")"
+ javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f \"$javaExecutable\")"
+ fi
+ javaHome="$(dirname \"$javaExecutable\")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ]; then
+ if [ -n "$JAVA_HOME" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(which java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ]; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]; then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ]; do
+ if [ -d "$wdir"/.mvn ]; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(
+ cd "$wdir/.."
+ pwd
+ )
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' <"$1")"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(pwd)")
+if [ -z "$BASE_DIR" ]; then
+ exit 1
+fi
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+echo $MAVEN_PROJECTBASEDIR
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=$(cygpath --path --windows "$M2_HOME")
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/spring-boot-modules/spring-boot-1/mvnw.cmd b/spring-boot-modules/spring-boot-1/mvnw.cmd
new file mode 100644
index 0000000000..019bd74d76
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/mvnw.cmd
@@ -0,0 +1,143 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/spring-boot-modules/spring-boot-1/pom.xml b/spring-boot-modules/spring-boot-1/pom.xml
new file mode 100644
index 0000000000..145bb221e0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+ spring-boot-1
+ jar
+ Module for Spring Boot version 1.x
+
+
+ com.baeldung
+ parent-boot-1
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-1
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java
new file mode 100644
index 0000000000..f48fc87640
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/CustomEndpoint.java
@@ -0,0 +1,35 @@
+package com.baeldung.actuator;
+
+import org.springframework.boot.actuate.endpoint.Endpoint;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class CustomEndpoint implements Endpoint> {
+
+ @Override
+ public String getId() {
+ return "customEndpoint";
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isSensitive() {
+ return true;
+ }
+
+ @Override
+ public List invoke() {
+ // Custom logic to build the output
+ List messages = new ArrayList<>();
+ messages.add("This is message 1");
+ messages.add("This is message 2");
+ return messages;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java
new file mode 100644
index 0000000000..45db408465
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/HealthCheck.java
@@ -0,0 +1,23 @@
+package com.baeldung.actuator;
+
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.stereotype.Component;
+
+@Component("myHealthCheck")
+public class HealthCheck implements HealthIndicator {
+
+ @Override
+ public Health health() {
+ int errorCode = check(); // perform some specific health check
+ if (errorCode != 0) {
+ return Health.down().withDetail("Error Code", errorCode).build();
+ }
+ return Health.up().build();
+ }
+
+ public int check() {
+ // Our logic to check health
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java
new file mode 100644
index 0000000000..925ce69a39
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/LoginServiceImpl.java
@@ -0,0 +1,28 @@
+package com.baeldung.actuator;
+
+import org.springframework.boot.actuate.metrics.CounterService;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+
+@Service
+public class LoginServiceImpl {
+
+ private final CounterService counterService;
+
+ public LoginServiceImpl(CounterService counterService) {
+ this.counterService = counterService;
+ }
+
+ public boolean login(String userName, char[] password) {
+ boolean success;
+ if (userName.equals("admin") && Arrays.equals("secret".toCharArray(), password)) {
+ counterService.increment("counter.login.success");
+ success = true;
+ } else {
+ counterService.increment("counter.login.failure");
+ success = false;
+ }
+ return success;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java
new file mode 100644
index 0000000000..bdf28e49cb
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/main/java/com/baeldung/actuator/SpringBoot.java
@@ -0,0 +1,13 @@
+package com.baeldung.actuator;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringBoot {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBoot.class, args);
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-1/src/main/resources/application.properties b/spring-boot-modules/spring-boot-1/src/main/resources/application.properties
new file mode 100644
index 0000000000..ac095e1cab
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/main/resources/application.properties
@@ -0,0 +1,19 @@
+### server port
+server.port=8080
+#port used to expose actuator
+management.port=8081
+#CIDR allowed to hit actuator
+management.address=127.0.0.1
+# Actuator Configuration
+# customize /beans endpoint
+endpoints.beans.id=springbeans
+endpoints.beans.sensitive=false
+endpoints.beans.enabled=true
+# for the Spring Boot version 1.5.0 and above, we have to disable security to expose the health endpoint fully for unauthorized access.
+# see: https://docs.spring.io/spring-boot/docs/1.5.x/reference/html/production-ready-monitoring.html
+management.security.enabled=false
+endpoints.health.sensitive=false
+# customize /info endpoint
+info.app.name=Spring Sample Application
+info.app.description=This is my first spring boot application
+info.app.version=1.0.0
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java
new file mode 100644
index 0000000000..663e6055c7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/CustomEndpointIntegrationTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.actuator;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
+public class CustomEndpointIntegrationTest {
+
+ @LocalManagementPort
+ private int port;
+
+ private RestTemplate restTemplate = new RestTemplate();
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenActuatorCustomEndpointWorks() throws IOException {
+ ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/customEndpoint", String.class);
+
+ assertThat(entity.getStatusCode(), is(HttpStatus.OK));
+
+ List response = objectMapper.readValue(entity.getBody(), new TypeReference>() {
+ });
+
+ assertThat(response, hasItems("This is message 1", "This is message 2"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java
new file mode 100644
index 0000000000..f80e2745a0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckIntegrationTest.java
@@ -0,0 +1,49 @@
+package com.baeldung.actuator;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapContaining.hasKey;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
+public class HealthCheckIntegrationTest {
+
+ @LocalManagementPort
+ private int port;
+
+ private RestTemplate restTemplate = new RestTemplate();
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenActuatorHealthEndpointWorks() throws IOException {
+ ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/health", String.class);
+
+ assertThat(entity.getStatusCode(), is(HttpStatus.OK));
+
+ Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() {
+ });
+
+ assertThat(response, hasEntry("status", "UP"));
+ assertThat(response, hasKey("myHealthCheck"));
+ assertThat(response, hasKey("diskSpace"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java
new file mode 100644
index 0000000000..a464e51b1f
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/HealthCheckUnitTest.java
@@ -0,0 +1,35 @@
+package com.baeldung.actuator;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.Status;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class HealthCheckUnitTest {
+
+ @Test
+ public void whenCheckMethodReturnsZero_thenHealthMethodReturnsStatusUP() {
+ HealthCheck healthCheck = Mockito.spy(new HealthCheck());
+ when(healthCheck.check()).thenReturn(0);
+ Health health = healthCheck.health();
+
+ assertThat(health.getStatus(), is(Status.UP));
+ }
+
+ @Test
+ public void whenCheckMethodReturnsOtherThanZero_thenHealthMethodReturnsStatusDOWN() {
+ HealthCheck healthCheck = Mockito.spy(new HealthCheck());
+ when(healthCheck.check()).thenReturn(-1);
+ Health health = healthCheck.health();
+
+ assertThat(health.getStatus(), is(Status.DOWN));
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java
new file mode 100644
index 0000000000..851de81d7f
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceIntegrationTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.actuator;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasEntry;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
+public class LoginServiceIntegrationTest {
+
+ @LocalManagementPort
+ private int port;
+
+ @Autowired
+ private LoginServiceImpl loginService;
+
+ private RestTemplate restTemplate = new RestTemplate();
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Test
+ public void whenLoginIsAdmin_thenSuccessCounterIsIncremented() throws IOException {
+ boolean success = loginService.login("admin", "secret".toCharArray());
+ ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class);
+ Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() {
+ });
+
+ assertThat(success, is(true));
+ assertThat(entity.getStatusCode(), is(HttpStatus.OK));
+ assertThat(response, hasEntry("counter.login.success", 1));
+ }
+
+ @Test
+ public void whenLoginIsNotAdmin_thenFailureCounterIsIncremented() throws IOException {
+ boolean success = loginService.login("user", "notsecret".toCharArray());
+ ResponseEntity entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class);
+ Map response = objectMapper.readValue(entity.getBody(), new TypeReference>() {
+ });
+
+ assertThat(success, is(false));
+ assertThat(entity.getStatusCode(), is(HttpStatus.OK));
+ assertThat(response, hasEntry("counter.login.failure", 1));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java
new file mode 100644
index 0000000000..489d005782
--- /dev/null
+++ b/spring-boot-modules/spring-boot-1/src/test/java/com/baeldung/actuator/LoginServiceUnitTest.java
@@ -0,0 +1,41 @@
+package com.baeldung.actuator;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.metrics.CounterService;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LoginServiceImpl.class)
+public class LoginServiceUnitTest {
+
+ @MockBean
+ CounterService counterService;
+
+ @Autowired
+ LoginServiceImpl loginService;
+
+ @Test
+ public void whenLoginUserIsAdmin_thenSuccessCounterIsIncremented() {
+ boolean loginResult = loginService.login("admin", "secret".toCharArray());
+ assertThat(loginResult, is(true));
+ verify(counterService, times(1)).increment("counter.login.success");
+ }
+
+ @Test
+ public void whenLoginUserIsNotAdmin_thenFailureCounterIsIncremented() {
+ boolean loginResult = loginService.login("user", "notsecret".toCharArray());
+ assertThat(loginResult, is(false));
+ verify(counterService, times(1)).increment("counter.login.failure");
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml
index c29c1a738b..68d4ec4b8f 100644
--- a/spring-boot-modules/spring-boot-keycloak/pom.xml
+++ b/spring-boot-modules/spring-boot-keycloak/pom.xml
@@ -11,9 +11,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
@@ -76,7 +76,7 @@
- 3.3.0.Final
+ 10.0.1
diff --git a/spring-boot-modules/spring-boot-kotlin/pom.xml b/spring-boot-modules/spring-boot-kotlin/pom.xml
index 79d62645da..7ee048546a 100644
--- a/spring-boot-modules/spring-boot-kotlin/pom.xml
+++ b/spring-boot-modules/spring-boot-kotlin/pom.xml
@@ -14,38 +14,6 @@
../../parent-kotlin
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- true
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/milestone
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- true
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/milestone
-
-
-
org.jetbrains.kotlin
@@ -142,9 +110,9 @@
1.3.31
- 1.0.0.M1
- 1.0.0.M7
- 1.0.0.BUILD-SNAPSHOT
+ 1.0.0.RELEASE
+ 0.8.2.RELEASE
+ 0.8.4.RELEASE
1.2.1
diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt
index 363090abac..464ed2773a 100644
--- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt
+++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/controller/ProductControllerCoroutines.kt
@@ -2,12 +2,11 @@ package com.baeldung.nonblockingcoroutines.controller
import com.baeldung.nonblockingcoroutines.model.Product
import com.baeldung.nonblockingcoroutines.repository.ProductRepositoryCoroutines
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType.APPLICATION_JSON
@@ -15,7 +14,6 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBody
-import org.springframework.web.reactive.function.client.awaitExchange
class ProductControllerCoroutines {
@Autowired
@@ -38,7 +36,7 @@ class ProductControllerCoroutines {
webClient.get()
.uri("/stock-service/product/$id/quantity")
.accept(APPLICATION_JSON)
- .awaitExchange().awaitBody()
+ .retrieve().awaitBody()
}
ProductStockView(product.await()!!, quantity.await())
}
diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt
index 41c4510e0d..e05b718e64 100644
--- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt
+++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/handlers/ProductsHandler.kt
@@ -12,7 +12,6 @@ import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBody
-import org.springframework.web.reactive.function.client.awaitExchange
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyAndAwait
@@ -37,7 +36,7 @@ class ProductsHandler(
webClient.get()
.uri("/stock-service/product/$id/quantity")
.accept(MediaType.APPLICATION_JSON)
- .awaitExchange().awaitBody()
+ .retrieve().awaitBody()
}
return ServerResponse.ok().json().bodyAndAwait(ProductStockView(product.await()!!, quantity.await()))
}
diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt
index 20c3827c26..64ffd014ad 100644
--- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt
+++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepository.kt
@@ -1,7 +1,7 @@
package com.baeldung.nonblockingcoroutines.repository
import com.baeldung.nonblockingcoroutines.model.Product
-import org.springframework.data.r2dbc.function.DatabaseClient
+import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@@ -10,7 +10,7 @@ import reactor.core.publisher.Mono
class ProductRepository(private val client: DatabaseClient) {
fun getProductById(id: Int): Mono {
- return client.execute().sql("SELECT * FROM products WHERE id = $1")
+ return client.execute("SELECT * FROM products WHERE id = $1")
.bind(0, id)
.`as`(Product::class.java)
.fetch()
@@ -18,8 +18,7 @@ class ProductRepository(private val client: DatabaseClient) {
}
fun addNewProduct(name: String, price: Float): Mono {
- return client.execute()
- .sql("INSERT INTO products (name, price) VALUES($1, $2)")
+ return client.execute("INSERT INTO products (name, price) VALUES($1, $2)")
.bind(0, name)
.bind(1, price)
.then()
diff --git a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt
index 60a19d4d00..f2667ec033 100644
--- a/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt
+++ b/spring-boot-modules/spring-boot-kotlin/src/main/kotlin/com/baeldung/nonblockingcoroutines/repository/ProductRepositoryCoroutines.kt
@@ -6,14 +6,14 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.reactive.awaitFirstOrNull
import kotlinx.coroutines.reactive.flow.asFlow
-import org.springframework.data.r2dbc.function.DatabaseClient
+import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Repository
@Repository
class ProductRepositoryCoroutines(private val client: DatabaseClient) {
suspend fun getProductById(id: Int): Product? =
- client.execute().sql("SELECT * FROM products WHERE id = $1")
+ client.execute("SELECT * FROM products WHERE id = $1")
.bind(0, id)
.`as`(Product::class.java)
.fetch()
@@ -21,8 +21,7 @@ class ProductRepositoryCoroutines(private val client: DatabaseClient) {
.awaitFirstOrNull()
suspend fun addNewProduct(name: String, price: Float) =
- client.execute()
- .sql("INSERT INTO products (name, price) VALUES($1, $2)")
+ client.execute("INSERT INTO products (name, price) VALUES($1, $2)")
.bind(0, name)
.bind(1, price)
.then()
diff --git a/spring-boot-modules/spring-boot-libraries/pom.xml b/spring-boot-modules/spring-boot-libraries/pom.xml
index 090967d8a8..189eb4cf1a 100644
--- a/spring-boot-modules/spring-boot-libraries/pom.xml
+++ b/spring-boot-modules/spring-boot-libraries/pom.xml
@@ -87,7 +87,35 @@
javase
${zxing.version}
-
+
+
+ com.github.vladimir-bukhtoyarov
+ bucket4j-core
+ ${bucket4j.version}
+
+
+ com.giffing.bucket4j.spring.boot.starter
+ bucket4j-spring-boot-starter
+ ${bucket4j-spring-boot-starter.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ javax.cache
+ cache-api
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ ${caffeine.version}
+
+
+ com.github.ben-manes.caffeine
+ jcache
+ ${caffeine.version}
+
@@ -200,6 +228,9 @@
2.1
2.6.0
3.3.0
+ 4.10.0
+ 0.2.0
+ 2.8.2
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java
new file mode 100644
index 0000000000..f16d347f85
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jRateLimitApp.java
@@ -0,0 +1,21 @@
+package com.baeldung.ratelimiting.bootstarterapp;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cache.annotation.EnableCaching;
+
+@SpringBootApplication(scanBasePackages = "com.baeldung.ratelimiting", exclude = {
+ DataSourceAutoConfiguration.class,
+ SecurityAutoConfiguration.class,
+})
+@EnableCaching
+public class Bucket4jRateLimitApp {
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(Bucket4jRateLimitApp.class)
+ .properties("spring.config.location=classpath:ratelimiting/application-bucket4j-starter.yml")
+ .run(args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java
new file mode 100644
index 0000000000..bb179b9b38
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitApp.java
@@ -0,0 +1,35 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.baeldung.ratelimiting.bucket4japp.interceptor.RateLimitInterceptor;
+
+@SpringBootApplication(scanBasePackages = "com.baeldung.ratelimiting", exclude = {
+ DataSourceAutoConfiguration.class,
+ SecurityAutoConfiguration.class
+})
+public class Bucket4jRateLimitApp implements WebMvcConfigurer {
+
+ @Autowired
+ @Lazy
+ private RateLimitInterceptor interceptor;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(interceptor)
+ .addPathPatterns("/api/v1/area/**");
+ }
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(Bucket4jRateLimitApp.class)
+ .properties("spring.config.location=classpath:ratelimiting/application-bucket4j.yml")
+ .run(args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java
new file mode 100644
index 0000000000..8a18d6c2b5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/interceptor/RateLimitInterceptor.java
@@ -0,0 +1,57 @@
+package com.baeldung.ratelimiting.bucket4japp.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlanService;
+
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.ConsumptionProbe;
+
+@Component
+public class RateLimitInterceptor implements HandlerInterceptor {
+
+ private static final String HEADER_API_KEY = "X-api-key";
+ private static final String HEADER_LIMIT_REMAINING = "X-Rate-Limit-Remaining";
+ private static final String HEADER_RETRY_AFTER = "X-Rate-Limit-Retry-After-Seconds";
+
+ @Autowired
+ private PricingPlanService pricingPlanService;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+
+ String apiKey = request.getHeader(HEADER_API_KEY);
+
+ if (apiKey == null || apiKey.isEmpty()) {
+ response.sendError(HttpStatus.BAD_REQUEST.value(), "Missing Header: " + HEADER_API_KEY);
+ return false;
+ }
+
+ Bucket tokenBucket = pricingPlanService.resolveBucket(apiKey);
+
+ ConsumptionProbe probe = tokenBucket.tryConsumeAndReturnRemaining(1);
+
+ if (probe.isConsumed()) {
+
+ response.addHeader(HEADER_LIMIT_REMAINING, String.valueOf(probe.getRemainingTokens()));
+ return true;
+
+ } else {
+
+ long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
+
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.addHeader(HEADER_RETRY_AFTER, String.valueOf(waitForRefill));
+ response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), "You have exhausted your API Request Quota"); // 429
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java
new file mode 100644
index 0000000000..27c30ba3a0
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlan.java
@@ -0,0 +1,42 @@
+package com.baeldung.ratelimiting.bucket4japp.service;
+
+import java.time.Duration;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Refill;
+
+public enum PricingPlan {
+
+ FREE(20),
+
+ BASIC(40),
+
+ PROFESSIONAL(100);
+
+ private int bucketCapacity;
+
+ private PricingPlan(int bucketCapacity) {
+ this.bucketCapacity = bucketCapacity;
+ }
+
+ Bandwidth getLimit() {
+ return Bandwidth.classic(bucketCapacity, Refill.intervally(bucketCapacity, Duration.ofHours(1)));
+ }
+
+ public int bucketCapacity() {
+ return bucketCapacity;
+ }
+
+ static PricingPlan resolvePlanFromApiKey(String apiKey) {
+ if (apiKey == null || apiKey.isEmpty()) {
+ return FREE;
+
+ } else if (apiKey.startsWith("PX001-")) {
+ return PROFESSIONAL;
+
+ } else if (apiKey.startsWith("BX001-")) {
+ return BASIC;
+ }
+ return FREE;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java
new file mode 100644
index 0000000000..7d8a718601
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/bucket4japp/service/PricingPlanService.java
@@ -0,0 +1,31 @@
+package com.baeldung.ratelimiting.bucket4japp.service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.stereotype.Service;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.Bucket4j;
+
+@Service
+public class PricingPlanService {
+
+ private final Map cache = new ConcurrentHashMap<>();
+
+ public Bucket resolveBucket(String apiKey) {
+ return cache.computeIfAbsent(apiKey, this::newBucket);
+ }
+
+ private Bucket newBucket(String apiKey) {
+ PricingPlan pricingPlan = PricingPlan.resolvePlanFromApiKey(apiKey);
+ return bucket(pricingPlan.getLimit());
+ }
+
+ private Bucket bucket(Bandwidth limit) {
+ return Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java
new file mode 100644
index 0000000000..f3fb63ebdd
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/controller/AreaCalculationController.java
@@ -0,0 +1,29 @@
+package com.baeldung.ratelimiting.controller;
+
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.ratelimiting.dto.AreaV1;
+import com.baeldung.ratelimiting.dto.RectangleDimensionsV1;
+import com.baeldung.ratelimiting.dto.TriangleDimensionsV1;
+
+@RestController
+@RequestMapping(value = "/api/v1/area", consumes = MediaType.APPLICATION_JSON_VALUE)
+class AreaCalculationController {
+
+ @PostMapping(value = "/rectangle", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity rectangle(@RequestBody RectangleDimensionsV1 dimensions) {
+
+ return ResponseEntity.ok(new AreaV1("rectangle", dimensions.getLength() * dimensions.getWidth()));
+ }
+
+ @PostMapping(value = "/triangle", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity triangle(@RequestBody TriangleDimensionsV1 dimensions) {
+
+ return ResponseEntity.ok(new AreaV1("triangle", 0.5d * dimensions.getHeight() * dimensions.getBase()));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java
new file mode 100644
index 0000000000..78097f55b2
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/AreaV1.java
@@ -0,0 +1,20 @@
+package com.baeldung.ratelimiting.dto;
+
+public class AreaV1 {
+
+ private String shape;
+ private Double area;
+
+ public AreaV1(String shape, Double area) {
+ this.area = area;
+ this.shape = shape;
+ }
+
+ public Double getArea() {
+ return area;
+ }
+
+ public String getShape() {
+ return shape;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java
new file mode 100644
index 0000000000..e3c17e1ba7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/RectangleDimensionsV1.java
@@ -0,0 +1,15 @@
+package com.baeldung.ratelimiting.dto;
+
+public class RectangleDimensionsV1 {
+
+ private double length;
+ private double width;
+
+ public double getLength() {
+ return length;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java
new file mode 100644
index 0000000000..44c954bded
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/java/com/baeldung/ratelimiting/dto/TriangleDimensionsV1.java
@@ -0,0 +1,15 @@
+package com.baeldung.ratelimiting.dto;
+
+public class TriangleDimensionsV1 {
+
+ private double base;
+ private double height;
+
+ public double getBase() {
+ return base;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml
new file mode 100644
index 0000000000..ecc9f22e0a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j-starter.yml
@@ -0,0 +1,40 @@
+server:
+ port: 9001
+
+spring:
+ application:
+ name: bucket4j-starter-api-rate-limit-app
+ mvc:
+ throw-exception-if-no-handler-found: true
+ resources:
+ add-mappings: false
+ cache:
+ cache-names:
+ - rate-limit-buckets
+ caffeine:
+ spec: maximumSize=100000,expireAfterAccess=3600s
+
+bucket4j:
+ enabled: true
+ filters:
+ - cache-name: rate-limit-buckets
+ url: /api/v1/area.*
+ http-response-body: "{ \"status\": 429, \"error\": \"Too Many Requests\", \"message\": \"You have exhausted your API Request Quota\" }"
+ rate-limits:
+ - expression: "getHeader('X-api-key')"
+ execute-condition: "getHeader('X-api-key').startsWith('PX001-')"
+ bandwidths:
+ - capacity: 100
+ time: 1
+ unit: hours
+ - expression: "getHeader('X-api-key')"
+ execute-condition: "getHeader('X-api-key').startsWith('BX001-')"
+ bandwidths:
+ - capacity: 40
+ time: 1
+ unit: hours
+ - expression: "getHeader('X-api-key')"
+ bandwidths:
+ - capacity: 20
+ time: 1
+ unit: hours
diff --git a/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml
new file mode 100644
index 0000000000..ae19622d9b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/main/resources/ratelimiting/application-bucket4j.yml
@@ -0,0 +1,10 @@
+server:
+ port: 9000
+
+spring:
+ application:
+ name: bucket4j-api-rate-limit-app
+ mvc:
+ throw-exception-if-no-handler-found: true
+ resources:
+ add-mappings: false
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java
new file mode 100644
index 0000000000..d93e61988b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bootstarterapp/Bucket4jBootStarterRateLimitIntegrationTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.ratelimiting.bootstarterapp;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Bucket4jRateLimitApp.class)
+@TestPropertySource(properties = "spring.config.location=classpath:ratelimiting/application-bucket4j-starter.yml")
+@AutoConfigureMockMvc
+public class Bucket4jBootStarterRateLimitIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void givenTriangleAreaCalculator_whenRequestsWithinRateLimit_thenAccepted() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/triangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"height\": 8, \"base\": 10 }")
+ .header("X-api-key", "FX001-UBSZ5YRYQ");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request)
+ .andExpect(status().isOk())
+ .andExpect(header().exists("X-Rate-Limit-Remaining"))
+ .andExpect(jsonPath("$.shape", equalTo("triangle")))
+ .andExpect(jsonPath("$.area", equalTo(40d)));
+ }
+ }
+
+ @Test
+ public void givenTriangleAreaCalculator_whenRequestRateLimitTriggered_thenRejected() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/triangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"height\": 8, \"base\": 10 }")
+ .header("X-api-key", "FX001-ZBSY6YSLP");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request); // exhaust limit
+ }
+
+ mockMvc.perform(request)
+ .andExpect(status().isTooManyRequests())
+ .andExpect(jsonPath("$.message", equalTo("You have exhausted your API Request Quota")))
+ .andExpect(header().exists("X-Rate-Limit-Retry-After-Seconds"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java
new file mode 100644
index 0000000000..20f57a7021
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jRateLimitIntegrationTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Bucket4jRateLimitApp.class)
+@AutoConfigureMockMvc
+public class Bucket4jRateLimitIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void givenRectangleAreaCalculator_whenRequestsWithinRateLimit_thenAccepted() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/rectangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"length\": 12, \"width\": 10 }")
+ .header("X-api-key", "FX001-UBSZ5YRYQ");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request)
+ .andExpect(status().isOk())
+ .andExpect(header().exists("X-Rate-Limit-Remaining"))
+ .andExpect(jsonPath("$.shape", equalTo("rectangle")))
+ .andExpect(jsonPath("$.area", equalTo(120d)));
+ }
+ }
+
+ @Test
+ public void givenReactangleAreaCalculator_whenRequestRateLimitTriggered_thenRejected() throws Exception {
+
+ RequestBuilder request = post("/api/v1/area/rectangle").contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("{ \"length\": 12, \"width\": 10 }")
+ .header("X-api-key", "FX001-ZBSY6YSLP");
+
+ for (int i = 1; i <= PricingPlan.FREE.bucketCapacity(); i++) {
+ mockMvc.perform(request); // exhaust limit
+ }
+
+ mockMvc.perform(request)
+ .andExpect(status().isTooManyRequests())
+ .andExpect(status().reason("You have exhausted your API Request Quota"))
+ .andExpect(header().exists("X-Rate-Limit-Retry-After-Seconds"));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java
new file mode 100644
index 0000000000..fbf63ba403
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/Bucket4jUsageUnitTest.java
@@ -0,0 +1,82 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Bucket;
+import io.github.bucket4j.Bucket4j;
+import io.github.bucket4j.Refill;
+
+public class Bucket4jUsageUnitTest {
+
+ @Test
+ public void givenBucketLimit_whenExceedLimit_thenConsumeReturnsFalse() {
+ Refill refill = Refill.intervally(10, Duration.ofMinutes(1));
+ Bandwidth limit = Bandwidth.classic(10, refill);
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+
+ for (int i = 1; i <= 10; i++) {
+ assertTrue(bucket.tryConsume(1));
+ }
+ assertFalse(bucket.tryConsume(1));
+ }
+
+ @Test
+ public void givenMultipletLimits_whenExceedSmallerLimit_thenConsumeReturnsFalse() {
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(Bandwidth.classic(10, Refill.intervally(10, Duration.ofMinutes(1))))
+ .addLimit(Bandwidth.classic(5, Refill.intervally(5, Duration.ofSeconds(20))))
+ .build();
+
+ for (int i = 1; i <= 5; i++) {
+ assertTrue(bucket.tryConsume(1));
+ }
+ assertFalse(bucket.tryConsume(1));
+ }
+
+ @Test
+ public void givenBucketLimit_whenThrottleRequests_thenConsumeReturnsTrue() throws InterruptedException {
+ Refill refill = Refill.intervally(1, Duration.ofSeconds(2));
+ Bandwidth limit = Bandwidth.classic(1, refill);
+ Bucket bucket = Bucket4j.builder()
+ .addLimit(limit)
+ .build();
+
+ assertTrue(bucket.tryConsume(1));
+
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+ CountDownLatch latch = new CountDownLatch(1);
+
+ executor.schedule(new AssertTryConsume(bucket, latch), 2, TimeUnit.SECONDS);
+
+ latch.await();
+ }
+
+ static class AssertTryConsume implements Runnable {
+
+ private Bucket bucket;
+ private CountDownLatch latch;
+
+ AssertTryConsume(Bucket bucket, CountDownLatch latch) {
+ this.bucket = bucket;
+ this.latch = latch;
+ }
+
+ @Override
+ public void run() {
+ assertTrue(bucket.tryConsume(1));
+ latch.countDown();
+ }
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java
new file mode 100644
index 0000000000..325b898779
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries/src/test/java/com/baeldung/ratelimiting/bucket4japp/PricingPlanServiceUnitTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.ratelimiting.bucket4japp;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlan;
+import com.baeldung.ratelimiting.bucket4japp.service.PricingPlanService;
+
+import io.github.bucket4j.Bucket;
+
+public class PricingPlanServiceUnitTest {
+
+ private PricingPlanService service = new PricingPlanService();
+
+ @Test
+ public void givenAPIKey_whenFreePlan_thenReturnFreePlanBucket() {
+ Bucket bucket = service.resolveBucket("FX001-UBSZ5YRYQ");
+
+ assertEquals(PricingPlan.FREE.bucketCapacity(), bucket.getAvailableTokens());
+ }
+
+ @Test
+ public void givenAPIKey_whenBasiclan_thenReturnBasicPlanBucket() {
+ Bucket bucket = service.resolveBucket("BX001-MBSZ5YRYP");
+
+ assertEquals(PricingPlan.BASIC.bucketCapacity(), bucket.getAvailableTokens());
+ }
+
+ @Test
+ public void givenAPIKey_whenProfessionalPlan_thenReturnProfessionalPlanBucket() {
+ Bucket bucket = service.resolveBucket("PX001-NBSZ5YRYY");
+
+ assertEquals(PricingPlan.PROFESSIONAL.bucketCapacity(), bucket.getAvailableTokens());
+ }
+}
diff --git a/spring-boot-modules/spring-boot-mvc-birt/pom.xml b/spring-boot-modules/spring-boot-mvc-birt/pom.xml
index f65b851f30..0e8e231a84 100644
--- a/spring-boot-modules/spring-boot-mvc-birt/pom.xml
+++ b/spring-boot-modules/spring-boot-mvc-birt/pom.xml
@@ -75,7 +75,6 @@
- 2.1.1.RELEASE
com.baeldung.birt.engine.ReportEngineApplication
1.8
1.8
diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java
new file mode 100644
index 0000000000..60ba4cc108
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/ListsPropertiesUnitTest.java
@@ -0,0 +1,88 @@
+package com.baeldung.properties.lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = SpringListPropertiesApplication.class)
+public class ListsPropertiesUnitTest {
+
+ @Value("${arrayOfStrings}")
+ private String[] arrayOfStrings;
+
+ @Value("${arrayOfStrings}")
+ private List unexpectedListOfStrings;
+
+ @Value("#{'${arrayOfStrings}'.split(',')}")
+ private List listOfStrings;
+
+ @Value("#{${listOfStrings}}")
+ private List listOfStringsV2;
+
+ @Value("#{'${listOfStringsWithCustomDelimiter}'.split(';')}")
+ private List listOfStringsWithCustomDelimiter;
+
+ @Value("#{'${listOfBooleans}'.split(',')}")
+ private List listOfBooleans;
+
+ @Value("#{'${listOfIntegers}'.split(',')}")
+ private List listOfIntegers;
+
+ @Value("#{'${listOfCharacters}'.split(',')}")
+ private List listOfCharacters;
+
+ @Autowired
+ private Environment environment;
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedArrayContainsExpectedValues() {
+ assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings);
+ }
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedListContainsUnexpectedValues() {
+ assertEquals(Collections.singletonList("Baeldung,dot,com"), unexpectedListOfStrings);
+ }
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedListContainsExpectedValues() {
+ assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings);
+ }
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedListV2ContainsExpectedValues() {
+ assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsV2);
+ }
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedListWithCustomDelimiterContainsExpectedValues() {
+ assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsWithCustomDelimiter);
+ }
+
+ @Test
+ public void whenContextIsInitialized_thenInjectedListOfBasicTypesContainsExpectedValues() {
+ assertEquals(Arrays.asList(false, false, true), listOfBooleans);
+ assertEquals(Arrays.asList(1, 2, 3, 4), listOfIntegers);
+ assertEquals(Arrays.asList('a', 'b', 'c'), listOfCharacters);
+ }
+
+ @Test
+ public void whenReadingFromSpringEnvironment_thenPropertiesHaveExpectedValues() {
+ String[] arrayOfStrings = environment.getProperty("arrayOfStrings", String[].class);
+ List listOfStrings = (List)environment.getProperty("arrayOfStrings", List.class);
+
+ assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings);
+ assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java
new file mode 100644
index 0000000000..8a66079201
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/java/com/baeldung/properties/lists/SpringListPropertiesApplication.java
@@ -0,0 +1,10 @@
+package com.baeldung.properties.lists;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource(value = "lists.properties")
+public class SpringListPropertiesApplication {
+
+}
diff --git a/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties b/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties
new file mode 100644
index 0000000000..cc54d699a7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties-2/src/test/resources/lists.properties
@@ -0,0 +1,6 @@
+arrayOfStrings=Baeldung,dot,com
+listOfStrings={'Baeldung','dot','com'}
+listOfStringsWithCustomDelimiter=Baeldung;dot;com
+listOfBooleans=false,false,true
+listOfIntegers=1,2,3,4
+listOfCharacters=a,b,c
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-properties/.dockerignore b/spring-boot-modules/spring-boot-properties/.dockerignore
new file mode 100644
index 0000000000..df36044e46
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties/.dockerignore
@@ -0,0 +1,13 @@
+# Logs
+logs
+*.log
+
+# Git
+.git
+.cache
+
+# Classes
+**/*.class
+
+# Ignore md files
+*.md
diff --git a/spring-boot-modules/spring-boot-properties/Dockerfile b/spring-boot-modules/spring-boot-properties/Dockerfile
new file mode 100644
index 0000000000..d6bd2a95ae
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties/Dockerfile
@@ -0,0 +1,10 @@
+FROM maven:3.6.0-jdk-11
+WORKDIR /code/spring-boot-modules/spring-boot-properties/
+COPY ./spring-boot-modules/spring-boot-properties/pom.xml .
+COPY ./spring-boot-modules/spring-boot-properties/src ./src
+COPY ./parent-boot-2/pom.xml /code/parent-boot-2/pom.xml
+COPY ./pom.xml /code/pom.xml
+COPY ./custom-pmd-0.0.1.jar /code/custom-pmd-0.0.1.jar
+COPY ./baeldung-pmd-rules.xml /code/baeldung-pmd-rules.xml
+RUN mvn dependency:resolve
+CMD ["mvn", "spring-boot:run"]
\ 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 ef9c084f4c..98d328bd19 100644
--- a/spring-boot-modules/spring-boot-properties/pom.xml
+++ b/spring-boot-modules/spring-boot-properties/pom.xml
@@ -128,7 +128,8 @@
4.4.11
@
2.2.4.RELEASE
- com.baeldung.buildproperties.Application
+
+ com.baeldung.yaml.MyApplication
diff --git a/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml b/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml
index 6fc6f67cd0..4914ff15f7 100644
--- a/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml
+++ b/spring-boot-modules/spring-boot-properties/src/main/resources/application.yml
@@ -1,7 +1,14 @@
+spring:
+ profiles:
+ active:
+ - test
+
+---
+
spring:
profiles: test
name: test-YAML
-environment: test
+environment: testing
servers:
- www.abc.test.com
- www.xyz.test.com
@@ -15,3 +22,13 @@ environment: production
servers:
- www.abc.com
- www.xyz.com
+
+---
+
+spring:
+ profiles: dev
+name: ${DEV_NAME:dev-YAML}
+environment: development
+servers:
+ - www.abc.dev.com
+ - www.xyz.dev.com
diff --git a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLDevIntegrationTest.java b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLDevIntegrationTest.java
new file mode 100644
index 0000000000..8dfc4c2208
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLDevIntegrationTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.yaml;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.jupiter.api.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.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = MyApplication.class)
+@TestPropertySource(properties = {"spring.profiles.active = dev"})
+class YAMLDevIntegrationTest {
+
+ @Autowired
+ private YAMLConfig config;
+
+ @Test
+ void whenProfileTest_thenNameTesting() {
+ assertTrue("development".equalsIgnoreCase(config.getEnvironment()));
+ assertTrue("dev-YAML".equalsIgnoreCase(config.getName()));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLIntegrationTest.java b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLIntegrationTest.java
new file mode 100644
index 0000000000..090d5c592e
--- /dev/null
+++ b/spring-boot-modules/spring-boot-properties/src/test/java/com/baeldung/yaml/YAMLIntegrationTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.yaml;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.jupiter.api.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.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = MyApplication.class)
+class YAMLIntegrationTest {
+
+ @Autowired
+ private YAMLConfig config;
+
+ @Test
+ void whenProfileTest_thenNameTesting() {
+ assertTrue("testing".equalsIgnoreCase(config.getEnvironment()));
+ assertTrue("test-YAML".equalsIgnoreCase(config.getName()));
+ }
+}
diff --git a/spring-boot-rest/pom.xml b/spring-boot-rest/pom.xml
index a8500d50f2..10dacf99e8 100644
--- a/spring-boot-rest/pom.xml
+++ b/spring-boot-rest/pom.xml
@@ -79,11 +79,6 @@
modelmapper
${modelmapper.version}
-
- net.bytebuddy
- byte-buddy
- ${byte-buddy.version}
-
diff --git a/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java
similarity index 97%
rename from spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java
rename to spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java
index e02e5da246..c83d4f9e96 100644
--- a/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java
+++ b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationTest.java
@@ -17,7 +17,7 @@ import com.baeldung.multiplecachemanager.repository.OrderDetailRepository;
@SpringBootApplication
@SpringBootTest
-public class MultipleCacheManagerIntegrationUnitTest {
+public class MultipleCacheManagerIntegrationTest {
@MockBean
private OrderDetailRepository orderDetailRepository;
diff --git a/spring-cloud-bus/pom.xml b/spring-cloud-bus/pom.xml
index 513c8bade6..ec56e23ac7 100644
--- a/spring-cloud-bus/pom.xml
+++ b/spring-cloud-bus/pom.xml
@@ -11,9 +11,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../parent-boot-1
+ ../parent-boot-2
@@ -34,7 +34,7 @@
- Brixton.SR7
+ Hoxton.SR4
diff --git a/spring-cloud-bus/spring-cloud-config-client/pom.xml b/spring-cloud-bus/spring-cloud-config-client/pom.xml
index 7e1185415b..cc1c237646 100644
--- a/spring-cloud-bus/spring-cloud-config-client/pom.xml
+++ b/spring-cloud-bus/spring-cloud-config-client/pom.xml
@@ -33,6 +33,11 @@
org.springframework.boot
spring-boot-actuator
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+
org.springframework.cloud
diff --git a/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml b/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml
index 547e0284f3..fbbc6d138f 100644
--- a/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml
+++ b/spring-cloud-bus/spring-cloud-config-client/src/main/resources/application.yml
@@ -4,4 +4,14 @@ spring:
host: localhost
port: 5672
username: guest
- password: guest
\ No newline at end of file
+ password: guest
+ cloud:
+ bus:
+ enabled: true
+ refresh:
+ enabled: true
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "*"
\ No newline at end of file
diff --git a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties
index 4c18c192c0..6d7a945612 100644
--- a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties
+++ b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/application.properties
@@ -1,11 +1,8 @@
server.port=8888
spring.cloud.config.server.git.uri=
-security.user.name=root
-security.user.password=s3cr3t
-encrypt.key-store.location=classpath:/config-server.jks
-encrypt.key-store.password=my-s70r3-s3cr3t
-encrypt.key-store.alias=config-server-key
-encrypt.key-store.secret=my-k34-s3cr3t
+spring.cloud.bus.enabled=true
+spring.security.user.name=root
+spring.security.user.password=s3cr3t
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
diff --git a/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties
new file mode 100644
index 0000000000..b0c35c72a6
--- /dev/null
+++ b/spring-cloud-bus/spring-cloud-config-server/src/main/resources/bootstrap.properties
@@ -0,0 +1,4 @@
+encrypt.key-store.location=classpath:/config-server.jks
+encrypt.key-store.password=my-s70r3-s3cr3t
+encrypt.key-store.alias=config-server-key
+encrypt.key-store.secret=my-k34-s3cr3t
\ No newline at end of file
diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml
index c4e606e190..3de527c33b 100644
--- a/spring-cloud/pom.xml
+++ b/spring-cloud/pom.xml
@@ -32,6 +32,7 @@
spring-cloud-zuul-eureka-integration
spring-cloud-contract
spring-cloud-kubernetes
+ spring-cloud-open-service-broker
spring-cloud-archaius
spring-cloud-functions
spring-cloud-vault
diff --git a/spring-cloud/spring-cloud-connectors-heroku/pom.xml b/spring-cloud/spring-cloud-connectors-heroku/pom.xml
index 1dad3ddcb7..c09a282197 100644
--- a/spring-cloud/spring-cloud-connectors-heroku/pom.xml
+++ b/spring-cloud/spring-cloud-connectors-heroku/pom.xml
@@ -9,9 +9,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
@@ -35,6 +35,11 @@
org.postgresql
postgresql
+
+ net.bytebuddy
+ byte-buddy-dep
+ ${bytebuddy.version}
+
com.h2database
h2
@@ -55,8 +60,9 @@
- Brixton.SR7
- 9.4-1201-jdbc4
+ Hoxton.SR4
+ 42.2.10
+ 1.10.10
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java
index eb2972f35a..f998059028 100644
--- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java
+++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookController.java
@@ -1,5 +1,7 @@
package com.baeldung.spring.cloud.connectors.heroku.book;
+import java.util.Optional;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -15,7 +17,7 @@ public class BookController {
}
@GetMapping("/{bookId}")
- public Book findBook(@PathVariable Long bookId) {
+ public Optional findBook(@PathVariable Long bookId) {
return bookService.findBookById(bookId);
}
diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java
index 4978ded65f..a83dfe64b7 100644
--- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java
+++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/book/BookService.java
@@ -1,5 +1,7 @@
package com.baeldung.spring.cloud.connectors.heroku.book;
+import java.util.Optional;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -15,8 +17,8 @@ public class BookService {
this.bookRepository = bookRepository;
}
- public Book findBookById(Long bookId) {
- return bookRepository.findOne(bookId);
+ public Optional findBookById(Long bookId) {
+ return bookRepository.findById(bookId);
}
@Transactional(propagation = Propagation.REQUIRED)
diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java
index 51cf4412bf..7875c712f9 100644
--- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java
+++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductController.java
@@ -1,5 +1,7 @@
package com.baeldung.spring.cloud.connectors.heroku.product;
+import java.util.Optional;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -15,7 +17,7 @@ public class ProductController {
}
@GetMapping("/{productId}")
- public Product findProduct(@PathVariable Long productId) {
+ public Optional findProduct(@PathVariable Long productId) {
return productService.findProductById(productId);
}
diff --git a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java
index f25b4ecf7b..bdd13e9863 100644
--- a/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java
+++ b/spring-cloud/spring-cloud-connectors-heroku/src/main/java/com/baeldung/spring/cloud/connectors/heroku/product/ProductService.java
@@ -1,5 +1,7 @@
package com.baeldung.spring.cloud.connectors.heroku.product;
+import java.util.Optional;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -15,8 +17,8 @@ public class ProductService {
this.productRepository = productRepository;
}
- public Product findProductById(Long productId) {
- return productRepository.findOne(productId);
+ public Optional findProductById(Long productId) {
+ return productRepository.findById(productId);
}
@Transactional(propagation = Propagation.REQUIRED)
diff --git a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml
index 66d8f096ce..f0d34d2231 100644
--- a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml
+++ b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/liveness-example/pom.xml
@@ -8,10 +8,10 @@
1.0-SNAPSHOT
- com.baeldung
- parent-boot-1
- 0.0.1-SNAPSHOT
- ../../../../parent-boot-1
+ com.baeldung.spring.cloud
+ spring-cloud-kubernetes
+ 1.0-SNAPSHOT
+ ../../../spring-cloud-kubernetes
@@ -44,7 +44,6 @@
UTF-8
UTF-8
- 1.5.17.RELEASE
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml
index fbb9e09d07..8bfd4d305d 100644
--- a/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml
+++ b/spring-cloud/spring-cloud-kubernetes/kubernetes-selfhealing/readiness-example/pom.xml
@@ -8,10 +8,10 @@
1.0-SNAPSHOT
- com.baeldung
- parent-boot-1
- 0.0.1-SNAPSHOT
- ../../../../parent-boot-1
+ com.baeldung.spring.cloud
+ spring-cloud-kubernetes
+ 1.0-SNAPSHOT
+ ../../../spring-cloud-kubernetes
@@ -44,7 +44,6 @@
UTF-8
UTF-8
- 1.5.17.RELEASE
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-open-service-broker/README.md b/spring-cloud/spring-cloud-open-service-broker/README.md
new file mode 100644
index 0000000000..4084e8ebb2
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Quick Guide to Spring Cloud Open Service Broker](https://www.baeldung.com/spring-cloud-open-service-broker)
diff --git a/spring-cloud/spring-cloud-open-service-broker/pom.xml b/spring-cloud/spring-cloud-open-service-broker/pom.xml
new file mode 100644
index 0000000000..7acd302dc1
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+ com.baeldung
+ spring-cloud-open-service-broker
+ jar
+
+
+ com.baeldung.spring.cloud
+ spring-cloud
+ 1.0.0-SNAPSHOT
+
+
+
+ 3.1.1.RELEASE
+ 2.2.7.RELEASE
+ 3.3.5.RELEASE
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-open-service-broker
+ ${spring-cloud-starter-open-service-broker.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring-boot-starter-web.version}
+
+
+ io.projectreactor
+ reactor-test
+ ${reactor-test.version}
+ test
+
+
+
+
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java
new file mode 100644
index 0000000000..2dbb4bc546
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/ServiceBrokerApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.cloud.openservicebroker;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ServiceBrokerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ServiceBrokerApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java
new file mode 100644
index 0000000000..e9e9452785
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/config/CatalogConfiguration.java
@@ -0,0 +1,34 @@
+package com.baeldung.spring.cloud.openservicebroker.config;
+
+import org.springframework.cloud.servicebroker.model.catalog.Catalog;
+import org.springframework.cloud.servicebroker.model.catalog.Plan;
+import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class CatalogConfiguration {
+
+ @Bean
+ public Catalog catalog() {
+ Plan mailFreePlan = Plan.builder()
+ .id("fd81196c-a414-43e5-bd81-1dbb082a3c55")
+ .name("mail-free-plan")
+ .description("Mail Service Free Plan")
+ .free(true)
+ .build();
+
+ ServiceDefinition serviceDefinition = ServiceDefinition.builder()
+ .id("b92c0ca7-c162-4029-b567-0d92978c0a97")
+ .name("mail-service")
+ .description("Mail Service")
+ .bindable(true)
+ .tags("mail", "service")
+ .plans(mailFreePlan)
+ .build();
+
+ return Catalog.builder()
+ .serviceDefinitions(serviceDefinition)
+ .build();
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java
new file mode 100644
index 0000000000..e0c0b36428
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailController.java
@@ -0,0 +1,19 @@
+package com.baeldung.spring.cloud.openservicebroker.mail;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class MailController {
+
+ @GetMapping("/mail-dashboard/{mailSystemId}")
+ public String dashboard(@PathVariable("mailSystemId") String mailSystemId) {
+ return "Mail Dashboard - " + mailSystemId;
+ }
+
+ @GetMapping("/mail-system/{mailSystemId}")
+ public String mailSystem(@PathVariable("mailSystemId") String mailSystemId) {
+ return "Mail System - " + mailSystemId;
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java
new file mode 100644
index 0000000000..07b7ad9a38
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailService.java
@@ -0,0 +1,95 @@
+package com.baeldung.spring.cloud.openservicebroker.mail;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+@Service
+public class MailService {
+
+ public static final String URI_KEY = "uri";
+ public static final String USERNAME_KEY = "username";
+ public static final String PASSWORD_KEY = "password";
+
+ private final String mailDashboardBaseURL;
+ private final String mailSystemBaseURL;
+
+ private Map mailServices = new HashMap<>();
+
+ private Map mailServiceBindings = new HashMap<>();
+
+ public MailService(@Value("${mail.system.dashboard.base-url}") String mailDashboardBaseURL,
+ @Value("${mail.system.base-url}") String mailSystemBaseURL) {
+ this.mailDashboardBaseURL = mailDashboardBaseURL;
+ this.mailSystemBaseURL = mailSystemBaseURL;
+ }
+
+ public Mono createServiceInstance(String instanceId, String serviceDefinitionId, String planId) {
+ MailServiceInstance mailServiceInstance = new MailServiceInstance(
+ instanceId, serviceDefinitionId, planId, mailDashboardBaseURL + instanceId);
+ mailServices.put(instanceId, mailServiceInstance);
+ return Mono.just(mailServiceInstance);
+ }
+
+ public Mono serviceInstanceExists(String instanceId) {
+ return Mono.just(mailServices.containsKey(instanceId));
+ }
+
+ public Mono getServiceInstance(String instanceId) {
+ if (mailServices.containsKey(instanceId)) {
+ return Mono.just(mailServices.get(instanceId));
+ }
+ return Mono.empty();
+ }
+
+ public Mono deleteServiceInstance(String instanceId) {
+ mailServices.remove(instanceId);
+ mailServiceBindings.remove(instanceId);
+ return Mono.empty();
+ }
+
+ public Mono createServiceBinding(String instanceId, String bindingId) {
+ return this.serviceInstanceExists(instanceId)
+ .flatMap(exists -> {
+ if (exists) {
+ MailServiceBinding mailServiceBinding =
+ new MailServiceBinding(bindingId, buildCredentials(instanceId, bindingId));
+ mailServiceBindings.put(instanceId, mailServiceBinding);
+ return Mono.just(mailServiceBinding);
+ } else {
+ return Mono.empty();
+ }
+ });
+ }
+
+ public Mono serviceBindingExists(String instanceId, String bindingId) {
+ return Mono.just(mailServiceBindings.containsKey(instanceId) &&
+ mailServiceBindings.get(instanceId).getBindingId().equalsIgnoreCase(bindingId));
+ }
+
+ public Mono getServiceBinding(String instanceId, String bindingId) {
+ if (mailServiceBindings.containsKey(instanceId) &&
+ mailServiceBindings.get(instanceId).getBindingId().equalsIgnoreCase(bindingId)) {
+ return Mono.just(mailServiceBindings.get(instanceId));
+ }
+ return Mono.empty();
+ }
+
+ public Mono deleteServiceBinding(String instanceId) {
+ mailServiceBindings.remove(instanceId);
+ return Mono.empty();
+ }
+
+ private Map buildCredentials(String instanceId, String bindingId) {
+ Map credentials = new HashMap<>();
+ credentials.put(URI_KEY, mailSystemBaseURL + instanceId);
+ credentials.put(USERNAME_KEY, bindingId);
+ credentials.put(PASSWORD_KEY, UUID.randomUUID().toString());
+ return credentials;
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java
new file mode 100644
index 0000000000..a72d4f7372
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceBinding.java
@@ -0,0 +1,22 @@
+package com.baeldung.spring.cloud.openservicebroker.mail;
+
+import java.util.Map;
+
+public class MailServiceBinding {
+
+ private String bindingId;
+ private Map credentials;
+
+ public MailServiceBinding(String bindingId, Map credentials) {
+ this.bindingId = bindingId;
+ this.credentials = credentials;
+ }
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public Map getCredentials() {
+ return credentials;
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java
new file mode 100644
index 0000000000..d4dbbe5657
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/mail/MailServiceInstance.java
@@ -0,0 +1,32 @@
+package com.baeldung.spring.cloud.openservicebroker.mail;
+
+public class MailServiceInstance {
+
+ private String instanceId;
+ private String serviceDefinitionId;
+ private String planId;
+ private String dashboardUrl;
+
+ public MailServiceInstance(String instanceId, String serviceDefinitionId, String planId, String dashboardUrl) {
+ this.instanceId = instanceId;
+ this.serviceDefinitionId = serviceDefinitionId;
+ this.planId = planId;
+ this.dashboardUrl = dashboardUrl;
+ }
+
+ public String getInstanceId() {
+ return instanceId;
+ }
+
+ public String getServiceDefinitionId() {
+ return serviceDefinitionId;
+ }
+
+ public String getPlanId() {
+ return planId;
+ }
+
+ public String getDashboardUrl() {
+ return dashboardUrl;
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java
new file mode 100644
index 0000000000..847b309f2c
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingService.java
@@ -0,0 +1,77 @@
+package com.baeldung.spring.cloud.openservicebroker.services;
+
+import com.baeldung.spring.cloud.openservicebroker.mail.MailService;
+import org.springframework.cloud.servicebroker.exception.ServiceInstanceBindingDoesNotExistException;
+import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException;
+import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
+import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest;
+import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceAppBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingRequest;
+import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingResponse;
+import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+@Service
+public class MailServiceInstanceBindingService implements ServiceInstanceBindingService {
+
+ private final MailService mailService;
+
+ public MailServiceInstanceBindingService(MailService mailService) {
+ this.mailService = mailService;
+ }
+
+ @Override
+ public Mono createServiceInstanceBinding(
+ CreateServiceInstanceBindingRequest request) {
+ return Mono.just(CreateServiceInstanceAppBindingResponse.builder())
+ .flatMap(responseBuilder -> mailService.serviceBindingExists(
+ request.getServiceInstanceId(), request.getBindingId())
+ .flatMap(exists -> {
+ if (exists) {
+ return mailService.getServiceBinding(
+ request.getServiceInstanceId(), request.getBindingId())
+ .flatMap(serviceBinding -> Mono.just(responseBuilder
+ .bindingExisted(true)
+ .credentials(serviceBinding.getCredentials())
+ .build()));
+ } else {
+ return mailService.createServiceBinding(
+ request.getServiceInstanceId(), request.getBindingId())
+ .switchIfEmpty(Mono.error(
+ new ServiceInstanceDoesNotExistException(
+ request.getServiceInstanceId())))
+ .flatMap(mailServiceBinding -> Mono.just(responseBuilder
+ .bindingExisted(false)
+ .credentials(mailServiceBinding.getCredentials())
+ .build()));
+ }
+ }));
+ }
+
+ @Override
+ public Mono getServiceInstanceBinding(GetServiceInstanceBindingRequest request) {
+ return mailService.getServiceBinding(request.getServiceInstanceId(), request.getBindingId())
+ .switchIfEmpty(Mono.error(new ServiceInstanceBindingDoesNotExistException(request.getBindingId())))
+ .flatMap(mailServiceBinding -> Mono.just(GetServiceInstanceAppBindingResponse.builder()
+ .credentials(mailServiceBinding.getCredentials())
+ .build()));
+ }
+
+ @Override
+ public Mono deleteServiceInstanceBinding(
+ DeleteServiceInstanceBindingRequest request) {
+ return mailService.serviceBindingExists(request.getServiceInstanceId(), request.getBindingId())
+ .flatMap(exists -> {
+ if (exists) {
+ return mailService.deleteServiceBinding(request.getServiceInstanceId())
+ .thenReturn(DeleteServiceInstanceBindingResponse.builder().build());
+ } else {
+ return Mono.error(new ServiceInstanceBindingDoesNotExistException(request.getBindingId()));
+ }
+ });
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java
new file mode 100644
index 0000000000..8c757c3f25
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceService.java
@@ -0,0 +1,72 @@
+package com.baeldung.spring.cloud.openservicebroker.services;
+
+import com.baeldung.spring.cloud.openservicebroker.mail.MailService;
+import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException;
+import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceRequest;
+import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceResponse;
+import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest;
+import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceResponse;
+import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceRequest;
+import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceResponse;
+import org.springframework.cloud.servicebroker.service.ServiceInstanceService;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+@Service
+public class MailServiceInstanceService implements ServiceInstanceService {
+
+ private final MailService mailService;
+
+ public MailServiceInstanceService(MailService mailService) {
+ this.mailService = mailService;
+ }
+
+ @Override
+ public Mono createServiceInstance(CreateServiceInstanceRequest request) {
+ return Mono.just(request.getServiceInstanceId())
+ .flatMap(instanceId -> Mono.just(CreateServiceInstanceResponse.builder())
+ .flatMap(responseBuilder -> mailService.serviceInstanceExists(instanceId)
+ .flatMap(exists -> {
+ if (exists) {
+ return mailService.getServiceInstance(instanceId)
+ .flatMap(mailServiceInstance -> Mono.just(responseBuilder
+ .instanceExisted(true)
+ .dashboardUrl(mailServiceInstance.getDashboardUrl())
+ .build()));
+ } else {
+ return mailService.createServiceInstance(
+ instanceId, request.getServiceDefinitionId(), request.getPlanId())
+ .flatMap(mailServiceInstance -> Mono.just(responseBuilder
+ .instanceExisted(false)
+ .dashboardUrl(mailServiceInstance.getDashboardUrl())
+ .build()));
+ }
+ })));
+ }
+
+ @Override
+ public Mono deleteServiceInstance(DeleteServiceInstanceRequest request) {
+ return Mono.just(request.getServiceInstanceId())
+ .flatMap(instanceId -> mailService.serviceInstanceExists(instanceId)
+ .flatMap(exists -> {
+ if (exists) {
+ return mailService.deleteServiceInstance(instanceId)
+ .thenReturn(DeleteServiceInstanceResponse.builder().build());
+ } else {
+ return Mono.error(new ServiceInstanceDoesNotExistException(instanceId));
+ }
+ }));
+ }
+
+ @Override
+ public Mono getServiceInstance(GetServiceInstanceRequest request) {
+ return Mono.just(request.getServiceInstanceId())
+ .flatMap(instanceId -> mailService.getServiceInstance(instanceId)
+ .switchIfEmpty(Mono.error(new ServiceInstanceDoesNotExistException(instanceId)))
+ .flatMap(serviceInstance -> Mono.just(GetServiceInstanceResponse.builder()
+ .serviceDefinitionId(serviceInstance.getServiceDefinitionId())
+ .planId(serviceInstance.getPlanId())
+ .dashboardUrl(serviceInstance.getDashboardUrl())
+ .build())));
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml b/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml
new file mode 100644
index 0000000000..d863b513b0
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/main/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ cloud:
+ openservicebroker:
+ base-path: /broker
+
+mail:
+ system:
+ base-url: http://localhost:8080/mail-system/
+ dashboard:
+ base-url: http://localhost:8080/mail-dashboard/
\ No newline at end of file
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java
new file mode 100644
index 0000000000..5b50d44600
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceBindingServiceUnitTest.java
@@ -0,0 +1,201 @@
+package com.baeldung.spring.cloud.openservicebroker.services;
+
+import com.baeldung.spring.cloud.openservicebroker.mail.MailService;
+import com.baeldung.spring.cloud.openservicebroker.mail.MailServiceBinding;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.cloud.servicebroker.exception.ServiceInstanceBindingDoesNotExistException;
+import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
+import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest;
+import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceAppBindingResponse;
+import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingRequest;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.PASSWORD_KEY;
+import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.URI_KEY;
+import static com.baeldung.spring.cloud.openservicebroker.mail.MailService.USERNAME_KEY;
+import static java.util.UUID.randomUUID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class MailServiceInstanceBindingServiceUnitTest {
+
+ private static final String MAIL_SERVICE_INSTANCE_ID = "test@baeldung.com";
+ private static final String MAIL_SERVICE_BINDING_ID = "test";
+ private static final String MAIL_SYSTEM_URL = "http://localhost:8080/mail-system/test@baeldung.com";
+
+ @Mock
+ private MailService mailService;
+
+ private MailServiceInstanceBindingService mailServiceInstanceBindingService;
+
+ @BeforeEach
+ public void setUp() {
+ initMocks(this);
+
+ this.mailServiceInstanceBindingService = new MailServiceInstanceBindingService(mailService);
+ }
+
+ @Test
+ public void givenServiceBindingDoesNotExist_whenCreateServiceBinding_thenNewBindingIsCreated() {
+ // given service binding does not exist
+ when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(false));
+
+ Map credentials = generateCredentials();
+ MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials);
+ when(mailService.createServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID))
+ .thenReturn(Mono.just(serviceBinding));
+
+ // when create service binding
+ CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then a new service binding is provisioned
+ StepVerifier.create(mailServiceInstanceBindingService.createServiceInstanceBinding(request))
+ .consumeNextWith(response -> {
+ assertTrue(response instanceof CreateServiceInstanceAppBindingResponse);
+ CreateServiceInstanceAppBindingResponse bindingResponse = (CreateServiceInstanceAppBindingResponse) response;
+ assertFalse(bindingResponse.isBindingExisted());
+ validateBindingCredentials(bindingResponse.getCredentials());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceBindingExists_whenCreateServiceBinding_thenExistingBindingIsRetrieved() {
+ // given service binding exists
+ when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(true));
+
+ Map credentials = generateCredentials();
+ MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials);
+ when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID))
+ .thenReturn(Mono.just(serviceBinding));
+
+ // when create service binding
+ CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then a new service binding is provisioned
+ StepVerifier.create(mailServiceInstanceBindingService.createServiceInstanceBinding(request))
+ .consumeNextWith(response -> {
+ assertTrue(response instanceof CreateServiceInstanceAppBindingResponse);
+ CreateServiceInstanceAppBindingResponse bindingResponse = (CreateServiceInstanceAppBindingResponse) response;
+ assertTrue(bindingResponse.isBindingExisted());
+ validateBindingCredentials(bindingResponse.getCredentials());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceBindingDoesNotExist_whenGetServiceBinding_thenException() {
+ // given service binding does not exist
+ when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.empty());
+
+ // when get service binding
+ GetServiceInstanceBindingRequest request = GetServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then ServiceInstanceBindingDoesNotExistException is thrown
+ StepVerifier.create(mailServiceInstanceBindingService.getServiceInstanceBinding(request))
+ .expectErrorMatches(ex -> ex instanceof ServiceInstanceBindingDoesNotExistException)
+ .verify();
+ }
+
+ @Test
+ public void givenServiceBindingExists_whenGetServiceBinding_thenExistingBindingIsRetrieved() {
+ // given service binding exists
+ Map credentials = generateCredentials();
+ MailServiceBinding serviceBinding = new MailServiceBinding(MAIL_SERVICE_BINDING_ID, credentials);
+ when(mailService.getServiceBinding(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID))
+ .thenReturn(Mono.just(serviceBinding));
+
+ // when get service binding
+ GetServiceInstanceBindingRequest request = GetServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then the existing service binding is retrieved
+ StepVerifier.create(mailServiceInstanceBindingService.getServiceInstanceBinding(request))
+ .consumeNextWith(response -> {
+ assertTrue(response instanceof GetServiceInstanceAppBindingResponse);
+ GetServiceInstanceAppBindingResponse bindingResponse = (GetServiceInstanceAppBindingResponse) response;
+ validateBindingCredentials(bindingResponse.getCredentials());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceBindingDoesNotExist_whenDeleteServiceBinding_thenException() {
+ // given service binding does not exist
+ when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(false));
+
+ // when delete service binding
+ DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then ServiceInstanceBindingDoesNotExistException is thrown
+ StepVerifier.create(mailServiceInstanceBindingService.deleteServiceInstanceBinding(request))
+ .expectErrorMatches(ex -> ex instanceof ServiceInstanceBindingDoesNotExistException)
+ .verify();
+ }
+
+ @Test
+ public void givenServiceBindingExists_whenDeleteServiceBinding_thenExistingBindingIsDeleted() {
+ // given service binding exists
+ when(mailService.serviceBindingExists(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_BINDING_ID)).thenReturn(Mono.just(true));
+ when(mailService.deleteServiceBinding(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty());
+
+ // when delete service binding
+ DeleteServiceInstanceBindingRequest request = DeleteServiceInstanceBindingRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .bindingId(MAIL_SERVICE_BINDING_ID)
+ .build();
+
+ // then the existing service binding is retrieved
+ StepVerifier.create(mailServiceInstanceBindingService.deleteServiceInstanceBinding(request))
+ .consumeNextWith(response -> {
+ assertFalse(response.isAsync());
+ assertNull(response.getOperation());
+ })
+ .verifyComplete();
+ }
+
+ private void validateBindingCredentials(Map bindingCredentials) {
+ assertNotNull(bindingCredentials);
+ assertEquals(3, bindingCredentials.size());
+ assertTrue(bindingCredentials.containsKey(URI_KEY));
+ assertTrue(bindingCredentials.containsKey(USERNAME_KEY));
+ assertTrue(bindingCredentials.containsKey(PASSWORD_KEY));
+ assertEquals(MAIL_SYSTEM_URL, bindingCredentials.get(URI_KEY));
+ assertEquals(MAIL_SERVICE_BINDING_ID, bindingCredentials.get(USERNAME_KEY));
+ assertNotNull(bindingCredentials.get(PASSWORD_KEY));
+ }
+
+ private Map generateCredentials() {
+ Map credentials = new HashMap<>();
+ credentials.put(URI_KEY, MAIL_SYSTEM_URL);
+ credentials.put(USERNAME_KEY, MAIL_SERVICE_BINDING_ID);
+ credentials.put(PASSWORD_KEY, randomUUID().toString());
+ return credentials;
+ }
+}
diff --git a/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java
new file mode 100644
index 0000000000..1302cad42e
--- /dev/null
+++ b/spring-cloud/spring-cloud-open-service-broker/src/test/java/com/baeldung/spring/cloud/openservicebroker/services/MailServiceInstanceServiceUnitTest.java
@@ -0,0 +1,166 @@
+package com.baeldung.spring.cloud.openservicebroker.services;
+
+import com.baeldung.spring.cloud.openservicebroker.mail.MailService;
+import com.baeldung.spring.cloud.openservicebroker.mail.MailServiceInstance;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.cloud.servicebroker.exception.ServiceInstanceDoesNotExistException;
+import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceRequest;
+import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest;
+import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceRequest;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+public class MailServiceInstanceServiceUnitTest {
+
+ private static final String MAIL_SERVICE_INSTANCE_ID = "test@baeldung.com";
+ private static final String MAIL_SERVICE_DEFINITION_ID = "mock-service-definition-id";
+ private static final String MAIL_SERVICE_PLAN_ID = "mock-service-plan-id";
+ private static final String MAIL_DASHBOARD_URL = "http://localhost:8080/mail-dashboard/test@baeldung.com";
+
+ @Mock
+ private MailService mailService;
+
+ private MailServiceInstanceService mailServiceInstanceService;
+
+ @BeforeEach
+ public void setUp() {
+ initMocks(this);
+
+ this.mailServiceInstanceService = new MailServiceInstanceService(mailService);
+ }
+
+ @Test
+ public void givenServiceInstanceDoesNotExist_whenCreateServiceInstance_thenProvisionNewService() {
+ // given service instance does not exist
+ when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(false));
+
+ MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID,
+ MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL);
+ when(mailService.createServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID, MAIL_SERVICE_PLAN_ID))
+ .thenReturn(Mono.just(serviceInstance));
+
+ // when create service instance
+ CreateServiceInstanceRequest request = CreateServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .serviceDefinitionId(MAIL_SERVICE_DEFINITION_ID)
+ .planId(MAIL_SERVICE_PLAN_ID)
+ .build();
+
+ // then a new service instance is provisioned
+ StepVerifier.create(mailServiceInstanceService.createServiceInstance(request))
+ .consumeNextWith(response -> {
+ assertFalse(response.isInstanceExisted());
+ assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceInstanceExists_whenCreateServiceInstance_thenExistingServiceInstanceIsRetrieved() {
+ // given service instance exists
+ when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(true));
+
+ MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID,
+ MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL);
+ when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(serviceInstance));
+
+ // when create service instance
+ CreateServiceInstanceRequest request = CreateServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .serviceDefinitionId(MAIL_SERVICE_DEFINITION_ID)
+ .planId(MAIL_SERVICE_PLAN_ID)
+ .build();
+
+ // then the existing one is retrieved
+ StepVerifier.create(mailServiceInstanceService.createServiceInstance(request))
+ .consumeNextWith(response -> {
+ assertTrue(response.isInstanceExisted());
+ assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceInstanceDoesNotExist_whenDeleteServiceInstance_thenException() {
+ // given service instance does not exist
+ when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(false));
+
+ // when delete service instance
+ DeleteServiceInstanceRequest request = DeleteServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .build();
+
+ // then ServiceInstanceDoesNotExistException is thrown
+ StepVerifier.create(mailServiceInstanceService.deleteServiceInstance(request))
+ .expectErrorMatches(ex -> ex instanceof ServiceInstanceDoesNotExistException)
+ .verify();
+ }
+
+ @Test
+ public void givenServiceInstanceExists_whenDeleteServiceInstance_thenSuccess() {
+ // given service instance exists
+ when(mailService.serviceInstanceExists(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(true));
+
+ // when delete service instance
+ when(mailService.deleteServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty());
+
+ DeleteServiceInstanceRequest request = DeleteServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .build();
+
+ // then success
+ StepVerifier.create(mailServiceInstanceService.deleteServiceInstance(request))
+ .consumeNextWith(response -> {
+ assertFalse(response.isAsync());
+ assertNull(response.getOperation());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ public void givenServiceInstanceDoesNotExist_whenGetServiceInstance_thenException() {
+ // given service instance does not exist
+ when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.empty());
+
+ // when get service instance
+ GetServiceInstanceRequest request = GetServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .build();
+
+ // then ServiceInstanceDoesNotExistException is thrown
+ StepVerifier.create(mailServiceInstanceService.getServiceInstance(request))
+ .expectErrorMatches(ex -> ex instanceof ServiceInstanceDoesNotExistException)
+ .verify();
+ }
+
+ @Test
+ public void givenServiceInstanceExists_whenGetServiceInstance_thenExistingServiceInstanceIsRetrieved() {
+ // given service instance exists
+ MailServiceInstance serviceInstance = new MailServiceInstance(MAIL_SERVICE_INSTANCE_ID, MAIL_SERVICE_DEFINITION_ID,
+ MAIL_SERVICE_PLAN_ID, MAIL_DASHBOARD_URL);
+ when(mailService.getServiceInstance(MAIL_SERVICE_INSTANCE_ID)).thenReturn(Mono.just(serviceInstance));
+
+ // when get service instance
+ GetServiceInstanceRequest request = GetServiceInstanceRequest.builder()
+ .serviceInstanceId(MAIL_SERVICE_INSTANCE_ID)
+ .build();
+
+ // then the existing service instance is retrieved
+ StepVerifier.create(mailServiceInstanceService.getServiceInstance(request))
+ .consumeNextWith(response -> {
+ assertEquals(MAIL_SERVICE_DEFINITION_ID, response.getServiceDefinitionId());
+ assertEquals(MAIL_SERVICE_PLAN_ID, response.getPlanId());
+ assertEquals(MAIL_DASHBOARD_URL, response.getDashboardUrl());
+ })
+ .verifyComplete();
+ }
+}
diff --git a/spring-cloud/spring-cloud-task/pom.xml b/spring-cloud/spring-cloud-task/pom.xml
index 377d16a999..e2006ee9d3 100644
--- a/spring-cloud/spring-cloud-task/pom.xml
+++ b/spring-cloud/spring-cloud-task/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-boot-1
+ parent-boot-2
0.0.1-SNAPSHOT
- ../../parent-boot-1
+ ../../parent-boot-2
@@ -40,8 +40,8 @@
- Brixton.SR7
- 1.2.2.RELEASE
+ Hoxton.SR4
+ 2.2.3.RELEASE
diff --git a/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml b/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml
index fd10322efb..4e6b8b8b6c 100644
--- a/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml
+++ b/spring-cloud/spring-cloud-task/springcloudtaskbatch/pom.xml
@@ -45,6 +45,13 @@
org.springframework.cloud
spring-cloud-task-batch
+
+
+ net.bytebuddy
+ byte-buddy-dep
+ ${bytebuddy.version}
+
+
com.h2database
h2
@@ -63,6 +70,7 @@
com.baeldung.TaskDemo
+ 1.10.10
diff --git a/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml b/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml
index 794ac4d247..8a6e4fc172 100644
--- a/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml
+++ b/spring-cloud/spring-cloud-task/springcloudtaskbatch/src/test/resources/application.yml
@@ -1,6 +1,6 @@
spring:
datasource:
- url: jdbc:h2:mem:springcloud
+ url: jdbc:h2:mem:springcloud;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
jpa:
diff --git a/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml b/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml
index 93255959e4..33f6ccde74 100644
--- a/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml
+++ b/spring-cloud/spring-cloud-task/springcloudtasksink/pom.xml
@@ -50,8 +50,7 @@
- 1.2.2.RELEASE
- 1.3.0.RELEASE
+ 2.3.1.RELEASE
diff --git a/spring-core-4/README.md b/spring-core-4/README.md
index 592f4cd011..11a966f23d 100644
--- a/spring-core-4/README.md
+++ b/spring-core-4/README.md
@@ -6,4 +6,5 @@ This module contains articles about core Spring functionality
- [Creating Spring Beans Through Factory Methods](https://www.baeldung.com/spring-beans-factory-methods)
- [How to dynamically Autowire a Bean in Spring](https://www.baeldung.com/spring-dynamic-autowire)
+- [Spring @Import Annotation](https://www.baeldung.com/spring-import-annotation)
- More articles: [[<-- prev]](/spring-core-3)
diff --git a/spring-core-4/pom.xml b/spring-core-4/pom.xml
index 53f7ca6912..fbec5ea9eb 100644
--- a/spring-core-4/pom.xml
+++ b/spring-core-4/pom.xml
@@ -24,6 +24,16 @@
spring-core
${spring.version}
+
+ org.springframework
+ spring-expression
+ ${spring.version}
+
+
+ com.google.guava
+ guava
+ 28.2-jre
+
org.springframework
spring-test
@@ -42,6 +52,18 @@
${junit-jupiter.version}
test
+
+ org.awaitility
+ awaitility
+ 4.0.2
+ test
+
+
+ org.assertj
+ assertj-core
+ 2.9.1
+ test
+
@@ -60,4 +82,4 @@
2.2.2.RELEASE
-
\ No newline at end of file
+
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java
new file mode 100644
index 0000000000..8b3c528c4d
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GlobalEventBus.java
@@ -0,0 +1,39 @@
+package com.baeldung.beanpostprocessor;
+
+import com.google.common.eventbus.AsyncEventBus;
+import com.google.common.eventbus.EventBus;
+
+import java.util.concurrent.Executors;
+
+@SuppressWarnings("ALL")
+public final class GlobalEventBus {
+
+ public static final String GLOBAL_EVENT_BUS_EXPRESSION = "T(com.baeldung.postprocessor.GlobalEventBus).getEventBus()";
+
+ private static final String IDENTIFIER = "global-event-bus";
+
+ private static final GlobalEventBus GLOBAL_EVENT_BUS = new GlobalEventBus();
+
+ private final EventBus eventBus = new AsyncEventBus(IDENTIFIER, Executors.newCachedThreadPool());
+
+ private GlobalEventBus() {
+ }
+
+ public static GlobalEventBus getInstance() {
+ return GlobalEventBus.GLOBAL_EVENT_BUS;
+ }
+
+ public static EventBus getEventBus() {
+ return GlobalEventBus.GLOBAL_EVENT_BUS.eventBus;
+ }
+
+ public static void subscribe(Object obj) {
+ getEventBus().register(obj);
+ }
+ public static void unsubscribe(Object obj) {
+ getEventBus().unregister(obj);
+ }
+ public static void post(Object event) {
+ getEventBus().post(event);
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java
new file mode 100644
index 0000000000..e0108655cf
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanFactoryPostProcessor.java
@@ -0,0 +1,63 @@
+package com.baeldung.beanpostprocessor;
+
+import com.google.common.eventbus.EventBus;
+
+import java.util.Iterator;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionException;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+
+@SuppressWarnings("ALL")
+public class GuavaEventBusBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final SpelExpressionParser expressionParser = new SpelExpressionParser();
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ for (Iterator names = beanFactory.getBeanNamesIterator(); names.hasNext(); ) {
+ Object proxy = this.getTargetObject(beanFactory.getBean(names.next()));
+ final Subscriber annotation = AnnotationUtils.getAnnotation(proxy.getClass(), Subscriber.class);
+ if (annotation == null)
+ continue;
+ this.logger.info("{}: processing bean of type {} during initialization", this.getClass().getSimpleName(),
+ proxy.getClass().getName());
+ final String annotationValue = annotation.value();
+ try {
+ final Expression expression = this.expressionParser.parseExpression(annotationValue);
+ final Object value = expression.getValue();
+ if (!(value instanceof EventBus)) {
+ this.logger.error("{}: expression {} did not evaluate to an instance of EventBus for bean of type {}",
+ this.getClass().getSimpleName(), annotationValue, proxy.getClass().getSimpleName());
+ return;
+ }
+ final EventBus eventBus = (EventBus)value;
+ eventBus.register(proxy);
+ } catch (ExpressionException ex) {
+ this.logger.error("{}: unable to parse/evaluate expression {} for bean of type {}", this.getClass().getSimpleName(),
+ annotationValue, proxy.getClass().getName());
+ }
+ }
+ }
+
+ private Object getTargetObject(Object proxy) throws BeansException {
+ if (AopUtils.isJdkDynamicProxy(proxy)) {
+ try {
+ return ((Advised)proxy).getTargetSource().getTarget();
+ } catch (Exception e) {
+ throw new FatalBeanException("Error getting target of JDK proxy", e);
+ }
+ }
+ return proxy;
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java
new file mode 100644
index 0000000000..be3800c40a
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/GuavaEventBusBeanPostProcessor.java
@@ -0,0 +1,87 @@
+package com.baeldung.beanpostprocessor;
+
+import com.google.common.eventbus.EventBus;
+
+import java.util.function.BiConsumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionException;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+
+/**
+ * A {@link DestructionAwareBeanPostProcessor} which registers/un-registers subscribers to a Guava {@link EventBus}. The class must
+ * be annotated with {@link Subscriber} and each subscribing method must be annotated with
+ * {@link com.google.common.eventbus.Subscribe}.
+ */
+@SuppressWarnings("ALL")
+public class GuavaEventBusBeanPostProcessor implements DestructionAwareBeanPostProcessor {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final SpelExpressionParser expressionParser = new SpelExpressionParser();
+
+ @Override
+ public void postProcessBeforeDestruction(final Object bean, final String beanName) throws BeansException {
+ this.process(bean, EventBus::unregister, "destruction");
+ }
+
+ @Override
+ public boolean requiresDestruction(Object bean) {
+ return true;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ this.process(bean, EventBus::register, "initialization");
+ return bean;
+ }
+
+ private void process(final Object bean, final BiConsumer consumer, final String action) {
+ Object proxy = this.getTargetObject(bean);
+ final Subscriber annotation = AnnotationUtils.getAnnotation(proxy.getClass(), Subscriber.class);
+ if (annotation == null)
+ return;
+ this.logger.info("{}: processing bean of type {} during {}", this.getClass().getSimpleName(), proxy.getClass().getName(),
+ action);
+ final String annotationValue = annotation.value();
+ try {
+ final Expression expression = this.expressionParser.parseExpression(annotationValue);
+ final Object value = expression.getValue();
+ if (!(value instanceof EventBus)) {
+ this.logger.error("{}: expression {} did not evaluate to an instance of EventBus for bean of type {}",
+ this.getClass().getSimpleName(), annotationValue, proxy.getClass().getSimpleName());
+ return;
+ }
+ final EventBus eventBus = (EventBus)value;
+ consumer.accept(eventBus, proxy);
+ } catch (ExpressionException ex) {
+ this.logger.error("{}: unable to parse/evaluate expression {} for bean of type {}", this.getClass().getSimpleName(),
+ annotationValue, proxy.getClass().getName());
+ }
+ }
+
+ private Object getTargetObject(Object proxy) throws BeansException {
+ if (AopUtils.isJdkDynamicProxy(proxy)) {
+ try {
+ return ((Advised)proxy).getTargetSource().getTarget();
+ } catch (Exception e) {
+ throw new FatalBeanException("Error getting target of JDK proxy", e);
+ }
+ }
+ return proxy;
+ }
+}
+
+
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java
new file mode 100644
index 0000000000..f27f9e7c9b
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTrade.java
@@ -0,0 +1,34 @@
+package com.baeldung.beanpostprocessor;
+
+import java.util.Date;
+
+public class StockTrade {
+
+ private final String symbol;
+ private final int quantity;
+ private final double price;
+ private final Date tradeDate;
+
+ public StockTrade(String symbol, int quantity, double price, Date tradeDate) {
+ this.symbol = symbol;
+ this.quantity = quantity;
+ this.price = price;
+ this.tradeDate = tradeDate;
+ }
+
+ public String getSymbol() {
+ return this.symbol;
+ }
+
+ public int getQuantity() {
+ return this.quantity;
+ }
+
+ public double getPrice() {
+ return this.price;
+ }
+
+ public Date getTradeDate() {
+ return this.tradeDate;
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java
new file mode 100644
index 0000000000..a0ee293293
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradeListener.java
@@ -0,0 +1,7 @@
+package com.baeldung.beanpostprocessor;
+
+@FunctionalInterface
+public interface StockTradeListener {
+
+ void stockTradePublished(StockTrade trade);
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java
new file mode 100644
index 0000000000..0058944b53
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/StockTradePublisher.java
@@ -0,0 +1,36 @@
+package com.baeldung.beanpostprocessor;
+
+import com.google.common.eventbus.AllowConcurrentEvents;
+import com.google.common.eventbus.Subscribe;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Subscriber
+public class StockTradePublisher {
+
+ private final Set stockTradeListeners = new HashSet<>();
+
+ public void addStockTradeListener(StockTradeListener listener) {
+ synchronized (this.stockTradeListeners) {
+ this.stockTradeListeners.add(listener);
+ }
+ }
+
+ public void removeStockTradeListener(StockTradeListener listener) {
+ synchronized (this.stockTradeListeners) {
+ this.stockTradeListeners.remove(listener);
+ }
+ }
+
+ @Subscribe
+ @AllowConcurrentEvents
+ private void handleNewStockTradeEvent(StockTrade trade) {
+ // publish to DB, send to PubNub, whatever you want here
+ final Set listeners;
+ synchronized (this.stockTradeListeners) {
+ listeners = new HashSet<>(this.stockTradeListeners);
+ }
+ listeners.forEach(li -> li.stockTradePublished(trade));
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java
new file mode 100644
index 0000000000..1aca507555
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/beanpostprocessor/Subscriber.java
@@ -0,0 +1,21 @@
+package com.baeldung.beanpostprocessor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation which indicates which Guava {@link com.google.common.eventbus.EventBus} a Spring bean wishes to subscribe to.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface Subscriber {
+
+ /**
+ * A SpEL expression which selects the {@link com.google.common.eventbus.EventBus}.
+ */
+ String value() default GlobalEventBus.GLOBAL_EVENT_BUS_EXPRESSION;
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java
new file mode 100644
index 0000000000..94f22788b8
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalConfiguration.java
@@ -0,0 +1,9 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import({ MammalConfiguration.class, BirdConfig.class })
+class AnimalConfiguration {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java
new file mode 100644
index 0000000000..9b4310b6d3
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/AnimalScanConfiguration.java
@@ -0,0 +1,9 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan
+public class AnimalScanConfiguration {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java
new file mode 100644
index 0000000000..a785cf7641
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bird.java
@@ -0,0 +1,4 @@
+package com.baeldung.importannotation.animal;
+
+class Bird {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java
new file mode 100644
index 0000000000..c5cefe8b22
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BirdConfig.java
@@ -0,0 +1,13 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class BirdConfig {
+
+ @Bean
+ Bird bird() {
+ return new Bird();
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java
new file mode 100644
index 0000000000..6abe08e393
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Bug.java
@@ -0,0 +1,7 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.stereotype.Component;
+
+@Component(value = "bug")
+class Bug {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java
new file mode 100644
index 0000000000..9bea16413a
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/BugConfig.java
@@ -0,0 +1,9 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import(Bug.class)
+class BugConfig {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java
new file mode 100644
index 0000000000..7eb36c81ce
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Cat.java
@@ -0,0 +1,4 @@
+package com.baeldung.importannotation.animal;
+
+class Cat {
+}
\ No newline at end of file
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java
new file mode 100644
index 0000000000..ebb35ffc11
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/CatConfig.java
@@ -0,0 +1,13 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class CatConfig {
+
+ @Bean
+ Cat cat() {
+ return new Cat();
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java
new file mode 100644
index 0000000000..00374c1bc0
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/Dog.java
@@ -0,0 +1,4 @@
+package com.baeldung.importannotation.animal;
+
+class Dog {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java
new file mode 100644
index 0000000000..c11ee44623
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/DogConfig.java
@@ -0,0 +1,13 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class DogConfig {
+
+ @Bean
+ Dog dog() {
+ return new Dog();
+ }
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java
new file mode 100644
index 0000000000..3d77ac878c
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/animal/MammalConfiguration.java
@@ -0,0 +1,9 @@
+package com.baeldung.importannotation.animal;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import({ DogConfig.class, CatConfig.class })
+class MammalConfiguration {
+}
diff --git a/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java b/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java
new file mode 100644
index 0000000000..01aa36a796
--- /dev/null
+++ b/spring-core-4/src/main/java/com/baeldung/importannotation/zoo/ZooApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.importannotation.zoo;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import com.baeldung.importannotation.animal.AnimalScanConfiguration;
+
+@Configuration
+@Import(AnimalScanConfiguration.class)
+class ZooApplication {
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java
new file mode 100644
index 0000000000..842283f563
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/PostProcessorConfiguration.java
@@ -0,0 +1,23 @@
+package com.baeldung.beanpostprocessor;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class PostProcessorConfiguration {
+
+ @Bean
+ public GlobalEventBus eventBus() {
+ return GlobalEventBus.getInstance();
+ }
+
+ @Bean
+ public GuavaEventBusBeanPostProcessor eventBusBeanPostProcessor() {
+ return new GuavaEventBusBeanPostProcessor();
+ }
+
+ @Bean
+ public StockTradePublisher stockTradePublisher() {
+ return new StockTradePublisher();
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java
new file mode 100644
index 0000000000..74d6765ecd
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/beanpostprocessor/StockTradeIntegrationTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.beanpostprocessor;
+
+import java.time.Duration;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+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 static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = {PostProcessorConfiguration.class})
+public class StockTradeIntegrationTest {
+
+ @Autowired
+ private StockTradePublisher stockTradePublisher;
+
+ @Test
+ public void givenValidConfig_whenTradePublished_thenTradeReceived() {
+ Date tradeDate = new Date();
+ StockTrade stockTrade = new StockTrade("AMZN", 100, 2483.52d, tradeDate);
+ AtomicBoolean assertionsPassed = new AtomicBoolean(false);
+ StockTradeListener listener = trade -> assertionsPassed.set(this.verifyExact(stockTrade, trade));
+ this.stockTradePublisher.addStockTradeListener(listener);
+ try {
+ GlobalEventBus.post(stockTrade);
+ await().atMost(Duration.ofSeconds(2L))
+ .untilAsserted(() -> assertThat(assertionsPassed.get()).isTrue());
+ } finally {
+ this.stockTradePublisher.removeStockTradeListener(listener);
+ }
+ }
+
+ private boolean verifyExact(StockTrade stockTrade, StockTrade trade) {
+ return Objects.equals(stockTrade.getSymbol(), trade.getSymbol())
+ && Objects.equals(stockTrade.getTradeDate(), trade.getTradeDate())
+ && stockTrade.getQuantity() == trade.getQuantity()
+ && stockTrade.getPrice() == trade.getPrice();
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java
new file mode 100644
index 0000000000..7f4795da25
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/AnimalConfigUnitTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.importannotation.animal;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = { AnimalConfiguration.class })
+class AnimalConfigUnitTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ @Test
+ void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() {
+ assertThatBeanExists("dog", Dog.class);
+ assertThatBeanExists("cat", Cat.class);
+ assertThatBeanExists("bird", Cat.class);
+ }
+
+ private void assertThatBeanExists(String beanName, Class> beanClass) {
+ assertTrue(context.containsBean(beanName));
+ assertNotNull(context.getBean(beanClass));
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java
new file mode 100644
index 0000000000..2a2e0b332a
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/BugConfigUnitTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.importannotation.animal;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = BugConfig.class)
+class BugConfigUnitTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ @Test
+ void givenImportInComponent_whenLookForBean_shallFindIt() {
+ assertTrue(context.containsBean("bug"));
+ assertNotNull(context.getBean(Bug.class));
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java
new file mode 100644
index 0000000000..dadd2abae6
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/ConfigUnitTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.importannotation.animal;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = { BirdConfig.class, CatConfig.class, DogConfig.class })
+class ConfigUnitTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ @Test
+ void givenImportedBeans_whenGettingEach_shallFindIt() {
+ assertThatBeanExists("dog", Dog.class);
+ assertThatBeanExists("cat", Cat.class);
+ assertThatBeanExists("bird", Bird.class);
+ }
+
+ private void assertThatBeanExists(String beanName, Class> beanClass) {
+ assertTrue(context.containsBean(beanName));
+ assertNotNull(context.getBean(beanClass));
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java
new file mode 100644
index 0000000000..5e1596253c
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/importannotation/animal/MammalConfigUnitTest.java
@@ -0,0 +1,33 @@
+package com.baeldung.importannotation.animal;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = { MammalConfiguration.class })
+class MammalConfigUnitTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ @Test
+ void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() {
+ assertThatBeanExists("dog", Dog.class);
+ assertThatBeanExists("cat", Cat.class);
+
+ assertFalse(context.containsBean("bird"));
+ }
+
+ private void assertThatBeanExists(String beanName, Class> beanClass) {
+ assertTrue(context.containsBean(beanName));
+ assertNotNull(context.getBean(beanClass));
+ }
+}
diff --git a/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java b/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java
new file mode 100644
index 0000000000..e832e27b28
--- /dev/null
+++ b/spring-core-4/src/test/java/com/baeldung/importannotation/zoo/ZooApplicationUnitTest.java
@@ -0,0 +1,26 @@
+package com.baeldung.importannotation.zoo;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = ZooApplication.class)
+class ZooApplicationUnitTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ @Test
+ void givenTheScanInTheAnimalPackage_whenGettingAnyAnimal_shallFindItInTheContext() {
+ assertNotNull(context.getBean("dog"));
+ assertNotNull(context.getBean("bird"));
+ assertNotNull(context.getBean("cat"));
+ assertNotNull(context.getBean("bug"));
+ }
+}
diff --git a/spring-jinq/pom.xml b/spring-jinq/pom.xml
index 073808823c..647c0907a7 100644
--- a/spring-jinq/pom.xml
+++ b/spring-jinq/pom.xml
@@ -37,12 +37,6 @@
spring-boot-starter-data-jpa
-
- net.bytebuddy
- byte-buddy-dep
- ${bytebuddy.version}
-
-
org.springframework
@@ -73,7 +67,6 @@
1.8.29
- 1.10.10
diff --git a/spring-rest-hal-browser/pom.xml b/spring-rest-hal-browser/pom.xml
index 32a0b52875..7b629dba44 100644
--- a/spring-rest-hal-browser/pom.xml
+++ b/spring-rest-hal-browser/pom.xml
@@ -35,11 +35,6 @@
com.h2database
h2
-
- net.bytebuddy
- byte-buddy-dep
- ${bytebuddy.version}
-
@@ -56,7 +51,6 @@
- 1.10.10
1.8
1.8
diff --git a/spring-rest-http/README.md b/spring-rest-http/README.md
index 35793cb281..f78f8784b0 100644
--- a/spring-rest-http/README.md
+++ b/spring-rest-http/README.md
@@ -13,3 +13,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Spring RequestMapping](https://www.baeldung.com/spring-requestmapping)
- [Guide to DeferredResult in Spring](https://www.baeldung.com/spring-deferred-result)
- [Using JSON Patch in Spring REST APIs](https://www.baeldung.com/spring-rest-json-patch)
+- [Using OpenAPI and JSON Request Parameters](https://www.baeldung.com/openapi-json-query-parameters)
diff --git a/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml
new file mode 100644
index 0000000000..53272c9cdb
--- /dev/null
+++ b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-2.yaml
@@ -0,0 +1,75 @@
+swagger: "2.0"
+info:
+ description: "This is a sample server."
+ version: "1.0.0"
+ title: "Sample API to send JSON objects as query parameters using OpenAPI 2"
+tags:
+- name: "tickets"
+ description: "Send Tickets as JSON Objects"
+schemes:
+- "https"
+- "http"
+paths:
+ /tickets:
+ get:
+ tags:
+ - "tickets"
+ summary: "Send an JSON Object as a query param"
+ parameters:
+ - name: "params"
+ in: "path"
+ description: "{\"type\":\"foo\",\"color\":\"green\"}"
+ required: true
+ type: "string"
+ responses:
+ "200":
+ description: "Successful operation"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not found"
+ post:
+ tags:
+ - "tickets"
+ summary: "Send an JSON Object in body"
+ parameters:
+ - name: "params"
+ in: "body"
+ description: "Parameter is an JSON object with the `type` and `color` properties that should be serialized as JSON {\"type\":\"foo\",\"color\":\"green\"}"
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Successful operation"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not found"
+ "405":
+ description: "Invalid input"
+ /tickets2:
+ get:
+ tags:
+ - "tickets"
+ summary: "Send an JSON Object in body of get reqest"
+ parameters:
+ - name: "params"
+ in: "body"
+ description: "Parameter is an JSON object with the `type` and `color` properties that should be serialized as JSON {\"type\":\"foo\",\"color\":\"green\"}"
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: "Successful operation"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not found"
diff --git a/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml
new file mode 100644
index 0000000000..a0ed147b9d
--- /dev/null
+++ b/spring-rest-http/src/main/resources/openapi-queryparam-definitions/openapi-3.yaml
@@ -0,0 +1,37 @@
+openapi: 3.0.1
+info:
+ title: Sample API to send JSON objects as query parameters using OpenAPI 3
+ description: This is a sample server.
+ version: 1.0.0
+servers:
+- url: /api
+tags:
+- name: tickets
+ description: Send Tickets as JSON Objects
+paths:
+ /tickets:
+ get:
+ tags:
+ - tickets
+ summary: Send an JSON Object as a query param
+ parameters:
+ - name: params
+ in: query
+ description: '{"type":"foo","color":"green"}'
+ required: true
+ schema:
+ type: object
+ properties:
+ type:
+ type: "string"
+ color:
+ type: "string"
+ responses:
+ 200:
+ description: Successful operation
+ 401:
+ description: Unauthorized
+ 403:
+ description: Forbidden
+ 404:
+ description: Not found
diff --git a/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java
new file mode 100644
index 0000000000..93949e52a3
--- /dev/null
+++ b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RequestFactoryLiveTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.resttemplate.proxy;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Proxy.Type;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * This class is used to test a request using {@link RestTemplate} with {@link Proxy}
+ * using a {@link SimpleClientHttpRequestFactory} as configuration.
+ *
+ *
+ *
+ * Before running the test we should change the PROXY_SERVER_HOST
+ * and PROXY_SERVER_PORT constants in our class to match our preferred proxy configuration.
+ */
+public class RequestFactoryLiveTest {
+
+ private static final String PROXY_SERVER_HOST = "127.0.0.1";
+ private static final int PROXY_SERVER_PORT = 8080;
+
+ RestTemplate restTemplate;
+
+ @Before
+ public void setUp() {
+ Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress(PROXY_SERVER_HOST, PROXY_SERVER_PORT));
+
+ SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
+ requestFactory.setProxy(proxy);
+
+ restTemplate = new RestTemplate(requestFactory);
+ }
+
+ @Test
+ public void givenRestTemplate_whenRequestedWithProxy_thenResponseBodyIsOk() {
+ ResponseEntity responseEntity = restTemplate.getForEntity("http://httpbin.org/get", String.class);
+
+ assertThat(responseEntity.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ }
+}
diff --git a/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java
new file mode 100644
index 0000000000..faeb49537a
--- /dev/null
+++ b/spring-resttemplate/src/test/java/com/baeldung/resttemplate/proxy/RestTemplateCustomizerLiveTest.java
@@ -0,0 +1,69 @@
+package com.baeldung.resttemplate.proxy;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.net.Proxy;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.boot.web.client.RestTemplateCustomizer;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * This class is used to test a request using {@link RestTemplate} with {@link Proxy}
+ * using a {@link RestTemplateCustomizer} as configuration.
+ *
+ *
+ *
+ * Before running the test we should change the PROXY_SERVER_HOST
+ * and PROXY_SERVER_PORT constants in our class to match our preferred proxy configuration.
+ */
+public class RestTemplateCustomizerLiveTest {
+
+ private static final String PROXY_SERVER_HOST = "127.0.0.1";
+ private static final int PROXY_SERVER_PORT = 8080;
+
+ RestTemplate restTemplate;
+
+ @Before
+ public void setUp() {
+ restTemplate = new RestTemplateBuilder(new ProxyCustomizer()).build();
+ }
+
+ @Test
+ public void givenRestTemplate_whenRequestedWithProxy_thenResponseBodyIsOk() {
+ ResponseEntity responseEntity = restTemplate.getForEntity("http://httpbin.org/get", String.class);
+
+ assertThat(responseEntity.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ }
+
+ private static class ProxyCustomizer implements RestTemplateCustomizer {
+
+ @Override
+ public void customize(RestTemplate restTemplate) {
+ HttpHost proxy = new HttpHost(PROXY_SERVER_HOST, PROXY_SERVER_PORT);
+ HttpClient httpClient = HttpClientBuilder.create()
+ .setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
+ @Override
+ public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
+ return super.determineProxy(target, request, context);
+ }
+ })
+ .build();
+ restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
+ }
+ }
+}
diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 7ce33dd3e3..954b9335e4 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -15,11 +15,11 @@
spring-security-acl
+ spring-security-auth0
spring-security-angular/server
spring-security-cache-control
spring-security-core
spring-security-cors
- spring-security-kerberos
spring-security-mvc
spring-security-mvc-boot-1
spring-security-mvc-boot-2
diff --git a/spring-security-modules/spring-security-auth0/pom.xml b/spring-security-modules/spring-security-auth0/pom.xml
new file mode 100644
index 0000000000..0bd879a40b
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+ spring-security-auth0
+ 1.0-SNAPSHOT
+ spring-security-auth0
+ war
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-core
+
+
+ org.springframework.security
+ spring-security-oauth2-resource-server
+
+
+ com.auth0
+ mvc-auth-commons
+ ${mvc-auth-commons.version}
+
+
+ org.json
+ json
+ ${json.version}
+
+
+
+
+ spring-security-auth0
+
+
+ src/main/resources
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
+ 20190722
+ 1.2.0
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java
similarity index 90%
rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java
rename to spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java
index 37dbe7dab8..42f8d946b5 100644
--- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/Application.java
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/Application.java
@@ -1,4 +1,4 @@
-package com.baeldung;
+package com.baeldung.auth0;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java
new file mode 100644
index 0000000000..69cf8b3071
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/AuthConfig.java
@@ -0,0 +1,114 @@
+package com.baeldung.auth0;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+import com.auth0.AuthenticationController;
+import com.baeldung.auth0.controller.LogoutController;
+import com.auth0.jwk.JwkProvider;
+import com.auth0.jwk.JwkProviderBuilder;
+
+@Configuration
+@EnableWebSecurity
+public class AuthConfig extends WebSecurityConfigurerAdapter {
+
+ @Value(value = "${com.auth0.domain}")
+ private String domain;
+
+ @Value(value = "${com.auth0.clientId}")
+ private String clientId;
+
+ @Value(value = "${com.auth0.clientSecret}")
+ private String clientSecret;
+
+ @Value(value = "${com.auth0.managementApi.clientId}")
+ private String managementApiClientId;
+
+ @Value(value = "${com.auth0.managementApi.clientSecret}")
+ private String managementApiClientSecret;
+
+ @Value(value = "${com.auth0.managementApi.grantType}")
+ private String grantType;
+
+ @Bean
+ public LogoutSuccessHandler logoutSuccessHandler() {
+ return new LogoutController();
+ }
+
+ @Bean
+ public AuthenticationController authenticationController() throws UnsupportedEncodingException {
+ JwkProvider jwkProvider = new JwkProviderBuilder(domain).build();
+ return AuthenticationController.newBuilder(domain, clientId, clientSecret)
+ .withJwkProvider(jwkProvider)
+ .build();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf().disable();
+ http
+ .authorizeRequests()
+ .antMatchers("/callback", "/login", "/").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .and()
+ .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public String getManagementApiClientId() {
+ return managementApiClientId;
+ }
+
+ public String getManagementApiClientSecret() {
+ return managementApiClientSecret;
+ }
+
+ public String getGrantType() {
+ return grantType;
+ }
+
+ public String getUserInfoUrl() {
+ return "https://" + getDomain() + "/userinfo";
+ }
+
+ public String getUsersUrl() {
+ return "https://" + getDomain() + "/api/v2/users";
+ }
+
+ public String getUsersByEmailUrl() {
+ return "https://" + getDomain() + "/api/v2/users-by-email?email=";
+ }
+
+ public String getLogoutUrl() {
+ return "https://" + getDomain() +"/v2/logout";
+ }
+
+ public String getContextPath(HttpServletRequest request) {
+ String path = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
+ return path;
+ }
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java
new file mode 100644
index 0000000000..48d09db155
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/AuthController.java
@@ -0,0 +1,77 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.client.RestTemplate;
+
+import com.auth0.AuthenticationController;
+import com.auth0.IdentityVerificationException;
+import com.auth0.Tokens;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.baeldung.auth0.AuthConfig;
+
+@Controller
+public class AuthController {
+
+ @Autowired
+ private AuthenticationController authenticationController;
+
+ @Autowired
+ private AuthConfig config;
+
+ private static final String AUTH0_TOKEN_URL = "https://dev-example.auth0.com/oauth/token";
+
+ @GetMapping(value = "/login")
+ protected void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String redirectUri = config.getContextPath(request) + "/callback";
+ String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri)
+ .withScope("openid email")
+ .build();
+ response.sendRedirect(authorizeUrl);
+ }
+
+ @GetMapping(value="/callback")
+ public void callback(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException {
+ Tokens tokens = authenticationController.handle(request, response);
+
+ DecodedJWT jwt = JWT.decode(tokens.getIdToken());
+ TestingAuthenticationToken authToken2 = new TestingAuthenticationToken(jwt.getSubject(), jwt.getToken());
+ authToken2.setAuthenticated(true);
+
+ SecurityContextHolder.getContext().setAuthentication(authToken2);
+ response.sendRedirect(config.getContextPath(request) + "/");
+ }
+
+ public String getManagementApiToken() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+
+ JSONObject requestBody = new JSONObject();
+ requestBody.put("client_id", config.getManagementApiClientId());
+ requestBody.put("client_secret", config.getManagementApiClientSecret());
+ requestBody.put("audience", "https://dev-example.auth0.com/api/v2/");
+ requestBody.put("grant_type", config.getGrantType());
+
+ HttpEntity request = new HttpEntity(requestBody.toString(), headers);
+
+ RestTemplate restTemplate = new RestTemplate();
+ HashMap result = restTemplate.postForObject(AUTH0_TOKEN_URL, request, HashMap.class);
+
+ return result.get("access_token");
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java
new file mode 100644
index 0000000000..8a4e650846
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/HomeController.java
@@ -0,0 +1,37 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.interfaces.DecodedJWT;
+
+@Controller
+public class HomeController {
+
+ @GetMapping(value = "/")
+ @ResponseBody
+ public String home(HttpServletRequest request, HttpServletResponse response, final Authentication authentication) throws IOException {
+
+ if (authentication!= null && authentication instanceof TestingAuthenticationToken) {
+ TestingAuthenticationToken token = (TestingAuthenticationToken) authentication;
+
+ DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
+ String email = jwt.getClaims().get("email").asString();
+
+ return "Welcome, " + email + "!";
+ } else {
+ response.sendRedirect("http://localhost:8080/login");
+ return null;
+ }
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java
new file mode 100755
index 0000000000..d508fe2c44
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/LogoutController.java
@@ -0,0 +1,35 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.stereotype.Controller;
+
+import com.baeldung.auth0.AuthConfig;
+
+@Controller
+public class LogoutController implements LogoutSuccessHandler {
+
+ @Autowired
+ private AuthConfig config;
+
+ @Override
+ public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authentication) {
+ if (req.getSession() != null) {
+ req.getSession().invalidate();
+ }
+ String returnTo = config.getContextPath(req);
+ String logoutUrl = config.getLogoutUrl() + "?client_id=" + config.getClientId() + "&returnTo=" +returnTo;
+ try {
+ res.sendRedirect(logoutUrl);
+ } catch(IOException e){
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java
new file mode 100644
index 0000000000..86601a06d3
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/controller/UserController.java
@@ -0,0 +1,57 @@
+package com.baeldung.auth0.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.auth0.IdentityVerificationException;
+import com.baeldung.auth0.AuthConfig;
+import com.baeldung.auth0.service.ApiService;
+
+@Controller
+public class UserController {
+
+ @Autowired
+ private ApiService apiService;
+
+ @Autowired
+ private AuthConfig config;
+
+ @GetMapping(value="/users")
+ @ResponseBody
+ public ResponseEntity users(HttpServletRequest request, HttpServletResponse response) throws IOException, IdentityVerificationException {
+ ResponseEntity result = apiService.getCall(config.getUsersUrl());
+ return result;
+ }
+
+ @GetMapping(value = "/userByEmail")
+ @ResponseBody
+ public ResponseEntity userByEmail(HttpServletResponse response, @RequestParam String email) {
+ ResponseEntity result = apiService.getCall(config.getUsersByEmailUrl()+email);
+ return result;
+ }
+
+ @GetMapping(value = "/createUser")
+ @ResponseBody
+ public ResponseEntity createUser(HttpServletResponse response) {
+ JSONObject request = new JSONObject();
+ request.put("email", "norman.lewis@email.com");
+ request.put("given_name", "Norman");
+ request.put("family_name", "Lewis");
+ request.put("connection", "Username-Password-Authentication");
+ request.put("password", "Pa33w0rd");
+
+ ResponseEntity result = apiService.postCall(config.getUsersUrl(), request.toString());
+ return result;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java
new file mode 100644
index 0000000000..0d8263ae19
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/java/com/baeldung/auth0/service/ApiService.java
@@ -0,0 +1,44 @@
+package com.baeldung.auth0.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import com.baeldung.auth0.controller.AuthController;
+
+@Service
+public class ApiService {
+
+ @Autowired
+ private AuthController controller;
+
+ public ResponseEntity getCall(String url) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer "+controller.getManagementApiToken());
+
+ HttpEntity entity = new HttpEntity(headers);
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity result = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
+
+ return result;
+ }
+
+ public ResponseEntity postCall(String url, String requestBody) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer "+controller.getManagementApiToken());
+
+ HttpEntity request = new HttpEntity(requestBody, headers);
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity result = restTemplate.postForEntity(url, request, String.class);
+
+ return result;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-auth0/src/main/resources/application.properties b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties
new file mode 100644
index 0000000000..45492c5c00
--- /dev/null
+++ b/spring-security-modules/spring-security-auth0/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+com.auth0.domain: dev-example.auth0.com
+com.auth0.clientId: exampleClientId
+com.auth0.clientSecret: exampleClientSecret
+
+com.auth0.managementApi.clientId: exampleManagementApiClientId
+com.auth0.managementApi.clientSecret: exampleManagementApiClientSecret
+com.auth0.managementApi.grantType: client_credentials
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-kerberos/README.md b/spring-security-modules/spring-security-kerberos/README.md
deleted file mode 100644
index a868fb86b7..0000000000
--- a/spring-security-modules/spring-security-kerberos/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## Spring Security Kerberos
-
-This module contains articles about Spring Security Kerberos
-
-### Relevant Articles:
-
-- [Introduction to SPNEGO/Kerberos Authentication in Spring](https://www.baeldung.com/spring-security-kerberos)
-
-### @PreFilter and @PostFilter annotations
-
-### Build the Project ###
-
-`mvn clean install`
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-kerberos/pom.xml b/spring-security-modules/spring-security-kerberos/pom.xml
deleted file mode 100644
index 51a48a78c6..0000000000
--- a/spring-security-modules/spring-security-kerberos/pom.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
- 4.0.0
- spring-security-kerberos
- 0.1-SNAPSHOT
- spring-security-kerberos
- war
-
-
- com.baeldung
- parent-boot-2
- 0.0.1-SNAPSHOT
- ../../parent-boot-2
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-security
-
-
-
- org.springframework.security.kerberos
- spring-security-kerberos-core
- ${spring-security-kerberos.version}
-
-
- org.springframework.security.kerberos
- spring-security-kerberos-web
- ${spring-security-kerberos.version}
-
-
- org.springframework.security.kerberos
- spring-security-kerberos-client
- ${spring-security-kerberos.version}
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-war-plugin
-
-
-
-
-
- 1.0.1.RELEASE
-
-
-
diff --git a/spring-security-modules/spring-security-mvc-boot-1/pom.xml b/spring-security-modules/spring-security-mvc-boot-1/pom.xml
index b00b7bab32..7ad18376ec 100644
--- a/spring-security-modules/spring-security-mvc-boot-1/pom.xml
+++ b/spring-security-modules/spring-security-mvc-boot-1/pom.xml
@@ -106,10 +106,6 @@
${ehcache-core.version}
jar
-
- net.bytebuddy
- byte-buddy
-
diff --git a/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md
index 3aa092edb8..4bb0eea16c 100644
--- a/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md
+++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/README.md
@@ -1,3 +1,4 @@
## Relevant articles:
- [Spring Security Kerberos Integration](https://www.baeldung.com/spring-security-kerberos-integration)
+- [Introduction to SPNEGO/Kerberos Authentication in Spring](https://www.baeldung.com/spring-security-kerberos)
diff --git a/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java
new file mode 100644
index 0000000000..2cddbf0f22
--- /dev/null
+++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung.intro;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java
similarity index 97%
rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java
rename to spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java
index c1c206e5c9..cc694a3b83 100644
--- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/config/WebSecurityConfig.java
+++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/config/WebSecurityConfig.java
@@ -1,6 +1,5 @@
-package com.baeldung.config;
+package com.baeldung.intro.config;
-import com.baeldung.security.DummyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
@@ -16,6 +15,8 @@ import org.springframework.security.kerberos.web.authentication.SpnegoAuthentica
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import com.baeldung.intro.security.DummyUserDetailsService;
+
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
diff --git a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java
similarity index 94%
rename from spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java
rename to spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java
index 6ddd6c8969..f564c9f756 100644
--- a/spring-security-modules/spring-security-kerberos/src/main/java/com/baeldung/security/DummyUserDetailsService.java
+++ b/spring-security-modules/spring-security-sso/spring-security-sso-kerberos/src/main/java/com/baeldung/intro/security/DummyUserDetailsService.java
@@ -1,4 +1,4 @@
-package com.baeldung.security;
+package com.baeldung.intro.security;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
diff --git a/spring-social-login/pom.xml b/spring-social-login/pom.xml
index 628f439cc0..0de20cd087 100644
--- a/spring-social-login/pom.xml
+++ b/spring-social-login/pom.xml
@@ -62,12 +62,6 @@
h2
-
- net.bytebuddy
- byte-buddy-dep
- ${bytebuddy.version}
-
-
@@ -102,7 +96,6 @@
- 1.10.9
2.0.3.RELEASE
diff --git a/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java b/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
index 02bf8a9ee0..c1e3cf7458 100644
--- a/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
+++ b/spring-thymeleaf-3/src/test/java/com/baeldung/thymeleaf/currencies/CurrenciesControllerIntegrationTest.java
@@ -27,7 +27,7 @@ public class CurrenciesControllerIntegrationTest {
.header("Accept-Language", "es-ES")
.param("amount", "10032.5"))
.andExpect(status().isOk())
- .andExpect(content().string(containsString("10.032,50 €")));
+ .andExpect(content().string(containsString("10.032,50")));
}
@Test
@@ -42,10 +42,10 @@ public class CurrenciesControllerIntegrationTest {
@Test
public void whenCallCurrencyWithRomanianLocaleWithArrays_ThenReturnLocaleCurrencies() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/currency")
- .header("Accept-Language", "ro-RO")
+ .header("Accept-Language", "en-GB")
.param("amountList", "10", "20", "30"))
.andExpect(status().isOk())
- .andExpect(content().string(containsString("10,00 RON, 20,00 RON, 30,00 RON")));
+ .andExpect(content().string(containsString("£10.00, £20.00, £30.00")));
}
@Test
diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md
index 329228186f..1a013f5de3 100644
--- a/testing-modules/mockito-2/README.md
+++ b/testing-modules/mockito-2/README.md
@@ -5,4 +5,4 @@
- [Mockito Strict Stubbing and The UnnecessaryStubbingException](https://www.baeldung.com/mockito-unnecessary-stubbing-exception)
- [Mockito and Fluent APIs](https://www.baeldung.com/mockito-fluent-apis)
- [Mocking the ObjectMapper readValue() Method](https://www.baeldung.com/mockito-mock-jackson-read-value)
-- [Introduction to Mockito’s AdditionalAnswers](https://www.baeldung.com/mockito-additionalanswers)
+- [Introduction to Mockito’s AdditionalAnswers](https://www.baeldung.com/mockito-additionalanswers)
\ No newline at end of file
diff --git a/twitter4j/pom.xml b/twitter4j/pom.xml
index 274b5c75c3..0c36e72892 100644
--- a/twitter4j/pom.xml
+++ b/twitter4j/pom.xml
@@ -2,7 +2,7 @@
4.0.0
- twitter4J
+ twitter4j
twitter4J
jar