diff --git a/blade/pom.xml b/blade/pom.xml
index b763f69c85..f4bc6d73be 100644
--- a/blade/pom.xml
+++ b/blade/pom.xml
@@ -84,38 +84,6 @@
-
- com.bazaarvoice.maven.plugins
- process-exec-maven-plugin
- ${process-exec-maven-plugin.version}
-
-
-
- blade-process
- pre-integration-test
-
- start
-
-
- Blade
- false
-
- java
- -jar
- blade.jar
-
-
-
-
-
- stop-all
- post-integration-test
-
- stop-all
-
-
-
-
maven-assembly-plugin
${assembly.plugin.version}
diff --git a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
index a3a5592cd9..3b9db284c8 100644
--- a/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
+++ b/core-java-modules/core-java-11/src/test/java/com/baeldung/java11/httpclient/test/HttpRequestUnitTest.java
@@ -17,8 +17,8 @@ import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
+import org.junit.Ignore;
import org.junit.Test;
-import org.junit.jupiter.api.Disabled;
public class HttpRequestUnitTest {
@@ -51,10 +51,10 @@ public class HttpRequestUnitTest {
/*
* This test will fail as soon as the given URL returns a HTTP 2 response.
- * Therefore, let's leave it commented out.
+ * Therefore, let's leave it ignored.
* */
- @Test
- @Disabled
+ @Test
+ @Ignore
public void shouldFallbackToHttp1_1WhenWebsiteDoesNotUseHttp2() throws IOException, InterruptedException, URISyntaxException, NoSuchAlgorithmException {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
diff --git a/core-java-modules/core-java-16/README.md b/core-java-modules/core-java-16/README.md
new file mode 100644
index 0000000000..760513189f
--- /dev/null
+++ b/core-java-modules/core-java-16/README.md
@@ -0,0 +1,3 @@
+### Relevant articles:
+
+- [Collect a Java Stream to an Immutable Collection](https://www.baeldung.com/java-stream-immutable-collection)
diff --git a/core-java-modules/core-java-16/pom.xml b/core-java-modules/core-java-16/pom.xml
new file mode 100644
index 0000000000..230e342f01
--- /dev/null
+++ b/core-java-modules/core-java-16/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+ core-java-16
+ 0.1.0-SNAPSHOT
+ core-java-16
+ jar
+ http://maven.apache.org
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+ ../../
+
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source.version}
+ ${maven.compiler.target.version}
+
+
+
+
+
+
+ 16
+ 16
+ 3.6.1
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java
new file mode 100644
index 0000000000..afd7369d8d
--- /dev/null
+++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/streams/StreamToImmutableUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.streams;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class StreamToImmutableUnitTest {
+
+ @Test
+ public void whenUsingStreamToList_thenReturnImmutableList() {
+
+ List immutableList = Stream.of("a", "b", "c", "d")
+ .toList();
+
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> {
+ immutableList.add("e");
+ });
+
+ }
+
+}
diff --git a/core-java-modules/core-java-annotations/README.md b/core-java-modules/core-java-annotations/README.md
index 93da3aea62..18f5589771 100644
--- a/core-java-modules/core-java-annotations/README.md
+++ b/core-java-modules/core-java-annotations/README.md
@@ -10,3 +10,5 @@
- [Overview of Java Built-in Annotations](https://www.baeldung.com/java-default-annotations)
- [Creating a Custom Annotation in Java](https://www.baeldung.com/java-custom-annotation)
- [Efficient Word Frequency Calculator in Java](https://www.baeldung.com/java-word-frequency)
+- [Why Missing Annotations Don’t Cause ClassNotFoundException](https://www.baeldung.com/classnotfoundexception-missing-annotation)
+- [Valid @SuppressWarnings Warning Names](https://www.baeldung.com/java-suppresswarnings-valid-names)
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java
new file mode 100644
index 0000000000..aad7cf49eb
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/annotations/ClassWithSuppressWarningsNames.java
@@ -0,0 +1,46 @@
+package com.baeldung.annotations;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings({"serial", "unchecked"})
+public class ClassWithSuppressWarningsNames implements Serializable {
+
+// private static final long serialVersionUID = -1166032307853492833L;
+
+ @SuppressWarnings("unused")
+ public static void suppressBoxingWarning() {
+ int value = 5;
+ int unusedVar = 10; // no warning here
+ List list = new ArrayList<>();
+ list.add(Integer.valueOf(value));
+ }
+
+ @SuppressWarnings("deprecated")
+ void suppressDeprecatedWarning() {
+ ClassWithSuppressWarningsNames cls = new ClassWithSuppressWarningsNames();
+ cls.deprecatedMethod(); // no warning here
+ }
+
+ @SuppressWarnings("fallthrough")
+ String suppressFallthroughWarning() {
+ int day = 5;
+ switch (day) {
+ case 5:
+ return "This is day 5";
+// break; // no warning here
+ case 10:
+ return "This is day 10";
+// break;
+ default:
+ return "This default day";
+ }
+ }
+
+ @Deprecated
+ String deprecatedMethod() {
+ return "deprecated method";
+ }
+
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java
new file mode 100644
index 0000000000..daf9f60b96
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/A.java
@@ -0,0 +1,8 @@
+package com.baeldung.missingannotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface A {
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java
new file mode 100644
index 0000000000..a86b2f72ec
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/B.java
@@ -0,0 +1,10 @@
+package com.baeldung.missingannotation;
+
+@A
+@C(D.class)
+public class B {
+
+ public static void main(String[] args) {
+ System.out.println("It worked");
+ }
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java
new file mode 100644
index 0000000000..2adf2b42fa
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/C.java
@@ -0,0 +1,9 @@
+package com.baeldung.missingannotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface C {
+ Class> value();
+}
diff --git a/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java
new file mode 100644
index 0000000000..7c534fa682
--- /dev/null
+++ b/core-java-modules/core-java-annotations/src/main/java/com/baeldung/missingannotation/D.java
@@ -0,0 +1,4 @@
+package com.baeldung.missingannotation;
+
+public class D {
+}
diff --git a/core-java-modules/core-java-collections-4/README.md b/core-java-modules/core-java-collections-4/README.md
index 4fd77473d4..0e14f407c4 100644
--- a/core-java-modules/core-java-collections-4/README.md
+++ b/core-java-modules/core-java-collections-4/README.md
@@ -6,3 +6,4 @@
- [ArrayList vs. LinkedList vs. HashMap in Java](https://www.baeldung.com/java-arraylist-vs-linkedlist-vs-hashmap)
- [Java Deque vs. Stack](https://www.baeldung.com/java-deque-vs-stack)
+- [Collection.toArray(new T[0]) or .toArray(new T[size])](https://www.baeldung.com/java-collection-toarray-methods)
diff --git a/core-java-modules/core-java-concurrency-advanced-4/README.md b/core-java-modules/core-java-concurrency-advanced-4/README.md
index 5b93cec0dd..db856a2cd6 100644
--- a/core-java-modules/core-java-concurrency-advanced-4/README.md
+++ b/core-java-modules/core-java-concurrency-advanced-4/README.md
@@ -2,3 +2,4 @@
- [Binary Semaphore vs Reentrant Lock](https://www.baeldung.com/java-binary-semaphore-vs-reentrant-lock)
- [Bad Practices With Synchronization](https://www.baeldung.com/java-synchronization-bad-practices)
+- [Start Two Threads at the Exact Same Time in Java](https://www.baeldung.com/java-start-two-threads-at-same-time)
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java
new file mode 100644
index 0000000000..3e90bd4399
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/ThreadsStartAtSameTime.java
@@ -0,0 +1,86 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Phaser;
+
+public class ThreadsStartAtSameTime {
+
+ public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
+ usingCountDownLatch();
+
+ Thread.sleep(30);
+
+ usingCyclicBarrier();
+
+ Thread.sleep(30);
+
+ usingPhaser();
+
+ }
+
+ private static void usingCountDownLatch() throws InterruptedException {
+ System.out.println("===============================================");
+ System.out.println(" >>> Using CountDownLatch <<<<");
+ System.out.println("===============================================");
+
+ CountDownLatch latch = new CountDownLatch(1);
+
+ WorkerWithCountDownLatch worker1 = new WorkerWithCountDownLatch("Worker with latch 1", latch);
+ WorkerWithCountDownLatch worker2 = new WorkerWithCountDownLatch("Worker with latch 2", latch);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now release the latch:");
+ System.out.println("-----------------------------------------------");
+ latch.countDown();
+ }
+
+ private static void usingCyclicBarrier() throws BrokenBarrierException, InterruptedException {
+ System.out.println("\n===============================================");
+ System.out.println(" >>> Using CyclicBarrier <<<<");
+ System.out.println("===============================================");
+
+ CyclicBarrier barrier = new CyclicBarrier(3);
+
+ WorkerWithCyclicBarrier worker1 = new WorkerWithCyclicBarrier("Worker with barrier 1", barrier);
+ WorkerWithCyclicBarrier worker2 = new WorkerWithCyclicBarrier("Worker with barrier 2", barrier);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now open the barrier:");
+ System.out.println("-----------------------------------------------");
+ barrier.await();
+ }
+
+ private static void usingPhaser() throws InterruptedException {
+ System.out.println("\n===============================================");
+ System.out.println(" >>> Using Phaser <<<");
+ System.out.println("===============================================");
+
+ Phaser phaser = new Phaser();
+ phaser.register();
+
+ WorkerWithPhaser worker1 = new WorkerWithPhaser("Worker with phaser 1", phaser);
+ WorkerWithPhaser worker2 = new WorkerWithPhaser("Worker with phaser 2", phaser);
+
+ worker1.start();
+ worker2.start();
+
+ Thread.sleep(10);//simulation of some actual work
+
+ System.out.println("-----------------------------------------------");
+ System.out.println(" Now open the phaser barrier:");
+ System.out.println("-----------------------------------------------");
+ phaser.arriveAndAwaitAdvance();
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java
new file mode 100644
index 0000000000..44b95ec042
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCountDownLatch.java
@@ -0,0 +1,24 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+public class WorkerWithCountDownLatch extends Thread {
+ private CountDownLatch latch;
+
+ public WorkerWithCountDownLatch(String name, CountDownLatch latch) {
+ this.latch = latch;
+ setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the latch\n", getName());
+ latch.await();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java
new file mode 100644
index 0000000000..99059e0725
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithCyclicBarrier.java
@@ -0,0 +1,25 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+public class WorkerWithCyclicBarrier extends Thread {
+ private CyclicBarrier barrier;
+
+ public WorkerWithCyclicBarrier(String name, CyclicBarrier barrier) {
+ this.barrier = barrier;
+ this.setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the barrier\n", getName());
+ barrier.await();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (InterruptedException | BrokenBarrierException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java
new file mode 100644
index 0000000000..44994d1cd0
--- /dev/null
+++ b/core-java-modules/core-java-concurrency-advanced-4/src/main/java/com/baeldung/threadsstartatsametime/WorkerWithPhaser.java
@@ -0,0 +1,26 @@
+package com.baeldung.threadsstartatsametime;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Phaser;
+
+public class WorkerWithPhaser extends Thread {
+ private Phaser phaser;
+
+ public WorkerWithPhaser(String name, Phaser phaser) {
+ this.phaser = phaser;
+ phaser.register();
+ setName(name);
+ }
+
+ @Override public void run() {
+ try {
+ System.out.printf("[ %s ] created, blocked by the phaser\n", getName());
+ phaser.arriveAndAwaitAdvance();
+ System.out.printf("[ %s ] starts at: %s\n", getName(), Instant.now());
+ // do actual work here...
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/core-java-modules/core-java-concurrency-basic-2/README.md b/core-java-modules/core-java-concurrency-basic-2/README.md
index c4a6105c56..d2ff75ca95 100644
--- a/core-java-modules/core-java-concurrency-basic-2/README.md
+++ b/core-java-modules/core-java-concurrency-basic-2/README.md
@@ -13,4 +13,5 @@ This module contains articles about basic Java concurrency
- [Why are Local Variables Thread-Safe in Java](https://www.baeldung.com/java-local-variables-thread-safe)
- [How to Stop Execution After a Certain Time in Java](https://www.baeldung.com/java-stop-execution-after-certain-time)
- [How to Handle InterruptedException in Java](https://www.baeldung.com/java-interrupted-exception)
+- [How to Get the Number of Threads in a Java Process](https://www.baeldung.com/java-get-number-of-threads)
- [[<-- Prev]](/core-java-modules/core-java-concurrency-basic)
diff --git a/core-java-modules/core-java-io-conversions-2/pom.xml b/core-java-modules/core-java-io-conversions-2/pom.xml
index e594666b55..dcb9d494dc 100644
--- a/core-java-modules/core-java-io-conversions-2/pom.xml
+++ b/core-java-modules/core-java-io-conversions-2/pom.xml
@@ -36,6 +36,16 @@
core-java-io-conversions-2
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${source.version}
+ ${target.version}
+
+
+
src/main/resources
@@ -45,6 +55,8 @@
+ 11
+ 11
20200518
4.1
diff --git a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
index c34c32891f..03f528766b 100644
--- a/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
+++ b/core-java-modules/core-java-io-conversions-2/src/test/java/com/baeldung/inputstreamtostring/JavaInputStreamToXUnitTest.java
@@ -21,11 +21,11 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
-import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;
import java.util.UUID;
@@ -70,6 +70,16 @@ public class JavaInputStreamToXUnitTest {
assertThat(text, equalTo(originalString));
}
+ @Test
+ public void givenUsingJava9_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException {
+ final String originalString = randomAlphabetic(DEFAULT_SIZE);
+ final InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
+
+ final String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+
+ assertThat(text, equalTo(originalString));
+ }
+
@Test
public final void givenUsingJava7_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException {
final String originalString = randomAlphabetic(DEFAULT_SIZE);
@@ -155,15 +165,13 @@ public class JavaInputStreamToXUnitTest {
@Test
public final void whenConvertingToFile_thenCorrect() throws IOException {
- final InputStream initialStream = new FileInputStream(new File("src/test/resources/sample.txt"));
- final byte[] buffer = new byte[initialStream.available()];
- initialStream.read(buffer);
+ final Path path = Paths.get("src/test/resources/sample.txt");
+ final byte[] buffer = java.nio.file.Files.readAllBytes(path);
final File targetFile = new File("src/test/resources/targetFile.tmp");
final OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
- IOUtils.closeQuietly(initialStream);
IOUtils.closeQuietly(outStream);
}
diff --git a/core-java-modules/core-java-lang-4/README.md b/core-java-modules/core-java-lang-4/README.md
index 18bbf43647..562be950c0 100644
--- a/core-java-modules/core-java-lang-4/README.md
+++ b/core-java-modules/core-java-lang-4/README.md
@@ -7,3 +7,4 @@ This module contains articles about core features in the Java language
- [What are Compile-time Constants in Java?](https://www.baeldung.com/java-compile-time-constants)
- [Java Objects.hash() vs Objects.hashCode()](https://www.baeldung.com/java-objects-hash-vs-objects-hashcode)
- [Referencing a Method in Javadoc Comments](https://www.baeldung.com/java-method-in-javadoc)
+- [Tiered Compilation in JVM](https://www.baeldung.com/jvm-tiered-compilation)
diff --git a/core-java-modules/core-java-lang-4/pom.xml b/core-java-modules/core-java-lang-4/pom.xml
index 4e692ffa55..46ad335568 100644
--- a/core-java-modules/core-java-lang-4/pom.xml
+++ b/core-java-modules/core-java-lang-4/pom.xml
@@ -21,6 +21,16 @@
jmh-core
${jmh-core.version}
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson.version}
+
org.openjdk.jmh
jmh-generator-annprocess
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java
new file mode 100644
index 0000000000..2eca40f055
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Article.java
@@ -0,0 +1,21 @@
+package com.baeldung.tieredcompilation;
+
+public class Article {
+
+ private String name;
+ private String author;
+
+ public Article(String name, String author) {
+ this.name = name;
+ this.author = author;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java
new file mode 100644
index 0000000000..ee5d74d5fe
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/Formatter.java
@@ -0,0 +1,7 @@
+package com.baeldung.tieredcompilation;
+
+public interface Formatter {
+
+ String format(T object) throws Exception;
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java
new file mode 100644
index 0000000000..9951b477da
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/JsonFormatter.java
@@ -0,0 +1,15 @@
+package com.baeldung.tieredcompilation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
+public class JsonFormatter implements Formatter {
+
+ private static final JsonMapper mapper = new JsonMapper();
+
+ @Override
+ public String format(T object) throws JsonProcessingException {
+ return mapper.writeValueAsString(object);
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java
new file mode 100644
index 0000000000..ea7178051e
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/TieredCompilation.java
@@ -0,0 +1,17 @@
+package com.baeldung.tieredcompilation;
+
+public class TieredCompilation {
+
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 1_000_000; i++) {
+ Formatter formatter;
+ if (i < 500_000) {
+ formatter = new JsonFormatter();
+ } else {
+ formatter = new XmlFormatter();
+ }
+ formatter.format(new Article("Tiered Compilation in JVM", "Baeldung"));
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java
new file mode 100644
index 0000000000..8b9823cd88
--- /dev/null
+++ b/core-java-modules/core-java-lang-4/src/main/java/com/baeldung/tieredcompilation/XmlFormatter.java
@@ -0,0 +1,15 @@
+package com.baeldung.tieredcompilation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+
+public class XmlFormatter implements Formatter {
+
+ private static final XmlMapper mapper = new XmlMapper();
+
+ @Override
+ public String format(T object) throws JsonProcessingException {
+ return mapper.writeValueAsString(object);
+ }
+
+}
diff --git a/core-java-modules/core-java-lang-oop-constructors/README.md b/core-java-modules/core-java-lang-oop-constructors/README.md
index 0082969807..4bec8db256 100644
--- a/core-java-modules/core-java-lang-oop-constructors/README.md
+++ b/core-java-modules/core-java-lang-oop-constructors/README.md
@@ -5,4 +5,5 @@ This module contains article about constructors in Java
### Relevant Articles:
- [A Guide to Constructors in Java](https://www.baeldung.com/java-constructors)
- [Java Copy Constructor](https://www.baeldung.com/java-copy-constructor)
-- [Cannot Reference “X” Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error)
\ No newline at end of file
+- [Cannot Reference “X” Before Supertype Constructor Has Been Called](https://www.baeldung.com/java-cannot-reference-x-before-supertype-constructor-error)
+- [Private Constructors in Java](https://www.baeldung.com/java-private-constructors)
diff --git a/core-java-modules/core-java-lang-oop-modifiers/README.md b/core-java-modules/core-java-lang-oop-modifiers/README.md
index eef905fa0e..1f37cc903e 100644
--- a/core-java-modules/core-java-lang-oop-modifiers/README.md
+++ b/core-java-modules/core-java-lang-oop-modifiers/README.md
@@ -9,4 +9,5 @@ This module contains articles about modifiers in Java
- [The “final” Keyword in Java](https://www.baeldung.com/java-final)
- [A Guide to the Static Keyword in Java](https://www.baeldung.com/java-static)
- [Static and Default Methods in Interfaces in Java](https://www.baeldung.com/java-static-default-methods)
-- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp)
\ No newline at end of file
+- [The strictfp Keyword in Java](https://www.baeldung.com/java-strictfp)
+- [Static Classes Versus the Singleton Pattern in Java](https://www.baeldung.com/java-static-class-vs-singleton)
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java
new file mode 100644
index 0000000000..d472e386eb
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/CachingSingleton.java
@@ -0,0 +1,30 @@
+package com.baeldung.staticsingletondifference;
+
+public class CachingSingleton implements SingletonInterface {
+
+ private CachingSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final CachingSingleton instance = new CachingSingleton();
+ }
+
+ public static CachingSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ return "Caching Responsibilities";
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ return lock.takeLock(1);
+ }
+
+ @Override
+ public void increment() {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java
new file mode 100644
index 0000000000..84ea5d4d1a
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/FileSystemSingleton.java
@@ -0,0 +1,36 @@
+package com.baeldung.staticsingletondifference;
+
+public class FileSystemSingleton implements SingletonInterface{
+
+ private int filesWritten;
+
+ private FileSystemSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final FileSystemSingleton instance = new FileSystemSingleton();
+ }
+
+ public static FileSystemSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ return "File System Responsibilities";
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ return lock.takeLock(2);
+ }
+
+ @Override
+ public void increment() {
+ this.filesWritten++;
+ }
+
+ public int getFilesWritten() {
+ return filesWritten;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java
new file mode 100644
index 0000000000..3a124bd678
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/MyLock.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class MyLock {
+
+ protected String takeLock(int locks) {
+ return "Taken Specific Lock";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java
new file mode 100644
index 0000000000..64a422f9ec
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SerializableCloneableSingleton.java
@@ -0,0 +1,49 @@
+package com.baeldung.staticsingletondifference;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+public class SerializableCloneableSingleton implements SingletonInterface, Serializable, Cloneable {
+
+ private static final long serialVersionUID = -1917003064592196223L;
+
+ private int state;
+
+ private SerializableCloneableSingleton() {
+ }
+
+ private static class SingletonHolder {
+ public static final SerializableCloneableSingleton instance = new SerializableCloneableSingleton();
+ }
+
+ public static SerializableCloneableSingleton getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String describeMe() {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+
+ @Override
+ public String passOnLocks(MyLock lock) {
+ throw new UnsupportedOperationException("Not Supported Here");
+ }
+
+ @Override
+ public void increment() {
+ this.state++;
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return SingletonHolder.instance;
+ }
+
+ public Object cloneObject() throws CloneNotSupportedException {
+ return this.clone();
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java
new file mode 100644
index 0000000000..34e14c5cac
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonInterface.java
@@ -0,0 +1,10 @@
+package com.baeldung.staticsingletondifference;
+
+public interface SingletonInterface {
+
+ public String describeMe();
+
+ public String passOnLocks(MyLock lock);
+
+ public void increment();
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java
new file mode 100644
index 0000000000..35d9931597
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SingletonLock.java
@@ -0,0 +1,20 @@
+package com.baeldung.staticsingletondifference;
+
+public class SingletonLock extends MyLock {
+
+ private SingletonLock() {
+ }
+
+ private static class SingletonHolder {
+ public static final SingletonLock instance = new SingletonLock();
+ }
+
+ public static SingletonLock getInstance() {
+ return SingletonHolder.instance;
+ }
+
+ @Override
+ public String takeLock(int locks) {
+ return "Taken Singleton Lock";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java
new file mode 100644
index 0000000000..c9661cbf70
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SubUtility.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class SubUtility extends SuperUtility {
+
+ public static String echoIt(String data) {
+ return data;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java
new file mode 100644
index 0000000000..00011f4697
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/main/java/com/baeldung/staticsingletondifference/SuperUtility.java
@@ -0,0 +1,8 @@
+package com.baeldung.staticsingletondifference;
+
+public class SuperUtility {
+
+ public static String echoIt(String data) {
+ return "SUPER";
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java
new file mode 100644
index 0000000000..4b0b23b5ec
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-modifiers/src/test/java/com/baeldung/staticsingletondifference/ForSingletonsUnitTest.java
@@ -0,0 +1,77 @@
+package com.baeldung.staticsingletondifference;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.stream.IntStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ForSingletonsUnitTest {
+
+ @Test
+ public void whenStaticUtilClassInheritance_thenOverridingFails() {
+ SuperUtility superUtility = new SubUtility();
+ Assert.assertNotEquals("ECHO", superUtility.echoIt("ECHO"));
+ Assert.assertEquals("SUPER", superUtility.echoIt("ECHO"));
+ }
+
+ @Test
+ public void whenSingletonDerivesBaseClass_thenRuntimePolymorphism() {
+ MyLock myLock = new MyLock();
+ Assert.assertEquals("Taken Specific Lock", myLock.takeLock(10));
+ myLock = SingletonLock.getInstance();
+ Assert.assertEquals("Taken Singleton Lock", myLock.takeLock(10));
+ }
+
+ @Test
+ public void whenSingletonImplementsInterface_thenRuntimePolymorphism() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ Assert.assertEquals("File System Responsibilities", singleton.describeMe());
+ singleton = CachingSingleton.getInstance();
+ Assert.assertEquals("Caching Responsibilities", singleton.describeMe());
+ }
+
+ @Test
+ public void whenSingleton_thenPassAsArguments() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ Assert.assertEquals("Taken Singleton Lock", singleton.passOnLocks(SingletonLock.getInstance()));
+ }
+
+ @Test
+ public void whenSingleton_thenAllowState() {
+ SingletonInterface singleton = FileSystemSingleton.getInstance();
+ IntStream.range(0, 5)
+ .forEach(i -> singleton.increment());
+ Assert.assertEquals(5, ((FileSystemSingleton) singleton).getFilesWritten());
+ }
+
+ @Test
+ public void whenSingleton_thenAllowSerializationDeserialization() {
+ SingletonInterface singleton = SerializableCloneableSingleton.getInstance();
+ singleton.increment();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ new ObjectOutputStream(baos).writeObject(singleton);
+ SerializableCloneableSingleton singletonNew = (SerializableCloneableSingleton) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject();
+ Assert.assertEquals(1, singletonNew.getState());
+ Assert.assertEquals(singleton.hashCode(), singletonNew.hashCode());
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void whenSingleton_thenAllowCloneable() {
+ SerializableCloneableSingleton singleton = SerializableCloneableSingleton.getInstance();
+ singleton.increment();
+ try {
+ Assert.assertEquals(2, ((SerializableCloneableSingleton) singleton.cloneObject()).getState());
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-lang-operators-2/README.md b/core-java-modules/core-java-lang-operators-2/README.md
index 46c874f361..1a4c01c6e5 100644
--- a/core-java-modules/core-java-lang-operators-2/README.md
+++ b/core-java-modules/core-java-lang-operators-2/README.md
@@ -3,3 +3,5 @@
This module contains articles about Java operators
## Relevant Articles:
+
+- [Logical vs Bitwise OR Operator](https://www.baeldung.com/java-logical-vs-bitwise-or-operator)
diff --git a/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java
new file mode 100644
index 0000000000..9af59d7c1c
--- /dev/null
+++ b/core-java-modules/core-java-lang-operators-2/src/test/java/com/baeldung/oroperators/BitmaskingUnitTest.java
@@ -0,0 +1,120 @@
+package com.baeldung.oroperators;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class BitmaskingUnitTest {
+
+ @Test
+ public void givenIntegerShouldPrintBinaryRepresentation() {
+ // given
+ int intRepresentation = 1094795523;
+
+ // expected
+ String stringRepresentation = getStringRepresentation(intRepresentation);
+ assertEquals(stringRepresentation, "01000001010000010100000100000011");
+ }
+
+ private String getStringRepresentation(int intRepresentation) {
+ String binaryString = Integer.toBinaryString(intRepresentation);
+ return padWithZeros(binaryString);
+ }
+
+ private String padWithZeros(String binaryString) {
+ return String.format("%" + 32 + "s", binaryString).replace(' ', '0');
+ }
+
+ @Test
+ public void givenBinaryRepresentationShouldReturnNumber() {
+ // given
+ String stringRepresentation = "01000001010000010100000100000011";
+
+ // expected
+ int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2);
+ assertEquals(intRepresentation, 1094795523);
+ }
+
+ @Test
+ public void givenBinaryRepresentationShouldReturnCharacter() {
+ // given
+ String stringRepresentation = "010000010100000101000001";
+
+ // expected
+ assertEquals(binaryToText(stringRepresentation), "AAA");
+ }
+
+ @Test
+ public void givenIntAndPositionNumberShouldReturnBitValue() {
+ // given
+ String stringRepresentation = "010000010100000101000001000000011";
+ int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2);
+
+ // when
+ boolean value1 = extractValueAtPosition(intRepresentation, 1);
+ boolean value2 = extractValueAtPosition(intRepresentation, 2);
+ boolean value3 = extractValueAtPosition(intRepresentation, 3);
+
+ // then
+ assertTrue(value1);
+ assertTrue(value2);
+ assertFalse(value3);
+
+ }
+
+ @Test
+ public void givenIntegerShouldExtractLastThreeBytes() {
+ // given
+ int intRepresentation = 1094795523;
+
+ // when
+ int lastThreeBites = intRepresentation >> 8;
+
+ // expected
+ String stringRepresentation = getStringRepresentation(lastThreeBites);
+ assertEquals(stringRepresentation, "00000000010000010100000101000001");
+ assertEquals(binaryToText(stringRepresentation), "AAA");
+ }
+
+ @Test
+ public void givenIntegerShouldApplyMask() {
+ // given
+ int intRepresentation = Integer.parseUnsignedInt("00000000010000010100000101000001", 2);
+ int mask = Integer.parseUnsignedInt("00000000000000000000000000000011", 2);
+ int mask2 = Integer.parseUnsignedInt("00000000000000000000000000000001", 2);
+
+ // when
+ int masked = intRepresentation & mask;
+ int masked2 = intRepresentation & mask2;
+
+ // expected
+ assertEquals(getStringRepresentation(masked), "00000000000000000000000000000001");
+ assertEquals(getStringRepresentation(masked2), "00000000000000000000000000000001");
+ assertNotEquals(masked, mask);
+ assertEquals(masked2, mask2);
+ assertFalse((intRepresentation & mask) == mask);
+ }
+
+ private boolean extractValueAtPosition(int intRepresentation, int position) {
+ String mask = getStringRepresentation(1 << (position - 1));
+ System.out.println(getStringRepresentation(intRepresentation));
+ System.out.println(mask);
+ System.out.println("-------------------------------- &");
+ System.out.println(getStringRepresentation((intRepresentation) & (1 << (position - 1))));
+ System.out.println();
+ return ((intRepresentation) & (1 << (position - 1))) != 0;
+ }
+
+ private static String binaryToText(String stringRepresentation) {
+ return Arrays.stream(stringRepresentation.split("(?<=\\G.{8})"))
+ .filter(eightBits -> !eightBits.equals("00000000"))
+ .map(eightBits -> (char)Integer.parseInt(eightBits, 2))
+ .collect(
+ StringBuilder::new,
+ StringBuilder::append,
+ StringBuilder::append
+ ).toString();
+ }
+}
diff --git a/core-java-modules/core-java-networking-3/pom.xml b/core-java-modules/core-java-networking-3/pom.xml
index 0ad800e173..d3b2398b75 100644
--- a/core-java-modules/core-java-networking-3/pom.xml
+++ b/core-java-modules/core-java-networking-3/pom.xml
@@ -47,6 +47,24 @@
javax.mail
1.6.2
+
+
+ com.github.seancfoley
+ ipaddress
+ ${seancfoley.ipaddress.version}
+
+
+
+ com.github.jgonian
+ commons-ip-math
+ ${jgonian.commons-ip-math.version}
+
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ ${googlecode.ipv6.version}
+
@@ -66,6 +84,9 @@
9.4.31.v20200723
10.0.0-M7
3.11.1
+ 5.3.3
+ 1.32
+ 0.17
\ No newline at end of file
diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java
new file mode 100644
index 0000000000..634c88ccba
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheck.java
@@ -0,0 +1,81 @@
+package com.baeldung.ipingivenrange;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressSeqRange;
+import inet.ipaddr.IPAddressString;
+import inet.ipaddr.AddressStringException;
+
+import com.github.jgonian.ipmath.Ipv4;
+import com.github.jgonian.ipmath.Ipv4Range;
+import com.github.jgonian.ipmath.Ipv6;
+import com.github.jgonian.ipmath.Ipv6Range;
+import com.googlecode.ipv6.IPv6Address;
+import com.googlecode.ipv6.IPv6AddressRange;
+
+public class IPWithGivenRangeCheck {
+
+ // using IPAddress library
+ public static boolean checkIPIsInGivenRange(String inputIP, String rangeStartIP, String rangeEndIP) throws AddressStringException {
+ IPAddress startIPAddress = new IPAddressString(rangeStartIP).getAddress();
+ IPAddress endIPAddress = new IPAddressString(rangeEndIP).getAddress();
+ IPAddressSeqRange ipRange = startIPAddress.toSequentialRange(endIPAddress);
+ IPAddress inputIPAddress = new IPAddressString(inputIP).toAddress();
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // using Commons IP Math library for IPv4
+ public static boolean checkIPv4IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) {
+ Ipv4 startIPAddress = Ipv4.of(rangeStartIP);
+ Ipv4 endIPAddress = Ipv4.of(rangeEndIP);
+ Ipv4Range ipRange = Ipv4Range.from(startIPAddress)
+ .to(endIPAddress);
+ Ipv4 inputIPAddress = Ipv4.of(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // using Commons IP Math library for IPv6
+ public static boolean checkIPv6IsInRange(String inputIP, String rangeStartIP, String rangeEndIP) {
+ Ipv6 startIPAddress = Ipv6.of(rangeStartIP);
+ Ipv6 endIPAddress = Ipv6.of(rangeEndIP);
+ Ipv6Range ipRange = Ipv6Range.from(startIPAddress)
+ .to(endIPAddress);
+ Ipv6 inputIPAddress = Ipv6.of(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+
+ // checking IP is in range by converting it to an integer
+ public static boolean checkIPv4IsInRangeByConvertingToInt(String inputIP, String rangeStartIP, String rangeEndIP) throws UnknownHostException {
+ long startIPAddress = ipToLongInt(InetAddress.getByName(rangeStartIP));
+ long endIPAddress = ipToLongInt(InetAddress.getByName(rangeEndIP));
+ long inputIPAddress = ipToLongInt(InetAddress.getByName(inputIP));
+
+ return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
+ }
+
+ private static long ipToLongInt(InetAddress ipAddress) {
+ long resultIP = 0;
+ byte[] ipAddressOctets = ipAddress.getAddress();
+
+ for (byte octet : ipAddressOctets) {
+ resultIP <<= 8;
+ resultIP |= octet & 0xFF;
+ }
+ return resultIP;
+ }
+
+ // using Java IPv6 library (which internally uses two long integers to store ip address)
+ public static boolean checkIPv6IsInRangeByIPv6library(String inputIP, String rangeStartIP, String rangeEndIP) {
+ IPv6Address startIPAddress = IPv6Address.fromString(rangeStartIP);
+ IPv6Address endIPAddress = IPv6Address.fromString(rangeEndIP);
+ IPv6AddressRange ipRange = IPv6AddressRange.fromFirstAndLast(startIPAddress, endIPAddress);
+ IPv6Address inputIPAddress = IPv6Address.fromString(inputIP);
+
+ return ipRange.contains(inputIPAddress);
+ }
+}
diff --git a/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java
new file mode 100644
index 0000000000..a9b54834d5
--- /dev/null
+++ b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/ipingivenrange/IPWithGivenRangeCheckUnitTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.ipingivenrange;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.ipingivenrange.IPWithGivenRangeCheck;
+
+class IPWithGivenRangeCheckUnitTest {
+
+ @Test
+ void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
+ // test for IPAddress library
+ assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for Common IP Math library
+ assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for IPv4 by converting it to an integer and checking if it falls under the specified range.
+ assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.220.3.0", "192.210.0.0", "192.255.0.0"));
+ }
+
+ @Test
+ void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
+ // test for IPAddress library
+ assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for Common IP Math library
+ assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+
+ // test for IPv4 by converting it to an integer and checking if it falls under the specified range.
+ assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.200.0.0", "192.210.0.0", "192.255.0.0"));
+ }
+
+ @Test
+ void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
+ // test for IPAddress library
+ assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Common IP Math library
+ assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRange("2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Java IPv6 library
+ assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("fe80::226:2dff:fefa:dcba", "fe80::226:2dff:fefa:cd1f", "fe80::226:2dff:fefa:ffff"));
+ }
+
+ @Test
+ void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
+ // test for IPAddress library
+ assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Common IP Math library
+ assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRange("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+
+ // test for Java IPv6 library
+ assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library("2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
+ }
+}
diff --git a/core-java-modules/core-java-regex-2/README.md b/core-java-modules/core-java-regex-2/README.md
index 7920ac456d..25656c3cb1 100644
--- a/core-java-modules/core-java-regex-2/README.md
+++ b/core-java-modules/core-java-regex-2/README.md
@@ -1,3 +1,5 @@
### Relevant Articles:
- [Non-Capturing Regex Groups in Java](https://www.baeldung.com/java-regex-non-capturing-groups)
+- [Lookahead and Lookbehind in Java Regex](https://www.baeldung.com/java-regex-lookahead-lookbehind)
+- [Converting Camel Case and Title Case to Words in Java](https://www.baeldung.com/java-camel-case-title-case-to-words)
diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java
new file mode 100644
index 0000000000..ea6593495f
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/CamelCaseToWords.java
@@ -0,0 +1,27 @@
+package com.baeldung.regex.camelcasetowords;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Convert a string in camelCase or TitleCase into a list of words
+ */
+public class CamelCaseToWords {
+ private static final Pattern WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))");
+
+ /**
+ * Find the words in mixed case string like ThisIsText or HereIsSomeText
+ * @param text the text to parse
+ * @return the list of words to process
+ */
+ public static List findWordsInMixedCase(String text) {
+ Matcher matcher = WORD_FINDER.matcher(text);
+ List words = new ArrayList<>();
+ while (matcher.find()) {
+ words.add(matcher.group(0));
+ }
+ return words;
+ }
+}
diff --git a/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java
new file mode 100644
index 0000000000..f87e59c6cc
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/main/java/com/baeldung/regex/camelcasetowords/Recapitalize.java
@@ -0,0 +1,43 @@
+package com.baeldung.regex.camelcasetowords;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class Recapitalize {
+ private static final Set STOP_WORDS = Stream.of("a", "an", "the", "and",
+ "but", "for", "at", "by", "to", "or")
+ .collect(Collectors.toSet());
+
+ public static String sentenceCase(List words) {
+ List capitalized = new ArrayList<>();
+ for (int i = 0; i < words.size(); i++) {
+ String currentWord = words.get(i);
+ if (i == 0) {
+ capitalized.add(capitalizeFirst(currentWord));
+ } else {
+ capitalized.add(currentWord.toLowerCase());
+ }
+ }
+ return String.join(" ", capitalized) + ".";
+ }
+
+ public static String capitalizeMyTitle(List words) {
+ List capitalized = new ArrayList<>();
+ for (int i = 0; i < words.size(); i++) {
+ String currentWord = words.get(i);
+ if (i == 0 || !STOP_WORDS.contains(currentWord.toLowerCase())) {
+ capitalized.add(capitalizeFirst(currentWord));
+ } else {
+ capitalized.add(currentWord.toLowerCase());
+ }
+ }
+ return String.join(" ", capitalized);
+ }
+
+ private static String capitalizeFirst(String word) {
+ return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
+ }
+}
diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java
new file mode 100644
index 0000000000..a843945bba
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/CamelCaseToWordsUnitTest.java
@@ -0,0 +1,39 @@
+package com.baeldung.regex.camelcasetowords;
+
+import org.junit.jupiter.api.Test;
+
+import static com.baeldung.regex.camelcasetowords.CamelCaseToWords.findWordsInMixedCase;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CamelCaseToWordsUnitTest {
+
+ @Test
+ void givenPlainStringWithNonLetters_thenFindsWords() {
+ assertThat(findWordsInMixedCase("some words"))
+ .containsExactly("some", "words");
+ }
+
+ @Test
+ void givenWordsInCamelCase_thenFindsWords() {
+ assertThat(findWordsInMixedCase("thisIsCamelCaseText"))
+ .containsExactly("this", "Is", "Camel", "Case", "Text");
+ }
+
+ @Test
+ void givenWordsInTitleCase_thenFindsWords() {
+ assertThat(findWordsInMixedCase("ThisIsTitleCaseText"))
+ .containsExactly("This", "Is", "Title", "Case", "Text");
+ }
+
+ @Test
+ void givenWordsAcrossMultipleTexts_thenFindsWords() {
+ assertThat(findWordsInMixedCase("ThisIsTitleCaseText --- andSoIsThis"))
+ .containsExactly("This", "Is", "Title", "Case", "Text", "and", "So", "Is", "This");
+ }
+
+ @Test
+ void givenCamelCaseHasASingleLetterWord_thenItCanBeSplit() {
+ assertThat(findWordsInMixedCase("thisHasASingleLetterWord"))
+ .containsExactly("this", "Has", "A", "Single", "Letter", "Word");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java
new file mode 100644
index 0000000000..32c8f39372
--- /dev/null
+++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/camelcasetowords/RecapitalizeUnitTest.java
@@ -0,0 +1,35 @@
+package com.baeldung.regex.camelcasetowords;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static com.baeldung.regex.camelcasetowords.Recapitalize.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RecapitalizeUnitTest {
+
+ @Test
+ void givenWords_thenCanComposeSentence() {
+ assertThat(sentenceCase(Arrays.asList("these", "Words", "Form", "A", "Sentence")))
+ .isEqualTo("These words form a sentence.");
+ }
+
+ @Test
+ void givenNonStopWords_thenTitleIsComposed() {
+ assertThat(capitalizeMyTitle(Arrays.asList("title", "words", "capitalize")))
+ .isEqualTo("Title Words Capitalize");
+ }
+
+ @Test
+ void givenStopWords_thenTitleHasThemInLowerCase() {
+ assertThat(capitalizeMyTitle(Arrays.asList("this", "is", "A", "title", "with", "a", "stop", "word", "or", "two")))
+ .isEqualTo("This Is a Title With a Stop Word or Two");
+ }
+
+ @Test
+ void givenStopWordIsFirstWord_thenTitleHasItCapitalized() {
+ assertThat(capitalizeMyTitle(Arrays.asList("a", "stop", "word", "first")))
+ .isEqualTo("A Stop Word First");
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-security-2/README.md b/core-java-modules/core-java-security-2/README.md
index 684f2504cc..680a7de925 100644
--- a/core-java-modules/core-java-security-2/README.md
+++ b/core-java-modules/core-java-security-2/README.md
@@ -17,4 +17,5 @@ This module contains articles about core Java Security
- [InvalidAlgorithmParameterException: Wrong IV Length](https://www.baeldung.com/java-invalidalgorithmparameter-exception)
- [The java.security.egd JVM Option](https://www.baeldung.com/java-security-egd)
- [RSA in Java](https://www.baeldung.com/java-rsa)
+- [3DES in Java](https://www.baeldung.com/java-3des)
- More articles: [[<-- prev]](/core-java-modules/core-java-security)
diff --git a/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java
new file mode 100644
index 0000000000..6e5df8e619
--- /dev/null
+++ b/core-java-modules/core-java-security-2/src/test/java/com/baeldung/des/TripleDESUnitTest.java
@@ -0,0 +1,90 @@
+package com.baeldung.des;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class TripleDESUnitTest {
+
+ @Test
+ public void given3DesKey_whenEncryptAndDecryptString_thenCompareResults() throws Exception {
+ byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes();
+ byte[] iv = "a76nb5h9".getBytes();
+
+ String secretMessage = "Baeldung secret message";
+
+ SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede");
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] secretMessagesBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
+ byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessagesBytes);
+
+ Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
+ String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
+
+ Assertions.assertEquals(secretMessage, decryptedMessage);
+ }
+
+ @Test
+ public void given3DesKey_whenEncryptAndDecryptFile_thenCompareResults() throws Exception {
+ byte[] secretKey = "9mng65v8jf4lxn93nabf981m".getBytes();
+ byte[] iv = "a76nb5h9".getBytes();
+
+ SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "DESede");
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+
+ String originalContent = "some secret message";
+ Path tempFile = Files.createTempFile("temp", "txt");
+ writeString(tempFile, originalContent);
+
+ byte[] fileBytes = Files.readAllBytes(tempFile);
+ Cipher encryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes);
+ try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {
+ stream.write(encryptedFileBytes);
+ }
+
+ encryptedFileBytes = Files.readAllBytes(tempFile);
+ Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
+ byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes);
+ try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {
+ stream.write(decryptedFileBytes);
+ }
+
+ String fileContent = readString(tempFile);
+
+ Assertions.assertEquals(originalContent, fileContent);
+ }
+
+ private void writeString(Path path, String content) throws Exception {
+ try (BufferedWriter writer = Files.newBufferedWriter(path)) {
+ writer.write(content);
+ }
+ }
+
+ private String readString(Path path) throws Exception {
+ StringBuilder resultStringBuilder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new FileReader(path.toFile()))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ resultStringBuilder.append(line);
+ }
+ }
+ return resultStringBuilder.toString();
+ }
+}
diff --git a/core-java-modules/core-java-streams-4/.gitignore b/core-java-modules/core-java-streams-4/.gitignore
new file mode 100644
index 0000000000..3de4cc647e
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/.gitignore
@@ -0,0 +1,26 @@
+*.class
+
+0.*
+
+#folders#
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+.resourceCache
+
+# Packaged files #
+*.jar
+*.war
+*.ear
+
+# Files generated by integration tests
+*.txt
+backup-pom.xml
+/bin/
+/temp
+
+#IntelliJ specific
+.idea/
+*.iml
\ No newline at end of file
diff --git a/core-java-modules/core-java-streams-4/README.md b/core-java-modules/core-java-streams-4/README.md
new file mode 100644
index 0000000000..6eeee943aa
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Count Occurrences Using Java groupingBy Collector](https://www.baeldung.com/java-groupingby-count)
diff --git a/core-java-modules/core-java-streams-4/pom.xml b/core-java-modules/core-java-streams-4/pom.xml
new file mode 100644
index 0000000000..0a411bcc43
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+ core-java-streams-4
+ 0.1.0-SNAPSHOT
+ core-java-streams
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+ ../
+
+
+
+
+ log4j
+ log4j
+ ${log4j.version}
+
+
+ org.junit
+ junit-bom
+ ${junit-jupiter.version}
+ pom
+ import
+
+
+
+
+ core-java-streams-4
+
+
+ src/main
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ -parameters
+
+
+
+
+
+
+
+ 3.1
+ 1.8
+ 1.8
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java
new file mode 100644
index 0000000000..c50d73a638
--- /dev/null
+++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streamcollectors/StreamGroupingByCollectorUnitTest.java
@@ -0,0 +1,84 @@
+package com.baeldung.streamcollectors;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StreamGroupingByCollectorUnitTest {
+ @Test
+ public void givenListOfStrings_whenGroupingEqualStrings_thenUseCollectorsGroupingByToGroupEqualStringsAndCountOfOccurrences() {
+
+ List list = new ArrayList<>(Arrays.asList("Foo", "Bar", "Bar", "Foo", "Bar"));
+
+ Map result = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get("Foo"));
+ Assert.assertEquals(new Long(3), result.get("Bar"));
+
+ }
+
+ @Test
+ public void givenListOfStrings_whenGroupingEqualLengthStrings_thenUseCollectorsGroupingByConcurrentToGroupEqualLengthStringsAndCountOfOccurrences() {
+
+ List list = new ArrayList<>(Arrays.asList("Adam", "Bill", "Jack", "Joe", "Ian"));
+
+ Map result = list.stream().collect(Collectors.groupingByConcurrent(String::length, Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get(3));
+ Assert.assertEquals(new Long(3), result.get(4));
+
+ }
+
+ @Test
+ public void givenListOfEmployees_whenGroupingDepartmentId_thenUseCollectorsGroupingByDepartmentIdAndCountNumberOfEmployeesWithinEveryDepartment() {
+
+ List list = new ArrayList<>(Arrays.asList(new Employee(1, "Joe", 1), new Employee(2, "Josh", 1), new Employee(3, "Jamie", 2), new Employee(4, "Jim", 2), new Employee(5, "Jack", 2)));
+
+ Map result = list.stream().collect(Collectors.groupingBy(Employee::getDepartmentId, Collectors.counting()));
+ Assert.assertEquals(new Long(2), result.get(1));
+ Assert.assertEquals(new Long(3), result.get(2));
+
+ }
+
+ static class Employee {
+
+ Integer employeeId;
+ String employeeName;
+ Integer departmentId;
+
+ Employee(Integer employeeId, String employeeName, Integer departmentId) {
+ this.employeeId = employeeId;
+ this.employeeName = employeeName;
+ this.departmentId = departmentId;
+ }
+
+ public Integer getEmployeeId() {
+ return employeeId;
+ }
+
+ public void setEmployeeId(Integer employeeId) {
+ this.employeeId = employeeId;
+ }
+
+ public String getEmployeeName() {
+ return employeeName;
+ }
+
+ public void setEmployeeName(String employeeName) {
+ this.employeeName = employeeName;
+ }
+
+ public Integer getDepartmentId() {
+ return departmentId;
+ }
+
+ public void setDepartmentId(Integer departmentId) {
+ this.departmentId = departmentId;
+ }
+ }
+
+}
diff --git a/core-java-modules/core-java-string-conversions-2/README.md b/core-java-modules/core-java-string-conversions-2/README.md
index 3bd3ba927e..46eb783a27 100644
--- a/core-java-modules/core-java-string-conversions-2/README.md
+++ b/core-java-modules/core-java-string-conversions-2/README.md
@@ -7,4 +7,6 @@ This module contains articles about string conversions from/to another type.
- [Convert String to Byte Array and Reverse in Java](https://www.baeldung.com/java-string-to-byte-array)
- [Convert Character Array to String in Java](https://www.baeldung.com/java-char-array-to-string)
- [Converting String to BigDecimal in Java](https://www.baeldung.com/java-string-to-bigdecimal)
+- [Converting String to BigInteger in Java](https://www.baeldung.com/java-string-to-biginteger)
+- [Convert a String to Camel Case](https://www.baeldung.com/java-string-to-camel-case)
- More articles: [[<-- prev]](/core-java-string-conversions)
diff --git a/core-java-modules/core-java-string-conversions-2/pom.xml b/core-java-modules/core-java-string-conversions-2/pom.xml
index 8a222a6b5d..584c808f56 100644
--- a/core-java-modules/core-java-string-conversions-2/pom.xml
+++ b/core-java-modules/core-java-string-conversions-2/pom.xml
@@ -33,7 +33,39 @@
${hamcrest.version}
test
+
+
+ com.ibm.icu
+ icu4j
+ ${icu.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+ org.apache.commons
+ commons-text
+ ${commons-text.version}
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+ 64.2
+ 3.12.2
+ 1.9
+ 30.1.1-jre
+
core-java-string-conversions-2
diff --git a/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java
new file mode 100644
index 0000000000..7a4369ac4a
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/main/java/com/baeldung/stringtocamelcase/StringToCamelCase.java
@@ -0,0 +1,109 @@
+package com.baeldung.stringtocamelcase;
+
+import com.google.common.base.CaseFormat;
+import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.text.BreakIterator;
+import org.apache.commons.text.CaseUtils;
+import org.apache.commons.text.WordUtils;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class StringToCamelCase {
+
+ public static String toCamelCaseByIteration(String text, char delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ boolean shouldConvertNextCharToLower = true;
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char currentChar = text.charAt(i);
+ if (currentChar == delimiter) {
+ shouldConvertNextCharToLower = false;
+ } else if (shouldConvertNextCharToLower) {
+ builder.append(Character.toLowerCase(currentChar));
+ } else {
+ builder.append(Character.toUpperCase(currentChar));
+ shouldConvertNextCharToLower = true;
+ }
+ }
+ return builder.toString();
+ }
+
+ public static String toCamelCaseBySplitting(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ String[] words = text.split(delimiter);
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0, wordsLength = words.length; i < wordsLength; i++) {
+ String word = words[i];
+ if (i == 0) {
+ //Make the first word all lowercase
+ word = word.isEmpty() ? word : word.toLowerCase();
+ } else {
+ //Convert the first character to Uppercase and others to lowercase
+ // e.g sTRING =====> String
+ word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
+ }
+ builder.append(word);
+ }
+ return builder.toString();
+ }
+
+ public static String toCamelCaseBySplittingUsingStreams(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ String[] words = text.split(delimiter);
+ //Convert the first word to lowercase and then every
+ //other word to Title Case.
+ String firstWord = words[0].toLowerCase();
+ String otherWords = Arrays.stream(words, 1, words.length)
+ .filter(word -> !word.isEmpty())
+ .map(word -> Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase())
+ .collect(Collectors.joining(""));
+
+ return firstWord + otherWords;
+ }
+
+ public static String toCamelCaseByRegex(String text) {
+ StringBuilder builder = new StringBuilder();
+ String[] words = text.split("[\\W_]+");
+ for (int i = 0; i < words.length; i++) {
+ String word = words[i];
+ if (i == 0) {
+ word = word.isEmpty() ? word : word.toLowerCase();
+ } else {
+ word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
+ }
+ builder.append(word);
+ }
+ return builder.toString();
+ }
+
+ //Third-Party Libraries
+ public static String toCamelCaseUsingICU4J(String text, String delimiter) {
+ if (text == null || text.isEmpty()) {
+ return text;
+ }
+ text = UCharacter.toTitleCase(text, BreakIterator.getTitleInstance()).replaceAll(delimiter, "");
+ StringBuilder builder = new StringBuilder(text);
+ builder.setCharAt(0, Character.toLowerCase(text.charAt(0)));
+ return builder.toString();
+ }
+
+ public static String toCamelCaseUsingGuava(String text, String delimiter) {
+ String toUpperUnderscore = text.toUpperCase().replaceAll(delimiter, "_");
+ return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, toUpperUnderscore);
+ }
+
+ public static String toCamelCaseUsingApacheCommons(String text, char delimiter) {
+ text = WordUtils.capitalizeFully(text, delimiter).replaceAll(String.valueOf(delimiter), "");
+ StringBuilder builder = new StringBuilder(text);
+ builder.setCharAt(0, Character.toLowerCase(text.charAt(0)));
+ return builder.toString();
+ }
+
+}
diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java
new file mode 100644
index 0000000000..82ffe96d84
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtobybiginteger/StringToBigIntegerUnitTest.java
@@ -0,0 +1,44 @@
+package com.baeldung.stringtobybiginteger;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+public class StringToBigIntegerUnitTest {
+
+ @Test
+ public void whenGetStringWithOutRadix_thenOK() {
+ String inputString = "878";
+ BigInteger result = new BigInteger(inputString);
+ assertEquals("878", result.toString());
+ }
+
+ @Test
+ public void whenGetStringWithRadix_thenOK() {
+ String inputString = "290f98";
+ BigInteger result = new BigInteger(inputString, 16);
+ assertEquals("2690968", result.toString());
+ }
+
+ @Test(expected = NumberFormatException.class)
+ public void whenGetStringWithOutRadix_thenThrowError() {
+ String inputString = "290f98";
+ new BigInteger(inputString);
+ }
+
+ @Test(expected = NumberFormatException.class)
+ public void whenGetStringWithRadix_thenThrowError() {
+ String inputString = "290f98";
+ new BigInteger(inputString, 7);
+ }
+
+ @Test
+ public void whenGetStringUsingByte_thenOk() {
+ String inputString = "290f98";
+ byte[] inputStringBytes = inputString.getBytes();
+ BigInteger result = new BigInteger(inputStringBytes);
+ assertEquals("290f98", new String(result.toByteArray()));
+ }
+}
diff --git a/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java
new file mode 100644
index 0000000000..3adc4434a9
--- /dev/null
+++ b/core-java-modules/core-java-string-conversions-2/src/test/java/com/baeldung/stringtocamelcase/StringToCamelCaseUnitTest.java
@@ -0,0 +1,111 @@
+package com.baeldung.stringtocamelcase;
+
+
+import com.google.common.base.CaseFormat;
+import org.apache.commons.text.CaseUtils;
+import org.junit.Test;
+
+import static com.baeldung.stringtocamelcase.StringToCamelCase.*;
+import static org.assertj.core.api.Assertions.*;
+
+
+public class StringToCamelCaseUnitTest {
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseByIteration_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByIteration("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseByIteration_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByIteration("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplitting_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplitting("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseBySplitting_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplitting("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplittingUsingStreams("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseBySplittingUsingStreams_ThenReturnCamelCase() {
+ assertThat(toCamelCaseBySplittingUsingStreams("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() {
+ assertThat(CaseUtils.toCamelCase("THIS STRING SHOULD BE IN CAMEL CASE", false, ' '))
+ .isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommonsText_ThenReturnCamelCase() {
+ assertThat(CaseUtils.toCamelCase("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", false, '_'))
+ .isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingICU4J("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingICU4J_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingICU4J("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingGuava("THIS STRING SHOULD BE IN CAMEL CASE", " ")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingGuava_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingGuava("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", "_")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingApacheCommons("THIS STRING SHOULD BE IN CAMEL CASE", ' ')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithUnderscores_WhenToCamelCaseUsingApacheCommons_ThenReturnCamelCase() {
+ assertThat(toCamelCaseUsingApacheCommons("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE", '_')).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteSpaces_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("THIS STRING SHOULD BE IN CAMEL CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenStringWithWhiteUnderscores_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenRandomString_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case")).isEqualTo("pleaseTurnThis56738ToCamelCase");
+ }
+
+ @Test
+ public void givenRandomStringWithDifferentDelimiters_WhenToCamelCaseByRegex_ThenReturnCamelCase() {
+ assertThat(toCamelCaseByRegex("Please Turn this56738 to camel Case This should-be_in;camel-case")).isEqualTo("pleaseTurnThis56738ToCamelCaseThisShouldBeInCamelCase");
+ }
+
+ @Test
+ public void givenUppercaseWordWithUnderscores_WhenCaseFormatToLowerCamel_ThenReturnCamelCase() {
+ assertThat(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "THIS_STRING_SHOULD_BE_IN_CAMEL_CASE")).isEqualTo("thisStringShouldBeInCamelCase");
+ }
+
+}
diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md
index 14857d5d87..f1cf2d8c09 100644
--- a/core-java-modules/core-java/README.md
+++ b/core-java-modules/core-java/README.md
@@ -12,3 +12,4 @@
- [A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle)
- [Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties)
- [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities)
+- [Generating Alphanumeric UUID String in Java](https://www.baeldung.com/java-generate-alphanumeric-uuid)
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
index 49d43c38a2..54de1f95c6 100644
--- a/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
+++ b/core-java-modules/core-java/src/main/java/com/baeldung/uuid/UUIDGenerator.java
@@ -176,8 +176,8 @@ public class UUIDGenerator {
byte[] hash = md.digest(bytes);
- long msb = peekLong(hash, 0, ByteOrder.BIG_ENDIAN);
- long lsb = peekLong(hash, 8, ByteOrder.BIG_ENDIAN);
+ long msb = getLeastAndMostSignificantBitsVersion5(hash, 0);
+ long lsb = getLeastAndMostSignificantBitsVersion5(hash, 8);
// Set the version field
msb &= ~(0xfL << 12);
msb |= ((long) 5) << 12;
@@ -191,18 +191,11 @@ public class UUIDGenerator {
}
}
- private static long peekLong(final byte[] src, final int offset, final ByteOrder order) {
+ private static long getLeastAndMostSignificantBitsVersion5(final byte[] src, final int offset) {
long ans = 0;
- if (order == ByteOrder.BIG_ENDIAN) {
- for (int i = offset; i < offset + 8; i += 1) {
- ans <<= 8;
- ans |= src[i] & 0xffL;
- }
- } else {
- for (int i = offset + 7; i >= offset; i -= 1) {
- ans <<= 8;
- ans |= src[i] & 0xffL;
- }
+ for (int i = offset + 7; i >= offset; i -= 1) {
+ ans <<= 8;
+ ans |= src[i] & 0xffL;
}
return ans;
}
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
index b2ca979c83..cf0d7656ff 100644
--- a/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
+++ b/core-java-modules/core-java/src/test/java/com/baeldung/uuid/UUIDGeneratorUnitTest.java
@@ -63,11 +63,11 @@ class UUIDGeneratorUnitTest {
}
@Test
- public void version_5_UUID_is_correctly_generated_for_domain_baeldung_com_without_namespace() throws UnsupportedEncodingException {
+ public void version_5_UUID_is_correctly_generated_for_domain_baeldung_name() {
UUID uuid = UUIDGenerator.generateType5UUID("baeldung.com");
- assertEquals("a3c27ab0-2b46-55ef-b50e-0e5c57bfea94", uuid.toString());
+ assertEquals("efd5462b-b07a-52a3-94ea-bf575c0e0e75", uuid.toString());
assertEquals(5, uuid.version());
assertEquals(2, uuid.variant());
}
diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml
index 8661f1ba01..f727695036 100644
--- a/core-java-modules/pom.xml
+++ b/core-java-modules/pom.xml
@@ -48,7 +48,6 @@
core-java-concurrency-collections
core-java-concurrency-collections-2
core-java-console
-
core-java-8-datetime-2
core-java-date-operations-2
core-java-8-datetime
@@ -63,7 +62,6 @@
core-java-io-4
core-java-io-apis
core-java-io-conversions
- core-java-io-conversions-2
core-java-jar
core-java-jndi
core-java-jvm
@@ -103,6 +101,7 @@
core-java-streams
core-java-streams-2
core-java-streams-3
+ core-java-streams-4
core-java-string-algorithms
core-java-string-algorithms-2
core-java-string-algorithms-3
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Cache.java b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java
new file mode 100644
index 0000000000..f070f66191
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/Cache.java
@@ -0,0 +1,15 @@
+package com.baeldung.lrucache;
+
+import java.util.Optional;
+
+public interface Cache {
+ boolean put(K key, V value);
+
+ Optional get(K key);
+
+ int size();
+
+ boolean isEmpty();
+
+ void clear();
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java
new file mode 100644
index 0000000000..7d260c4531
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/CacheElement.java
@@ -0,0 +1,31 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+
+public class CacheElement {
+ private K key;
+ private V value;
+
+ public CacheElement(K key, V value) {
+ this.value = value;
+ this.key = key;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public void setKey(K key) {
+ this.key = key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public void setValue(V value) {
+ this.value = value;
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java
new file mode 100644
index 0000000000..5850d6c13d
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/DoublyLinkedList.java
@@ -0,0 +1,163 @@
+package com.baeldung.lrucache;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class DoublyLinkedList {
+
+ private DummyNode dummyNode;
+ private LinkedListNode head;
+ private LinkedListNode tail;
+ private AtomicInteger size;
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public DoublyLinkedList() {
+ this.dummyNode = new DummyNode(this);
+ clear();
+ }
+
+ public void clear() {
+ this.lock.writeLock().lock();
+ try {
+ head = dummyNode;
+ tail = dummyNode;
+ size = new AtomicInteger(0);
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public int size() {
+ this.lock.readLock().lock();
+ try {
+ return size.get();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public boolean isEmpty() {
+ this.lock.readLock().lock();
+ try {
+ return head.isEmpty();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public boolean contains(T value) {
+ this.lock.readLock().lock();
+ try {
+ return search(value).hasElement();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public LinkedListNode search(T value) {
+ this.lock.readLock().lock();
+ try {
+ return head.search(value);
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ public LinkedListNode add(T value) {
+ this.lock.writeLock().lock();
+ try {
+ head = new Node(value, head, this);
+ if (tail.isEmpty()) {
+ tail = head;
+ }
+ size.incrementAndGet();
+ return head;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public boolean addAll(Collection values) {
+ this.lock.writeLock().lock();
+ try {
+ for (T value : values) {
+ if (add(value).isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode remove(T value) {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode linkedListNode = head.search(value);
+ if (!linkedListNode.isEmpty()) {
+ if (linkedListNode == tail) {
+ tail = tail.getPrev();
+ }
+ if (linkedListNode == head) {
+ head = head.getNext();
+ }
+ linkedListNode.detach();
+ size.decrementAndGet();
+ }
+ return linkedListNode;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode removeTail() {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode oldTail = tail;
+ if (oldTail == head) {
+ tail = head = dummyNode;
+ } else {
+ tail = tail.getPrev();
+ oldTail.detach();
+ }
+ if (!oldTail.isEmpty()) {
+ size.decrementAndGet();
+ }
+ return oldTail;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ public LinkedListNode moveToFront(LinkedListNode node) {
+ return node.isEmpty() ? dummyNode : updateAndMoveToFront(node, node.getElement());
+ }
+
+ public LinkedListNode updateAndMoveToFront(LinkedListNode node, T newValue) {
+ this.lock.writeLock().lock();
+ try {
+ if (node.isEmpty() || (this != (node.getListReference()))) {
+ return dummyNode;
+ }
+ detach(node);
+ add(newValue);
+ return head;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ private void detach(LinkedListNode node) {
+ if (node != tail) {
+ node.detach();
+ if (node == head) {
+ head = head.getNext();
+ }
+ size.decrementAndGet();
+ } else {
+ removeTail();
+ }
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java
new file mode 100644
index 0000000000..4965b70bcf
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/DummyNode.java
@@ -0,0 +1,62 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+public class DummyNode implements LinkedListNode {
+ private DoublyLinkedList list;
+
+ public DummyNode(DoublyLinkedList list) {
+ this.list = list;
+ }
+
+ @Override
+ public boolean hasElement() {
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public T getElement() throws NullPointerException {
+ throw new NullPointerException();
+ }
+
+ @Override
+ public void detach() {
+ return;
+ }
+
+ @Override
+ public DoublyLinkedList getListReference() {
+ return list;
+ }
+
+ @Override
+ public LinkedListNode setPrev(LinkedListNode next) {
+ return next;
+ }
+
+ @Override
+ public LinkedListNode setNext(LinkedListNode prev) {
+ return prev;
+ }
+
+ @Override
+ public LinkedListNode getPrev() {
+ return this;
+ }
+
+ @Override
+ public LinkedListNode getNext() {
+ return this;
+ }
+
+ @Override
+ public LinkedListNode search(T value) {
+ return this;
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java
new file mode 100644
index 0000000000..3032a90d8e
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/LRUCache.java
@@ -0,0 +1,100 @@
+package com.baeldung.lrucache;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class LRUCache implements Cache {
+ private int size;
+ private Map>> linkedListNodeMap;
+ private DoublyLinkedList> doublyLinkedList;
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public LRUCache(int size) {
+ this.size = size;
+ this.linkedListNodeMap = new ConcurrentHashMap<>(size);
+ this.doublyLinkedList = new DoublyLinkedList<>();
+ }
+
+ @Override
+ public boolean put(K key, V value) {
+ this.lock.writeLock().lock();
+ try {
+ CacheElement item = new CacheElement(key, value);
+ LinkedListNode> newNode;
+ if (this.linkedListNodeMap.containsKey(key)) {
+ LinkedListNode> node = this.linkedListNodeMap.get(key);
+ newNode = doublyLinkedList.updateAndMoveToFront(node, item);
+ } else {
+ if (this.size() >= this.size) {
+ this.evictElement();
+ }
+ newNode = this.doublyLinkedList.add(item);
+ }
+ if (newNode.isEmpty()) {
+ return false;
+ }
+ this.linkedListNodeMap.put(key, newNode);
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public Optional get(K key) {
+ this.lock.readLock().lock();
+ try {
+ LinkedListNode> linkedListNode = this.linkedListNodeMap.get(key);
+ if (linkedListNode != null && !linkedListNode.isEmpty()) {
+ linkedListNodeMap.put(key, this.doublyLinkedList.moveToFront(linkedListNode));
+ return Optional.of(linkedListNode.getElement().getValue());
+ }
+ return Optional.empty();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public int size() {
+ this.lock.readLock().lock();
+ try {
+ return doublyLinkedList.size();
+ } finally {
+ this.lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public void clear() {
+ this.lock.writeLock().lock();
+ try {
+ linkedListNodeMap.clear();
+ doublyLinkedList.clear();
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+
+
+ private boolean evictElement() {
+ this.lock.writeLock().lock();
+ try {
+ LinkedListNode> linkedListNode = doublyLinkedList.removeTail();
+ if (linkedListNode.isEmpty()) {
+ return false;
+ }
+ linkedListNodeMap.remove(linkedListNode.getElement().getKey());
+ return true;
+ } finally {
+ this.lock.writeLock().unlock();
+ }
+ }
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java
new file mode 100644
index 0000000000..219ee13496
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/LinkedListNode.java
@@ -0,0 +1,23 @@
+package com.baeldung.lrucache;
+
+public interface LinkedListNode {
+ boolean hasElement();
+
+ boolean isEmpty();
+
+ V getElement() throws NullPointerException;
+
+ void detach();
+
+ DoublyLinkedList getListReference();
+
+ LinkedListNode setPrev(LinkedListNode prev);
+
+ LinkedListNode setNext(LinkedListNode next);
+
+ LinkedListNode getPrev();
+
+ LinkedListNode getNext();
+
+ LinkedListNode search(V value);
+}
diff --git a/data-structures/src/main/java/com/baeldung/lrucache/Node.java b/data-structures/src/main/java/com/baeldung/lrucache/Node.java
new file mode 100644
index 0000000000..340bb1dd82
--- /dev/null
+++ b/data-structures/src/main/java/com/baeldung/lrucache/Node.java
@@ -0,0 +1,71 @@
+package com.baeldung.lrucache;
+
+/**
+ * Created by arash on 09.07.21.
+ */
+public class Node implements LinkedListNode {
+ private T value;
+ private DoublyLinkedList list;
+ private LinkedListNode next;
+ private LinkedListNode prev;
+
+ public Node(T value, LinkedListNode next, DoublyLinkedList list) {
+ this.value = value;
+ this.next = next;
+ this.setPrev(next.getPrev());
+ this.prev.setNext(this);
+ this.next.setPrev(this);
+ this.list = list;
+ }
+
+ @Override
+ public boolean hasElement() {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public T getElement() {
+ return value;
+ }
+
+ public void detach() {
+ this.prev.setNext(this.getNext());
+ this.next.setPrev(this.getPrev());
+ }
+
+ @Override
+ public DoublyLinkedList getListReference() {
+ return this.list;
+ }
+
+ @Override
+ public LinkedListNode setPrev(LinkedListNode prev) {
+ this.prev = prev;
+ return this;
+ }
+
+ @Override
+ public LinkedListNode setNext(LinkedListNode next) {
+ this.next = next;
+ return this;
+ }
+
+ @Override
+ public LinkedListNode getPrev() {
+ return this.prev;
+ }
+
+ @Override
+ public LinkedListNode getNext() {
+ return this.next;
+ }
+
+ @Override
+ public LinkedListNode search(T value) {
+ return this.getElement() == value ? this : this.getNext().search(value);
+ }
+}
diff --git a/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java
new file mode 100644
index 0000000000..e64085ae4c
--- /dev/null
+++ b/data-structures/src/test/java/com/baeldung/lrucache/LRUCacheUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.lrucache;
+
+import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.IntStream;
+import static org.junit.Assert.*;
+
+public class LRUCacheUnitTest {
+
+ @Test
+ public void addSomeDataToCache_WhenGetData_ThenIsEqualWithCacheElement() {
+ LRUCache lruCache = new LRUCache<>(3);
+ lruCache.put("1", "test1");
+ lruCache.put("2", "test2");
+ lruCache.put("3", "test3");
+ assertEquals("test1", lruCache.get("1").get());
+ assertEquals("test2", lruCache.get("2").get());
+ assertEquals("test3", lruCache.get("3").get());
+ }
+
+ @Test
+ public void addDataToCacheToTheNumberOfSize_WhenAddOneMoreData_ThenLeastRecentlyDataWillEvict() {
+ LRUCache lruCache = new LRUCache<>(3);
+ lruCache.put("1", "test1");
+ lruCache.put("2", "test2");
+ lruCache.put("3", "test3");
+ lruCache.put("4", "test4");
+ assertFalse(lruCache.get("1").isPresent());
+ }
+
+ @Test
+ public void runMultiThreadTask_WhenPutDataInConcurrentToCache_ThenNoDataLost() throws Exception {
+ final int size = 50;
+ final ExecutorService executorService = Executors.newFixedThreadPool(5);
+ Cache cache = new LRUCache<>(size);
+ CountDownLatch countDownLatch = new CountDownLatch(size);
+ try {
+ IntStream.range(0, size).mapToObj(key -> () -> {
+ cache.put(key, "value" + key);
+ countDownLatch.countDown();
+ }).forEach(executorService::submit);
+ countDownLatch.await();
+ } finally {
+ executorService.shutdown();
+ }
+ assertEquals(cache.size(), size);
+ IntStream.range(0, size).forEach(i -> assertEquals("value" + i, cache.get(i).get()));
+ }
+}
diff --git a/guava-modules/guava-concurrency/README.md b/guava-modules/guava-concurrency/README.md
new file mode 100644
index 0000000000..12fca9a1a5
--- /dev/null
+++ b/guava-modules/guava-concurrency/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Guava’s Futures and ListenableFuture](https://www.baeldung.com/guava-futures-listenablefuture)
diff --git a/guava-modules/guava-concurrency/pom.xml b/guava-modules/guava-concurrency/pom.xml
new file mode 100644
index 0000000000..ef7f756596
--- /dev/null
+++ b/guava-modules/guava-concurrency/pom.xml
@@ -0,0 +1,14 @@
+
+
+
+ guava-modules
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ guava-concurrency
+
+
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java
new file mode 100644
index 0000000000..b6620bd1e2
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/ListenableFutureService.java
@@ -0,0 +1,105 @@
+package com.baeldung.guava.future;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureService {
+
+ private final ListeningExecutorService lExecService;
+
+ public ListenableFutureService() {
+ this.lExecService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ }
+
+ public ListenableFutureService(ListeningExecutorService lExecService) {
+ this.lExecService = lExecService;
+ }
+
+ public ListenableFuture fetchConfig(String configKey) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public FutureTask fetchConfigTask(String configKey) {
+ return new FutureTask<>(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public ListenableFutureTask fetchConfigListenableTask(String configKey) {
+ return ListenableFutureTask.create(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return String.format("%s.%d", configKey, new Random().nextInt(Integer.MAX_VALUE));
+ });
+ }
+
+ public ListenableFuture succeedingTask() {
+ return Futures.immediateFuture(new Random().nextInt(Integer.MAX_VALUE));
+ }
+
+ public ListenableFuture failingTask() {
+ return Futures.immediateFailedFuture(new ListenableFutureException());
+ }
+
+ public ListenableFuture getCartId() {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return new Random().nextInt(Integer.MAX_VALUE);
+ });
+ }
+
+ public ListenableFuture getCustomerName() {
+ String[] names = new String[] { "Mark", "Jane", "June" };
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return names[new Random().nextInt(names.length)];
+ });
+ }
+
+ public ListenableFuture> getCartItems() {
+ String[] items = new String[] { "Apple", "Orange", "Mango", "Pineapple" };
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+
+ int noOfItems = new Random().nextInt(items.length);
+ if (noOfItems == 0) ++noOfItems;
+
+ return Arrays.stream(items, 0, noOfItems).collect(Collectors.toList());
+ });
+ }
+
+ public ListenableFuture generateUsername(String firstName) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ return firstName.replaceAll("[^a-zA-Z]+","")
+ .concat("@service.com");
+ });
+ }
+
+ public ListenableFuture generatePassword(String username) {
+ return lExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500);
+ if (username.contains("@")) {
+ String[] parts = username.split("@");
+ return parts[0] + "123@" + parts[1];
+ } else {
+ return username + "123";
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java
new file mode 100644
index 0000000000..921c02b54a
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/main/java/com/baeldung/guava/future/exception/ListenableFutureException.java
@@ -0,0 +1,4 @@
+package com.baeldung.guava.future.exception;
+
+public class ListenableFutureException extends Exception {
+}
diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java
new file mode 100644
index 0000000000..27a1cc6592
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureComplexUnitTest.java
@@ -0,0 +1,277 @@
+package com.baeldung.guava.future;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.function.UnaryOperator;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncCallable;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureComplexUnitTest {
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllAsList_thenAllSuccess() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.fetchConfig("config.1");
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3);
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ assertNotNull(configResults);
+ assertEquals(3, configResults.size());
+ for (int i = 0; i < 3; i++) {
+ assertTrue(configResults.get(i)
+ .contains("config." + i));
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenOneFailingTask_whenAllAsList_thenFailure() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.failingTask();
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.allAsList(task1, task2, task3);
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ fail("Expected a failed future");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertTrue(t instanceof ListenableFutureException);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenOneFailingTask_whenSuccessfulAsList_thenSomeSuccess() {
+ final ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ final ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture task1 = service.fetchConfig("config.0");
+ ListenableFuture task2 = service.failingTask();
+ ListenableFuture task3 = service.fetchConfig("config.2");
+
+ ListenableFuture> configsTask = Futures.successfulAsList(task1, task2, task3);
+
+ Futures.addCallback(configsTask, new FutureCallback>() {
+ @Override
+ public void onSuccess(@Nullable List configResults) {
+ assertNotNull(configResults);
+ assertTrue(configResults.get(0).contains("config.0"));
+ assertNull(configResults.get(1));
+ assertTrue(configResults.get(2).contains("config.2"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllSucceed_thenSuccess() {
+ ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture cartIdTask = service.getCartId();
+ ListenableFuture customerNameTask = service.getCustomerName();
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ ListenableFuture cartInfoTask = Futures.whenAllSucceed(cartIdTask, customerNameTask, cartItemsTask)
+ .call(() -> {
+ int cartId = Futures.getDone(cartIdTask);
+ String customerName = Futures.getDone(customerNameTask);
+ List cartItems = Futures.getDone(cartItemsTask);
+ return new CartInfo(cartId, customerName, cartItems);
+ }, listeningExecService);
+
+ Futures.addCallback(cartInfoTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable CartInfo result) {
+ assertNotNull(result);
+ assertTrue(result.cartId >= 0);
+ assertFalse(result.customerName.isEmpty());
+ assertFalse(result.cartItems.isEmpty());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void givenAllSucceedingTasks_whenAllComplete_thenSomeSuccess() {
+ ListeningExecutorService listeningExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listeningExecService);
+
+ ListenableFuture cartIdTask = service.getCartId();
+ ListenableFuture customerNameTask = service.failingTask();
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ ListenableFuture cartInfoTask = Futures.whenAllComplete(cartIdTask, customerNameTask, cartItemsTask)
+ .call(() -> {
+ Integer cartId = getOrNull(cartIdTask);
+ String customerName = getOrNull(customerNameTask);
+ List cartItems = getOrNull(cartItemsTask);
+ return new CartInfo(cartId, customerName, cartItems);
+ }, listeningExecService);
+
+ Futures.addCallback(cartInfoTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable CartInfo result) {
+ assertNotNull(result);
+ assertTrue(result.cartId >= 0);
+ assertNull(result.customerName);
+ assertFalse(result.cartItems.isEmpty());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listeningExecService);
+ }
+
+ @Test
+ public void whenTransform_thenTransformSuccess() {
+ ListeningExecutorService listenExecService = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(listenExecService);
+
+ ListenableFuture> cartItemsTask = service.getCartItems();
+
+ Function, Integer> itemCountFunc = cartItems -> {
+ assertNotNull(cartItems);
+ return cartItems.size();
+ };
+
+ ListenableFuture itemCountTask = Futures.transform(cartItemsTask, itemCountFunc, listenExecService);
+
+ Futures.addCallback(itemCountTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Integer cartItemCount) {
+ assertNotNull(cartItemCount);
+ assertTrue(cartItemCount > 0);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, listenExecService);
+ }
+
+ @Test
+ public void whenSubmitAsync_thenSuccess() {
+ ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(executor);
+
+ AsyncCallable asyncConfigTask = () -> {
+ ListenableFuture configTask = service.fetchConfig("config.a");
+ TimeUnit.MILLISECONDS.sleep(500); //some long running task
+ return configTask;
+ };
+
+ ListenableFuture configTask = Futures.submitAsync(asyncConfigTask, executor);
+
+ Futures.addCallback(configTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable String result) {
+ assertNotNull(result);
+ assertTrue(result.contains("config.a"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, executor);
+ }
+
+ @Test
+ public void whenAsyncTransform_thenSuccess() {
+ ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
+ ListenableFutureService service = new ListenableFutureService(executor);
+
+ ListenableFuture usernameTask = service.generateUsername("john");
+ AsyncFunction passwordFunc = username -> {
+ ListenableFuture generatePasswordTask = service.generatePassword(username);
+ TimeUnit.MILLISECONDS.sleep(500); // some long running task
+ return generatePasswordTask;
+ };
+
+ ListenableFuture passwordTask = Futures.transformAsync(usernameTask, passwordFunc, executor);
+
+ Futures.addCallback(passwordTask, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable String password) {
+ assertNotNull(password);
+ assertTrue(password.contains("john"));
+ assertTrue(password.contains("@"));
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Unexpected failure detected", t);
+ }
+ }, executor);
+ }
+
+ private static T getOrNull(ListenableFuture future) {
+ try {
+ return Futures.getDone(future);
+ } catch (ExecutionException e) {
+ return null;
+ }
+ }
+
+ static class CartInfo {
+ Integer cartId;
+ String customerName;
+ List cartItems;
+
+ public CartInfo(Integer cartId, String customerName, List cartItems) {
+ this.cartId = cartId;
+ this.customerName = customerName;
+ this.cartItems = cartItems;
+ }
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java
new file mode 100644
index 0000000000..7dce11a33f
--- /dev/null
+++ b/guava-modules/guava-concurrency/src/test/java/com/baeldung/guava/future/ListenableFutureSimpleUnitTest.java
@@ -0,0 +1,122 @@
+package com.baeldung.guava.future;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import com.baeldung.guava.future.exception.ListenableFutureException;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class ListenableFutureSimpleUnitTest {
+
+ @Test
+ public void whenSubmitToListeningExecutor_thenSuccess() throws ExecutionException, InterruptedException {
+ ExecutorService execService = Executors.newSingleThreadExecutor();
+ ListeningExecutorService listeningExecService = MoreExecutors.listeningDecorator(execService);
+
+ ListenableFuture asyncTask = listeningExecService.submit(() -> {
+ TimeUnit.MILLISECONDS.sleep(500); // long running task
+ return 5;
+ });
+
+ assertEquals(5, asyncTask.get());
+ }
+
+ @Test
+ public void
+ givenJavaExecutor_whenSubmitListeningTask_thenSuccess() throws ExecutionException, InterruptedException {
+ Executor executor = Executors.newSingleThreadExecutor();
+ ListenableFutureService service = new ListenableFutureService();
+
+ FutureTask configFuture = service.fetchConfigTask("future.value");
+ executor.execute(configFuture);
+ assertTrue(configFuture.get().contains("future.value"));
+
+ ListenableFutureTask configListenableFuture =
+ service.fetchConfigListenableTask("listenable.value");
+ executor.execute(configListenableFuture);
+ assertTrue(configListenableFuture.get().contains("listenable.value"));
+ }
+
+ @Test
+ public void givenNonFailingTask_whenCallbackListen_thenSuccess() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ ListenableFuture succeedingTask = new ListenableFutureService().succeedingTask();
+ Futures.addCallback(succeedingTask, new FutureCallback() {
+ @Override
+ public void onSuccess(Integer result) {
+ assertNotNull(result);
+ assertTrue(result >= 0);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Succeeding task cannot failed", t);
+ }
+ }, listeningExecutor);
+ }
+
+ @Test
+ public void givenFailingTask_whenCallbackListen_thenThrows() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ ListenableFuture failingTask = new ListenableFutureService().failingTask();
+ Futures.addCallback(failingTask, new FutureCallback() {
+ @Override
+ public void onSuccess(Integer result) {
+ fail("Failing task cannot succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertTrue(t instanceof ListenableFutureException);
+ }
+ }, listeningExecutor);
+ }
+
+ @Test
+ public void givenNonFailingTask_whenDirectListen_thenListenerExecutes() {
+ Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ int nextTask = 1;
+ Set runningTasks = ConcurrentHashMap.newKeySet();
+ runningTasks.add(nextTask);
+
+ ListenableFuture nonFailingTask = new ListenableFutureService().succeedingTask();
+ nonFailingTask.addListener(() -> runningTasks.remove(nextTask), listeningExecutor);
+
+ assertTrue(runningTasks.isEmpty());
+ }
+
+ @Test
+ public void givenFailingTask_whenDirectListen_thenListenerExecutes() {
+ final Executor listeningExecutor = MoreExecutors.directExecutor();
+
+ int nextTask = 1;
+ Set runningTasks = ConcurrentHashMap.newKeySet();
+ runningTasks.add(nextTask);
+
+ final ListenableFuture failingTask = new ListenableFutureService().failingTask();
+ failingTask.addListener(() -> runningTasks.remove(nextTask),listeningExecutor);
+
+ assertTrue(runningTasks.isEmpty());
+ }
+}
\ No newline at end of file
diff --git a/guava-modules/pom.xml b/guava-modules/pom.xml
index 957b8ad166..8ffac98b51 100644
--- a/guava-modules/pom.xml
+++ b/guava-modules/pom.xml
@@ -24,6 +24,7 @@
guava-collections-list
guava-collections-map
guava-collections-set
+ guava-concurrency
guava-io
diff --git a/httpclient-2/README.md b/httpclient-2/README.md
index 9d7a9683cd..7c2d5862bd 100644
--- a/httpclient-2/README.md
+++ b/httpclient-2/README.md
@@ -11,4 +11,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [How to Set TLS Version in Apache HttpClient](https://www.baeldung.com/apache-httpclient-tls)
- [Reading an HTTP Response Body as a String in Java](https://www.baeldung.com/java-http-response-body-as-string)
- [How To Get Cookies From the Apache HttpClient Response](https://www.baeldung.com/java-apache-httpclient-cookies)
+- [Enabling Logging for Apache HttpClient](https://www.baeldung.com/java-httpclient-enable-logging)
- More articles: [[<-- prev]](../httpclient)
diff --git a/httpclient-2/pom.xml b/httpclient-2/pom.xml
index 881b0407c3..a6b2ede900 100644
--- a/httpclient-2/pom.xml
+++ b/httpclient-2/pom.xml
@@ -26,6 +26,11 @@
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${httpclient5.version}
+
org.springframework.boot
@@ -63,6 +68,7 @@
4.5.8
+ 5.1
11
11
2.1.7.RELEASE
diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java
new file mode 100644
index 0000000000..35b21789ef
--- /dev/null
+++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClient5UnitTest.java
@@ -0,0 +1,29 @@
+package com.baeldung.httpclient.readresponsebodystring;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class ApacheHttpClient5UnitTest {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ public static final String DUMMY_URL = "https://postman-echo.com/get";
+
+ @Test
+ public void whenUseApacheHttpClient_thenCorrect() throws IOException, ParseException {
+ HttpGet request = new HttpGet(DUMMY_URL);
+
+ try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) {
+ HttpEntity entity = response.getEntity();
+ logger.debug("Response -> {}", EntityUtils.toString(entity));
+ }
+ }
+}
\ No newline at end of file
diff --git a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
index 5a638b2bd5..7625b415f2 100644
--- a/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
+++ b/httpclient-2/src/test/java/com/baeldung/httpclient/readresponsebodystring/ApacheHttpClientUnitTest.java
@@ -7,10 +7,13 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ApacheHttpClientUnitTest {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String DUMMY_URL = "https://postman-echo.com/get";
@Test
@@ -19,8 +22,7 @@ public class ApacheHttpClientUnitTest {
try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(request)) {
HttpEntity entity = response.getEntity();
- String result = EntityUtils.toString(entity);
- System.out.println("Response -> " + result);
+ logger.debug("Response -> {}", EntityUtils.toString(entity));
}
}
}
diff --git a/httpclient-2/src/test/resources/logback.xml b/httpclient-2/src/test/resources/logback.xml
new file mode 100644
index 0000000000..366a94e86e
--- /dev/null
+++ b/httpclient-2/src/test/resources/logback.xml
@@ -0,0 +1,15 @@
+
+
+
+ %date [%level] %logger - %msg %n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java-numbers-4/README.md b/java-numbers-4/README.md
index cdd53692e0..f053a82b80 100644
--- a/java-numbers-4/README.md
+++ b/java-numbers-4/README.md
@@ -3,3 +3,4 @@
- [Probability in Java](https://www.baeldung.com/java-probability)
- [Understanding the & 0xff Value in Java](https://www.baeldung.com/java-and-0xff)
- [Determine if an Integer’s Square Root Is an Integer in Java](https://www.baeldung.com/java-find-if-square-root-is-integer)
+- [Guide to Java BigInteger](https://www.baeldung.com/java-biginteger)
diff --git a/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java
new file mode 100644
index 0000000000..4ac185984d
--- /dev/null
+++ b/java-numbers-4/src/test/java/com/baeldung/biginteger/BigIntegerUnitTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.biginteger;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BigIntegerUnitTest {
+
+ @Test
+ void givenPositiveAndNegativeAndZeroBigInteger_whenGetSigNumValue_shouldReturnOneAndMinusOneAndZero() {
+ assertEquals(1, BigInteger.TEN.signum());
+ assertEquals(-1, BigInteger.TEN.negate().signum());
+ assertEquals(0, BigInteger.ZERO.signum());
+ }
+
+ @Test
+ void givenByteArrays_whenCreateBigInteger_shouldTranslateToTwosComplementBinary() {
+ assertEquals(new BigInteger("1"), new BigInteger(new byte[]{0b1}));
+ assertEquals(new BigInteger("2"), new BigInteger(new byte[]{0b10}));
+ assertEquals(new BigInteger("4"), new BigInteger(new byte[]{0b100}));
+ }
+
+ @Test
+ void givenSingleByte_whenCreateBigIntegerAndDifferentSigNum_thenOppositeValues() {
+ byte[] bytes = { (byte) -128 }; // 0b1000_0000
+
+ BigInteger positive = new BigInteger(1, bytes);
+ BigInteger negative = new BigInteger(-1, bytes);
+
+ assertEquals(new BigInteger("128"), positive);
+ assertEquals("10000000", positive.toString(2));
+
+ assertEquals(new BigInteger("-128"), negative);
+ assertEquals("-10000000", negative.toString(2));
+ }
+
+ @Test
+ void givenZeroBigInteger_whenCheckMagnitude_thenIsEmpty() {
+ assertEquals(0, BigInteger.ZERO.bitCount());
+ assertEquals(BigInteger.ZERO, new BigInteger(0, new byte[]{}));
+ }
+
+ @Test
+ void given63rdBitSet_whenCreateBigInteger_thenIsLargerThanLongByOne() {
+ // first
+ BigInteger bi1 = BigInteger.ZERO.setBit(63);
+ String str1 = bi1.toString(2);
+
+ // second
+ byte[] bytes = ByteBuffer.allocate(Long.BYTES).putLong(Long.MIN_VALUE).array();
+ BigInteger bi2 = new BigInteger(1, bytes);
+ String str2 = bi2.toString(2);
+
+ largerThanLongAssertionSet(bi1, str1);
+
+ assertEquals(bi1, bi2);
+
+ largerThanLongAssertionSet(bi2, str2);
+
+ }
+
+ private static void largerThanLongAssertionSet(BigInteger bi, String str)
+ {
+ assertEquals(64, bi.bitLength());
+ assertEquals(1, bi.signum());
+ assertEquals("9223372036854775808", bi.toString());
+ assertEquals(BigInteger.ONE, bi.subtract(BigInteger.valueOf(Long.MAX_VALUE)));
+
+ assertEquals(64, str.length());
+ assertTrue(str.matches("^10{63}$")); // 1000 0000
+ }
+}
diff --git a/jee-7/pom.xml b/jee-7/pom.xml
index 925d79728e..2030a769b0 100644
--- a/jee-7/pom.xml
+++ b/jee-7/pom.xml
@@ -487,7 +487,7 @@
1.0.0
- 1.0.0
+ 1.0-edr2
1.8
3.0.0
7.0
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml
new file mode 100644
index 0000000000..d463dd6dee
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Deployment.yaml
@@ -0,0 +1,54 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: web-app-deployment
+spec:
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 2
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app: web-app
+ replicas: 3
+ template:
+ metadata:
+ labels:
+ app: web-app
+ spec:
+ containers:
+ - name: web-app
+ image: hello-world:nanoserver-1809
+ volumeMounts:
+ - name: counter
+ mountPath: /app/
+ volumes:
+ - name: counter
+ persistentVolumeClaim:
+ claimName: counter
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: counter
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 50Mi
+ storageClassName: default
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: web-app-service
+spec:
+ ports:
+ - name: http
+ port: 80
+ nodePort: 30080
+ selector:
+ name: web-app
+ type: NodePort
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml
new file mode 100644
index 0000000000..99bbb5d555
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/PersitantVoulumeClaim.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: counter
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 50Mi
+ storageClassName: default
diff --git a/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml
new file mode 100644
index 0000000000..eabb12c0e0
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/Deployment/Service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: web-app-service
+spec:
+ ports:
+ - name: http
+ port: 80
+ nodePort: 30080
+ selector:
+ name: web-app
+ type: NodePort
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml
new file mode 100644
index 0000000000..59f137cff1
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/PersistantVolumeClaim.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: myclaim
+spec:
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 5Gi
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml
new file mode 100644
index 0000000000..80e5b50665
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/Service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx
+ labels:
+ app: nginx
+spec:
+ ports:
+ - port: 80
+ name: web
+ clusterIP: None
+ selector:
+ app: nginx
diff --git a/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml
new file mode 100644
index 0000000000..4ff4baca8c
--- /dev/null
+++ b/kubernetes/Deployment_Vs_StatefulSet/StatefulSets/StatefulSet.yaml
@@ -0,0 +1,28 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ selector:
+ matchLabels:
+ app: nginx
+ serviceName: "nginx"
+ replicas: 3
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx
+ ports:
+ - containerPort: 80
+ name: web
+ volumeMounts:
+ - name: www
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: www
+ persistentVolumeClaim:
+ claimName: myclaim
diff --git a/kubernetes/k8s-admission-controller/Dockerfile b/kubernetes/k8s-admission-controller/Dockerfile
new file mode 100644
index 0000000000..f8939ee7c8
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/Dockerfile
@@ -0,0 +1,11 @@
+FROM adoptopenjdk:11-jre-hotspot as builder
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} application.jar
+RUN java -Djarmode=layertools -jar application.jar extract
+
+FROM adoptopenjdk:11-jre-hotspot
+COPY --from=builder dependencies/ ./
+COPY --from=builder snapshot-dependencies/ ./
+COPY --from=builder spring-boot-loader/ ./
+COPY --from=builder application/ ./
+ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/pom.xml b/kubernetes/k8s-admission-controller/pom.xml
new file mode 100644
index 0000000000..fbee9ceba6
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/pom.xml
@@ -0,0 +1,86 @@
+
+
+ 4.0.0
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ./../../parent-boot-2
+
+
+ k8s-admission-controller
+ k8s-admission-controller
+ Demo project for Spring Boot
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ io.projectreactor
+ reactor-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+ com.baeldung.kubernetes.admission.Application
+
+ true
+
+
+
+
+
+
+
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java
new file mode 100644
index 0000000000..1260ec0af9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/Application.java
@@ -0,0 +1,17 @@
+package com.baeldung.kubernetes.admission;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+
+@SpringBootApplication
+@EnableConfigurationProperties(AdmissionControllerProperties.class)
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java
new file mode 100644
index 0000000000..f9103d85ba
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/config/AdmissionControllerProperties.java
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@ConfigurationProperties(prefix = "admission-controller")
+@Data
+public class AdmissionControllerProperties {
+
+ private boolean disabled;
+ private String annotation = "com.baeldung/wait-for-it";
+ private String waitForItImage = "willwill/wait-for-it";
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java
new file mode 100644
index 0000000000..21434535f9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/controller/AdmissionReviewController.java
@@ -0,0 +1,27 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.controller;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.baeldung.kubernetes.admission.service.AdmissionService;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.RequiredArgsConstructor;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequiredArgsConstructor
+public class AdmissionReviewController {
+
+ private final AdmissionService admissionService;
+
+ @PostMapping(path = "/mutate")
+ public Mono processAdmissionReviewRequest(@RequestBody Mono request) {
+ return request.map((body) -> admissionService.processAdmission(body));
+ }
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java
new file mode 100644
index 0000000000..6d590ff408
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewData.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * Result sent to the API server after reviewing and, possibly
+ * modifying the incoming request
+ */
+@Builder
+@Data
+public class AdmissionReviewData {
+
+ final String uid;
+ final boolean allowed;
+
+ @JsonInclude(Include.NON_NULL)
+ final String patchType;
+
+ @JsonInclude(Include.NON_NULL)
+ final String patch;
+
+ @JsonInclude(Include.NON_NULL)
+ final AdmissionStatus status;
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java
new file mode 100644
index 0000000000..07a3fa5b7a
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewException.java
@@ -0,0 +1,23 @@
+package com.baeldung.kubernetes.admission.dto;
+
+public class AdmissionReviewException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+ private final int code;
+
+ public AdmissionReviewException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public AdmissionReviewException(String message) {
+ super(message);
+ this.code = 400;
+
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java
new file mode 100644
index 0000000000..1780f52540
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionReviewResponse.java
@@ -0,0 +1,25 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.dto;
+
+import lombok.Builder;
+import lombok.Builder.Default;
+import lombok.Data;
+
+/**
+ * Response "envelope" sent back to the API Server
+ */
+@Builder
+@Data
+public class AdmissionReviewResponse {
+
+ @Default
+ final String apiVersion = "admission.k8s.io/v1";
+
+ @Default
+ final String kind = "AdmissionReview";
+
+ final AdmissionReviewData response;
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java
new file mode 100644
index 0000000000..ccab7ac958
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/dto/AdmissionStatus.java
@@ -0,0 +1,13 @@
+package com.baeldung.kubernetes.admission.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Builder
+@Data
+public class AdmissionStatus {
+
+ int code;
+ String message;
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java
new file mode 100644
index 0000000000..814bafbae7
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/main/java/com/baeldung/kubernetes/admission/service/AdmissionService.java
@@ -0,0 +1,218 @@
+/**
+ *
+ */
+package com.baeldung.kubernetes.admission.service;
+
+import java.util.Base64;
+import java.util.UUID;
+
+import org.springframework.stereotype.Component;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewData;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewException;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.baeldung.kubernetes.admission.dto.AdmissionStatus;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Process an incoming admission request and add the "wait-for-it" init container
+ * if there's an appropriate annotation
+ */
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class AdmissionService {
+
+ private final AdmissionControllerProperties admissionControllerProperties;
+ private final ObjectMapper om;
+
+ public AdmissionReviewResponse processAdmission(ObjectNode body) {
+
+ String uid = body.path("request")
+ .required("uid")
+ .asText();
+
+ log.info("[I42] processAdmission: uid={}",uid);
+ if ( log.isDebugEnabled()) {
+ log.debug("processAdmission: body={}", body.toPrettyString());
+ }
+
+ // Get request annotations
+ JsonNode annotations = body.path("request")
+ .path("object")
+ .path("metadata")
+ .path("annotations");
+ log.info("processAdmision: annotations={}", annotations.toString());
+
+ AdmissionReviewData data;
+ try {
+ if (admissionControllerProperties.isDisabled()) {
+ log.info("[I58] 'disabled' option in effect. No changes to current request will be made");
+ data = createSimpleAllowedReview(body);
+ } else if (annotations.isMissingNode()) {
+ log.info("[I68] No annotations found in request. No changes will be made");
+ data = createSimpleAllowedReview(body);
+ } else {
+ data = processAnnotations(body, annotations);
+ }
+
+ log.info("[I65] Review result: isAllowed=" + data.isAllowed());
+ log.info("[I64] AdmissionReviewData= {}", data);
+
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ } catch (AdmissionReviewException ex) {
+ log.error("[E72] Error processing AdmissionRequest: code={}, message={}", ex.getCode(), ex.getMessage());
+ data = createRejectedAdmissionReview(body, ex.getCode(), ex.getMessage());
+
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ } catch (Exception ex) {
+ log.error("[E72] Unable to process AdmissionRequest: " + ex.getMessage(), ex);
+ data = createRejectedAdmissionReview(body, 500, ex.getMessage());
+ return AdmissionReviewResponse.builder()
+ .apiVersion(body.required("apiVersion").asText())
+ .kind(body.required("kind").asText())
+ .response(data)
+ .build();
+ }
+ }
+
+ /**
+ * @param body
+ * @return
+ */
+ protected AdmissionReviewData createSimpleAllowedReview(ObjectNode body) {
+ AdmissionReviewData data;
+ String requestId = body.path("request")
+ .required("uid")
+ .asText();
+
+ data = AdmissionReviewData.builder()
+ .allowed(true)
+ .uid(requestId)
+ .build();
+
+ return data;
+
+ }
+
+ /**
+ * @param body
+ * @return
+ */
+ protected AdmissionReviewData createRejectedAdmissionReview(ObjectNode body, int code, String message) {
+ AdmissionReviewData data;
+ String requestId = body.path("request")
+ .required("uid")
+ .asText();
+
+ AdmissionStatus status = AdmissionStatus.builder()
+ .code(code)
+ .message(message)
+ .build();
+
+ data = AdmissionReviewData.builder()
+ .allowed(false)
+ .uid(requestId)
+ .status(status)
+ .build();
+
+ return data;
+
+ }
+
+ /**
+ * Processa anotações incluídas no deployment
+ * @param annotations
+ * @return
+ */
+ protected AdmissionReviewData processAnnotations(ObjectNode body, JsonNode annotations) {
+
+ if (annotations.path(admissionControllerProperties.getAnnotation())
+ .isMissingNode()) {
+ log.info("[I78] processAnnotations: Annotation {} not found in deployment deployment.", admissionControllerProperties.getAnnotation());
+ return createSimpleAllowedReview(body);
+ }
+ else {
+ log.info("[I163] annotation found: {}", annotations.path(admissionControllerProperties.getAnnotation()));
+ }
+
+ // Get wait-for-it arguments from the annotation
+ String waitForArgs = annotations.path(admissionControllerProperties.getAnnotation())
+ .asText();
+
+ log.info("[I169] waitForArgs={}", waitForArgs);
+ // Create a PATCH object
+ String patch = injectInitContainer(body, waitForArgs);
+
+ return AdmissionReviewData.builder()
+ .allowed(true)
+ .uid(body.path("request")
+ .required("uid")
+ .asText())
+ .patch(Base64.getEncoder()
+ .encodeToString(patch.getBytes()))
+ .patchType("JSONPatch")
+ .build();
+
+ }
+
+ /**
+ * Creates the JSONPatch to be included in the admission response
+ * @param body
+ * @param waitForArgs
+ * @return JSONPatch string
+ */
+ protected String injectInitContainer(ObjectNode body, String waitForArgs) {
+
+ // Recover original init containers from the request
+ JsonNode originalSpec = body.path("request")
+ .path("object")
+ .path("spec")
+ .path("template")
+ .path("spec")
+ .require();
+
+ JsonNode maybeInitContainers = originalSpec.path("initContainers");
+ ArrayNode initContainers =
+ maybeInitContainers.isMissingNode()?
+ om.createArrayNode():(ArrayNode) maybeInitContainers;
+
+ // Create the patch array
+ ArrayNode patchArray = om.createArrayNode();
+ ObjectNode addNode = patchArray.addObject();
+
+ addNode.put("op", "add");
+ addNode.put("path", "/spec/template/spec/initContainers");
+ ArrayNode values = addNode.putArray("value");
+
+ // Preserve original init containers
+ values.addAll(initContainers);
+
+ // append the "wait-for-it" container
+ ObjectNode wfi = values.addObject();
+ wfi.put("name", "wait-for-it-" + UUID.randomUUID()); // Create an unique name, JIC
+ wfi.put("image", admissionControllerProperties.getWaitForItImage());
+
+ ArrayNode args = wfi.putArray("args");
+ for (String s : waitForArgs.split("\\s")) {
+ args.add(s);
+ }
+
+ return patchArray.toString();
+ }
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java
new file mode 100644
index 0000000000..544b48a9d4
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/java/com/baeldung/kubernetes/admission/service/AdmissionServiceUnitTest.java
@@ -0,0 +1,65 @@
+package com.baeldung.kubernetes.admission.service;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Base64;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import com.baeldung.kubernetes.admission.config.AdmissionControllerProperties;
+import com.baeldung.kubernetes.admission.dto.AdmissionReviewResponse;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@SpringBootTest
+@ActiveProfiles("test")
+@EnableConfigurationProperties(AdmissionControllerProperties.class)
+class AdmissionServiceUnitTest {
+
+ @Autowired
+ private ObjectMapper mapper;
+
+ @Autowired
+ private AdmissionService admissionService;
+
+ @Test
+ void whenAnnotationPresent_thenAddContainer() throws Exception {
+
+ InputStream is = this.getClass()
+ .getClassLoader()
+ .getResourceAsStream("test1.json");
+ JsonNode body = mapper.readTree(is);
+ AdmissionReviewResponse response = admissionService.processAdmission((ObjectNode) body);
+ assertNotNull(response);
+ assertNotNull(response.getResponse());
+ assertNotNull(response.getResponse());
+ assertTrue(response.getResponse()
+ .isAllowed());
+
+ String jsonResponse = mapper.writeValueAsString(response);
+ System.out.println(jsonResponse);
+
+ // Decode Patch data
+ String b64patch = response.getResponse()
+ .getPatch();
+ assertNotNull(b64patch);
+ byte[] patch = Base64.getDecoder()
+ .decode(b64patch);
+
+ JsonNode root = mapper.reader()
+ .readTree(new ByteArrayInputStream(patch));
+ assertTrue(root instanceof ArrayNode);
+
+ assertEquals(1, ((ArrayNode) root).size());
+
+ }
+
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
new file mode 100644
index 0000000000..0c601660d1
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/k8s/nginx.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: frontend
+ labels:
+ app: nginx
+ annotations:
+ com.baeldung/wait-for-it: "www.google.com:80"
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ ports:
+ - containerPort: 80
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test1.json b/kubernetes/k8s-admission-controller/src/test/resources/test1.json
new file mode 100644
index 0000000000..0ad42ae133
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test1.json
@@ -0,0 +1,84 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test2.json b/kubernetes/k8s-admission-controller/src/test/resources/test2.json
new file mode 100644
index 0000000000..5b8a6ca243
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test2.json
@@ -0,0 +1,85 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "initContainers": [],
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test3.json b/kubernetes/k8s-admission-controller/src/test/resources/test3.json
new file mode 100644
index 0000000000..9cb4d516f9
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test3.json
@@ -0,0 +1,94 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "c46a6607-129d-425b-af2f-c6f87a0756da",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "requestKind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "requestResource": {
+ "group": "apps",
+ "version": "v1",
+ "resource": "deployments"
+ },
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "operation": "CREATE",
+ "object": {
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": {
+ "name": "test-deployment",
+ "namespace": "test-namespace",
+ "annotations": {
+ "com.baeldung/wait-for-it": "www.google.com:80"
+ }
+ },
+ "spec": {
+ "replicas": 1,
+ "selector": {
+ "matchLabels": {
+ "app": "test-app"
+ }
+ },
+ "template": {
+ "metadata": {
+ "name": "test-app",
+ "creationTimestamp": null,
+ "labels": {
+ "app": "test-app"
+ }
+ },
+ "spec": {
+ "initContainers": [
+ {
+ "name": "init1",
+ "image": "test-app-image:latest",
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ],
+ "containers": [
+ {
+ "name": "app",
+ "image": "test-app-image:latest",
+ "ports": [
+ {
+ "name": "http",
+ "containerPort": 8080,
+ "protocol": "TCP"
+ }
+ ],
+ "resources": {},
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Always"
+ }
+ ]
+ }
+ }
+ },
+ "status": {}
+ },
+ "oldObject": null,
+ "dryRun": false,
+ "options": {
+ "kind": "CreateOptions",
+ "apiVersion": "meta.k8s.io/v1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/resources/test4.json b/kubernetes/k8s-admission-controller/src/test/resources/test4.json
new file mode 100644
index 0000000000..1e3be90a93
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/resources/test4.json
@@ -0,0 +1,48 @@
+{
+ "kind": "AdmissionReview",
+ "apiVersion": "admission.k8s.io/v1",
+ "request": {
+ "uid": "26beb334-739a-48d2-b04d-25f6e5e7c106",
+ "kind": {
+ "group": "apps",
+ "version": "v1",
+ "kind": "Deployment"
+ },
+ "resource": {},
+ "f:type": {}
+ },
+ "f:template": {
+ "f:metadata": {
+ "f:labels": {
+ ".": {},
+ "f:app": {}
+ }
+ },
+ "f:spec": {
+ "f:containers": {
+ "k:{\"name\":\"nginx\"}": {
+ ".": {},
+ "f:image": {},
+ "f:imagePullPolicy": {},
+ "f:name": {},
+ "f:ports": {
+ ".": {},
+ "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
+ ".": {},
+ "f:containerPort": {},
+ "f:protocol": {}
+ }
+ },
+ "f:resources": {},
+ "f:terminationMessagePath": {},
+ "f:terminationMessagePolicy": {}
+ }
+ },
+ "f:dnsPolicy": {},
+ "f:restartPolicy": {},
+ "f:schedulerName": {},
+ "f:securityContext": {},
+ "f:terminationGracePeriodSeconds": {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
new file mode 100644
index 0000000000..4c305a080a
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/.gitignore
@@ -0,0 +1,3 @@
+.terraform
+terraform.tfstate
+terraform.tfstate.backup
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
new file mode 100644
index 0000000000..e13c37b4ec
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/.terraform.lock.hcl
@@ -0,0 +1,57 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/kubernetes" {
+ version = "2.3.2"
+ constraints = "2.3.2"
+ hashes = [
+ "h1:D8HWX3vouTPI3Jicq43xOQyoYWtSsVua92cBVrJ3ZMs=",
+ "zh:10f71c170be13538374a4b9553fcb3d98a6036bcd1ca5901877773116c3f828e",
+ "zh:11d2230e531b7480317e988207a73cb67b332f225b0892304983b19b6014ebe0",
+ "zh:3317387a9a6cc27fd7536b8f3cad4b8a9285e9461f125c5a15d192cef3281856",
+ "zh:458a9858362900fbe97e00432ae8a5bef212a4dacf97a57ede7534c164730da4",
+ "zh:50ea297007d9fe53e5411577f87a4b13f3877ce732089b42f938430e6aadff0d",
+ "zh:56705c959e4cbea3b115782d04c62c68ac75128c5c44ee7aa4043df253ffbfe3",
+ "zh:7eb3722f7f036e224824470c3e0d941f1f268fcd5fa2f8203e0eee425d0e1484",
+ "zh:9f408a6df4d74089e6ce18f9206b06b8107ddb57e2bc9b958a6b7dc352c62980",
+ "zh:aadd25ccc3021040808feb2645779962f638766eb583f586806e59f24dde81bb",
+ "zh:b101c3456e4309b09aab129b0118561178c92cb4be5d96dec553189c3084dca1",
+ "zh:ec08478573b4953764099fbfd670fae81dc24b60e467fb3b023e6fab50b70a9e",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/null" {
+ version = "3.1.0"
+ hashes = [
+ "h1:SFT7X3zY18CLWjoH2GfQyapxsRv6GDKsy9cF1aRwncc=",
+ "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2",
+ "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515",
+ "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521",
+ "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2",
+ "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e",
+ "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53",
+ "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d",
+ "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8",
+ "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70",
+ "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b",
+ "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/tls" {
+ version = "3.1.0"
+ hashes = [
+ "h1:ekOxs6MjdIElt8h9crEVaOwWbEqtfUUfArtA13Jkk6A=",
+ "zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6",
+ "zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2",
+ "zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e",
+ "zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca",
+ "zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698",
+ "zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d",
+ "zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841",
+ "zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989",
+ "zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5",
+ "zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d",
+ "zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0",
+ ]
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars
new file mode 100644
index 0000000000..eb1cdaea53
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/component.auto.tfvars
@@ -0,0 +1,10 @@
+#
+# Sample variable values.
+#
+namespace="default"
+deployment_name="wait-for-it-admission-controller"
+replicas=1
+image="psevestre/wait-for-it-admission-controller"
+image_prefix=""
+image_version="latest"
+k8s_config_context="minikube"
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/main.tf b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf
new file mode 100644
index 0000000000..a0717c4013
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/main.tf
@@ -0,0 +1,277 @@
+
+locals {
+ prefix = var.image_prefix != "" ? "${var.image_prefix}/":""
+ image = "${local.prefix}${var.image}:${var.image_version}"
+ cloud_sdk_image = "${local.prefix}frapsoft/openssl"
+ ns = data.kubernetes_namespace.ns.metadata[0].name
+
+ # Spring SSL Configuration
+ webhook_config_json = jsonencode({
+ server = {
+ port = 443
+ ssl = {
+ "key-store" = "/shared-config/webhook.p12"
+ "key-store-type" = "PKCS12"
+ "key-alias" = "webhook"
+ "key-store-password" = ""
+ }
+ }
+
+ admission-controller = {
+ disabled = false
+ image-prefix = "gcr.io/sandboxbv-01"
+ }
+ })
+
+}
+
+
+# Resource namespace
+data "kubernetes_namespace" "ns" {
+ metadata {
+ name = var.namespace
+ }
+}
+
+# TLS Key
+resource "tls_private_key" "tls" {
+ algorithm = "RSA"
+}
+
+# CSR
+resource "tls_cert_request" "tls" {
+ key_algorithm = "RSA"
+ private_key_pem = tls_private_key.tls.private_key_pem
+ subject {
+ common_name = "${var.deployment_name}.${var.namespace}.svc"
+ }
+
+ dns_names = [
+ var.deployment_name,
+ "${var.deployment_name}.${var.namespace}",
+ "${var.deployment_name}.${var.namespace}.svc",
+ "${var.deployment_name}.${var.namespace}.svc.cluster.local"
+ ]
+
+}
+
+# HTTPS Certificate
+resource "tls_self_signed_cert" "tls" {
+ key_algorithm = tls_private_key.tls.algorithm
+ private_key_pem = tls_private_key.tls.private_key_pem
+
+ subject {
+ common_name = "${var.deployment_name}.${local.ns}"
+ }
+
+ validity_period_hours = 24*365*20
+
+ dns_names = [
+ var.deployment_name,
+ "${var.deployment_name}.${var.namespace}",
+ "${var.deployment_name}.${var.namespace}.svc",
+ "${var.deployment_name}.${var.namespace}.svc.cluster.local"
+ ]
+
+ allowed_uses = [
+ "key_encipherment",
+ "digital_signature",
+ "server_auth"
+ ]
+}
+
+# Certificado
+# Obs: Desativado pois o certificado fica preso no estado "Issued"
+resource "kubernetes_certificate_signing_request" "tls" {
+ count = 0
+ metadata {
+ name = "${var.deployment_name}.${var.namespace}"
+ }
+
+ auto_approve = true
+
+ spec {
+ usages = [
+ "key encipherment",
+ "digital signature",
+ "server auth"
+ ]
+
+ signer_name = "kubernetes.io/kubelet-serving"
+
+ request = tls_cert_request.tls.cert_request_pem
+ }
+
+}
+
+# Secret to store TLS key/cert
+resource "kubernetes_secret" "tls" {
+ metadata {
+ namespace = local.ns
+ name = var.deployment_name
+ }
+
+ data = {
+ "webhook-key.pem" = tls_private_key.tls.private_key_pem
+ "webhook-cert.pem" = tls_self_signed_cert.tls.cert_pem
+ }
+
+}
+
+# Deployment
+resource "kubernetes_deployment" "main" {
+ metadata {
+ name = var.deployment_name
+ namespace = local.ns
+ }
+
+ spec {
+ replicas = var.replicas
+ selector {
+ match_labels = {
+ app = var.deployment_name
+ }
+ }
+
+ template {
+ metadata {
+ labels = {
+ app = var.deployment_name
+ }
+ }
+
+ spec {
+ container {
+ image = local.image
+ name = var.deployment_name
+ volume_mount {
+ mount_path = "/shared-config"
+ name = "shared-config"
+ }
+
+ env {
+ name = "SPRING_APPLICATION_JSON"
+ value = local.webhook_config_json
+ }
+
+ }
+
+ init_container {
+ name = "setup-keystore"
+ image = local.cloud_sdk_image
+
+ args = [
+ "pkcs12", "-export",
+ "-in", "/secret/webhook-cert.pem",
+ "-inkey", "/secret/webhook-key.pem",
+ "-name", "webhook",
+ "-out", "/shared-config/webhook.p12",
+ "-passout", "pass:"
+ ]
+
+ volume_mount {
+ mount_path = "/shared-config"
+ name = "shared-config"
+ }
+
+ volume_mount {
+ mount_path = "/secret/webhook-cert.pem"
+ name = "webhook-secret"
+ sub_path = "webhook-cert.pem"
+ }
+
+ volume_mount {
+ mount_path = "/secret/webhook-key.pem"
+ name = "webhook-secret"
+ sub_path = "webhook-key.pem"
+ }
+
+ }
+
+ volume {
+ name = "shared-config"
+ empty_dir {}
+ }
+
+ volume {
+ name = "webhook-secret"
+ secret {
+ secret_name = kubernetes_secret.tls.metadata[0].name
+ items {
+ key = "webhook-cert.pem"
+ path = "webhook-cert.pem"
+ }
+ items {
+ key = "webhook-key.pem"
+ path = "webhook-key.pem"
+ }
+ }
+ }
+
+ }
+ }
+ }
+}
+
+# Service
+resource "kubernetes_service" "svc" {
+ metadata {
+ name = var.deployment_name
+ namespace = local.ns
+ }
+
+ spec {
+ selector = {
+ "app" = var.deployment_name
+ }
+
+ port {
+ name = "https"
+ port = 443
+ protocol = "TCP"
+ target_port = 443
+ }
+
+ type = "ClusterIP"
+ }
+}
+
+# Admission Controller
+resource "kubernetes_mutating_webhook_configuration" "waitforit" {
+ metadata {
+ name = var.deployment_name
+ }
+
+ webhook {
+ name = var.admission_controller_name
+ admission_review_versions = [ "v1", "v1beta1" ]
+
+ #failure_policy = "Ignore" #
+
+ client_config {
+
+ service {
+ name = kubernetes_service.svc.metadata[0].name
+ namespace = local.ns
+ path = "/mutate"
+ port = 443
+ }
+
+ # IMPORTANT: CA_BUNDLE must be Base64-encoded
+ ca_bundle = tls_self_signed_cert.tls.cert_pem
+ }
+
+ rule {
+ api_groups = [ "*" ]
+ api_versions = [ "*" ]
+ operations = [ "CREATE", "UPDATE" ]
+ resources = [ "deployments", "statefulsets" ]
+ }
+
+ side_effects = "None" #
+ }
+
+ depends_on = [
+ kubernetes_deployment.main
+ ]
+}
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf
new file mode 100644
index 0000000000..eb095caa0e
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/providers.tf
@@ -0,0 +1,14 @@
+terraform {
+ required_providers {
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = "2.3.2"
+ }
+ }
+}
+
+# Use standard kubectl environment to get connection details
+provider "kubernetes" {
+ config_context = var.k8s_config_context
+ config_path = var.k8s_config_path
+}
\ No newline at end of file
diff --git a/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf
new file mode 100644
index 0000000000..33ca735f0d
--- /dev/null
+++ b/kubernetes/k8s-admission-controller/src/test/terraform/variables.tf
@@ -0,0 +1,50 @@
+variable "namespace" {
+ type = string
+ description = "Namespace where the Admission Controller will be deploymed"
+}
+
+variable "deployment_name" {
+ type = string
+ description = "Admission Controller Deployment Name"
+}
+
+variable "replicas" {
+ type = number
+ description = "Number of replicas used in the deployment"
+ default = 3
+}
+
+variable "image" {
+ type = string
+ description = "Admission Controller image name"
+}
+
+variable "image_version" {
+ type = string
+ description = "Admission Controller image version name"
+ default = "latest"
+}
+
+variable "image_prefix" {
+ type = string
+ description = "Image repository prefix"
+ default = "gcr.io/baeldung"
+}
+
+variable "admission_controller_name" {
+ type = string
+ description = "Admission Controller name"
+ default = "wait-for-it.service.local"
+}
+
+variable "k8s_config_context" {
+ type = string
+ description = "Name of the K8S config context"
+}
+
+variable "k8s_config_path" {
+ type = string
+ description = "Location of the standard K8S configuration"
+ default = "~/.kube/config"
+
+}
\ No newline at end of file
diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml
index 72cb1577a4..57ae4fd596 100644
--- a/kubernetes/pom.xml
+++ b/kubernetes/pom.xml
@@ -1,6 +1,5 @@
-
+
+
4.0.0
kubernetes
pom
@@ -13,5 +12,6 @@
k8s-intro
-
+ k8s-admission-controller
+
\ No newline at end of file
diff --git a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
index 4b35a94eb5..cce8b78808 100644
--- a/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
+++ b/libraries-data-db/src/main/java/com/baeldung/libraries/debezium/service/CustomerService.java
@@ -21,7 +21,7 @@ public class CustomerService {
final ObjectMapper mapper = new ObjectMapper();
final Customer customer = mapper.convertValue(customerData, Customer.class);
- if (Operation.DELETE.name().equals(operation.name())) {
+ if (Operation.DELETE == operation) {
customerRepository.deleteById(customer.getId());
} else {
customerRepository.save(customer);
diff --git a/libraries-data-io/README.md b/libraries-data-io/README.md
index 3e68334ec9..16c7cc66eb 100644
--- a/libraries-data-io/README.md
+++ b/libraries-data-io/README.md
@@ -10,3 +10,4 @@ This module contains articles about IO data processing libraries.
- [Interact with Google Sheets from Java](https://www.baeldung.com/google-sheets-java-client)
- [Introduction To Docx4J](https://www.baeldung.com/docx4j)
- [Breaking YAML Strings Over Multiple Lines](https://www.baeldung.com/yaml-multi-line)
+- [Different Serialization Approaches for Java](https://www.baeldung.com/java-serialization-approaches)
diff --git a/libraries-data-io/pom.xml b/libraries-data-io/pom.xml
index 58bfde9aa0..1335ba54d1 100644
--- a/libraries-data-io/pom.xml
+++ b/libraries-data-io/pom.xml
@@ -55,6 +55,46 @@
docx4j
${docx4j.version}
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.version}
+
+
+
+ com.esotericsoftware.yamlbeans
+ yamlbeans
+ ${yamlbeans.version}
+
+
+
+ org.apache.thrift
+ libthrift
+ ${apache-thrift.version}
+
+
+
+ com.google.protobuf
+ protobuf-java
+ ${google-protobuf.version}
+
org.assertj
assertj-core
@@ -73,6 +113,11 @@
3.9.0
3.3.5
2.1
+ 2.8.7
+ 2.12.3
+ 1.15
+ 0.14.2
+ 3.17.3
\ No newline at end of file
diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java
new file mode 100644
index 0000000000..27eadfb5f6
--- /dev/null
+++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/User.java
@@ -0,0 +1,32 @@
+package com.baeldung.serialization.protocols;
+
+import java.io.Serializable;
+
+public class User implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private int id;
+ private String name;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "User [id=" + id + ", name=" + name + "]";
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java
new file mode 100644
index 0000000000..adefabb7e8
--- /dev/null
+++ b/libraries-data-io/src/main/java/com/baeldung/serialization/protocols/UserProtos.java
@@ -0,0 +1,636 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: user.proto
+
+package com.baeldung.serialization.protocols;
+
+public final class UserProtos {
+ private UserProtos() {
+ }
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+
+ public interface UserOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:protobuf.User)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ int getId();
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ java.lang.String getName();
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ com.google.protobuf.ByteString getNameBytes();
+ }
+
+ /**
+ * Protobuf type {@code protobuf.User}
+ */
+ public static final class User extends com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:protobuf.User)
+ UserOrBuilder {
+ private static final long serialVersionUID = 0L;
+
+ // Use User.newBuilder() to construct.
+ private User(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+
+ private User() {
+ name_ = "";
+ }
+
+ @java.lang.Override
+ @SuppressWarnings({ "unused" })
+ protected java.lang.Object newInstance(UnusedPrivateParameter unused) {
+ return new User();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
+ return this.unknownFields;
+ }
+
+ private User(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 8: {
+
+ id_ = input.readInt32();
+ break;
+ }
+ case 18: {
+ java.lang.String s = input.readStringRequireUtf8();
+
+ name_ = s;
+ break;
+ }
+ default: {
+ if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class,
+ com.baeldung.serialization.protocols.UserProtos.User.Builder.class);
+ }
+
+ public static final int ID_FIELD_NUMBER = 1;
+ private int id_;
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ @java.lang.Override
+ public int getId() {
+ return id_;
+ }
+
+ public static final int NAME_FIELD_NUMBER = 2;
+ private volatile java.lang.Object name_;
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ @java.lang.Override
+ public java.lang.String getName() {
+ java.lang.Object ref = name_;
+ if (ref instanceof java.lang.String) {
+ return (java.lang.String) ref;
+ } else {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ name_ = s;
+ return s;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ @java.lang.Override
+ public com.google.protobuf.ByteString getNameBytes() {
+ java.lang.Object ref = name_;
+ if (ref instanceof java.lang.String) {
+ com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ name_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ private byte memoizedIsInitialized = -1;
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1)
+ return true;
+ if (isInitialized == 0)
+ return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
+ if (id_ != 0) {
+ output.writeInt32(1, id_);
+ }
+ if (!getNameBytes().isEmpty()) {
+ com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1)
+ return size;
+
+ size = 0;
+ if (id_ != 0) {
+ size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, id_);
+ }
+ if (!getNameBytes().isEmpty()) {
+ size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof com.baeldung.serialization.protocols.UserProtos.User)) {
+ return super.equals(obj);
+ }
+ com.baeldung.serialization.protocols.UserProtos.User other = (com.baeldung.serialization.protocols.UserProtos.User) obj;
+
+ if (getId() != other.getId())
+ return false;
+ if (!getName().equals(other.getName()))
+ return false;
+ if (!unknownFields.equals(other.unknownFields))
+ return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + ID_FIELD_NUMBER;
+ hash = (53 * hash) + getId();
+ hash = (37 * hash) + NAME_FIELD_NUMBER;
+ hash = (53 * hash) + getName().hashCode();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseDelimitedFrom(java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User parseFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() {
+ return newBuilder();
+ }
+
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+
+ public static Builder newBuilder(com.baeldung.serialization.protocols.UserProtos.User prototype) {
+ return DEFAULT_INSTANCE.toBuilder()
+ .mergeFrom(prototype);
+ }
+
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+
+ /**
+ * Protobuf type {@code protobuf.User}
+ */
+ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:protobuf.User)
+ com.baeldung.serialization.protocols.UserProtos.UserOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_fieldAccessorTable.ensureFieldAccessorsInitialized(com.baeldung.serialization.protocols.UserProtos.User.class,
+ com.baeldung.serialization.protocols.UserProtos.User.Builder.class);
+ }
+
+ // Construct using com.baeldung.serialization.compare.UserProtos.User.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {
+ }
+ }
+
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ id_ = 0;
+
+ name_ = "";
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
+ return com.baeldung.serialization.protocols.UserProtos.internal_static_protobuf_User_descriptor;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() {
+ return com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User build() {
+ com.baeldung.serialization.protocols.UserProtos.User result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User buildPartial() {
+ com.baeldung.serialization.protocols.UserProtos.User result = new com.baeldung.serialization.protocols.UserProtos.User(this);
+ result.id_ = id_;
+ result.name_ = name_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+
+ @java.lang.Override
+ public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.setField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+
+ @java.lang.Override
+ public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+
+ @java.lang.Override
+ public Builder setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+
+ @java.lang.Override
+ public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof com.baeldung.serialization.protocols.UserProtos.User) {
+ return mergeFrom((com.baeldung.serialization.protocols.UserProtos.User) other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(com.baeldung.serialization.protocols.UserProtos.User other) {
+ if (other == com.baeldung.serialization.protocols.UserProtos.User.getDefaultInstance())
+ return this;
+ if (other.getId() != 0) {
+ setId(other.getId());
+ }
+ if (!other.getName()
+ .isEmpty()) {
+ name_ = other.name_;
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
+ com.baeldung.serialization.protocols.UserProtos.User parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (com.baeldung.serialization.protocols.UserProtos.User) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private int id_;
+
+ /**
+ * int32 id = 1;
+ * @return The id.
+ */
+ @java.lang.Override
+ public int getId() {
+ return id_;
+ }
+
+ /**
+ * int32 id = 1;
+ * @param value The id to set.
+ * @return This builder for chaining.
+ */
+ public Builder setId(int value) {
+
+ id_ = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * int32 id = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearId() {
+
+ id_ = 0;
+ onChanged();
+ return this;
+ }
+
+ private java.lang.Object name_ = "";
+
+ /**
+ * string name = 2;
+ * @return The name.
+ */
+ public java.lang.String getName() {
+ java.lang.Object ref = name_;
+ if (!(ref instanceof java.lang.String)) {
+ com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
+ java.lang.String s = bs.toStringUtf8();
+ name_ = s;
+ return s;
+ } else {
+ return (java.lang.String) ref;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @return The bytes for name.
+ */
+ public com.google.protobuf.ByteString getNameBytes() {
+ java.lang.Object ref = name_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
+ name_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ /**
+ * string name = 2;
+ * @param value The name to set.
+ * @return This builder for chaining.
+ */
+ public Builder setName(java.lang.String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ name_ = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * string name = 2;
+ * @return This builder for chaining.
+ */
+ public Builder clearName() {
+
+ name_ = getDefaultInstance().getName();
+ onChanged();
+ return this;
+ }
+
+ /**
+ * string name = 2;
+ * @param value The bytes for name to set.
+ * @return This builder for chaining.
+ */
+ public Builder setNameBytes(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ checkByteStringIsUtf8(value);
+
+ name_ = value;
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+ // @@protoc_insertion_point(builder_scope:protobuf.User)
+ }
+
+ // @@protoc_insertion_point(class_scope:protobuf.User)
+ private static final com.baeldung.serialization.protocols.UserProtos.User DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new com.baeldung.serialization.protocols.UserProtos.User();
+ }
+
+ public static com.baeldung.serialization.protocols.UserProtos.User getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public User parsePartialFrom(com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {
+ return new User(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.baeldung.serialization.protocols.UserProtos.User getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor internal_static_protobuf_User_descriptor;
+ private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_protobuf_User_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
+ static {
+ java.lang.String[] descriptorData = { "\n\nuser.proto\022\010protobuf\" \n\004User\022\n\n\002id\030\001 \001" + "(\005\022\014\n\004name\030\002 \001(\tB0\n\"com.baeldung.seriali" + "zation.compareB\nUserProtosb\006proto3" };
+ descriptor = com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {});
+ internal_static_protobuf_User_descriptor = getDescriptor().getMessageTypes()
+ .get(0);
+ internal_static_protobuf_User_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(internal_static_protobuf_User_descriptor, new java.lang.String[] { "Id", "Name", });
+ }
+ // @@protoc_insertion_point(outer_class_scope)
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java
new file mode 100644
index 0000000000..7b6aa0a24b
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/ApacheThriftSerializationUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.junit.jupiter.api.Test;
+
+public class ApacheThriftSerializationUnitTest {
+
+ @Test
+ public void whenUsingThriftForSerialization_ThenDataIsSameAfterDeserialization() throws TException {
+
+ User user = new User();
+ user.setId(2);
+ user.setName("Greg");
+
+ TMemoryBuffer trans = new TMemoryBuffer(4096);
+ TProtocol proto = new TBinaryProtocol(trans);
+
+ proto.writeI32(user.getId());
+ proto.writeString(user.getName());
+
+ int userId = proto.readI32();
+ String userName = proto.readString();
+
+ assertEquals(2, userId);
+ assertEquals("Greg", userName);
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java
new file mode 100644
index 0000000000..46d66049d4
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GoogleProtocolBufferUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+public class GoogleProtocolBufferUnitTest {
+
+ @Test
+ public void whenUsingProtocolBuffersSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException {
+
+ String filePath = "src/test/resources/protocols/usersproto";
+
+ UserProtos.User user = UserProtos.User.newBuilder().setId(1234).setName("John Doe").build();
+ FileOutputStream fos = new FileOutputStream(filePath);
+ user.writeTo(fos);
+
+ UserProtos.User deserializedUser = UserProtos.User.newBuilder().mergeFrom(new FileInputStream(filePath)).build();
+
+ assertEquals(1234, deserializedUser.getId());
+ assertEquals("John Doe", deserializedUser.getName());
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java
new file mode 100644
index 0000000000..513dcf6ce0
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/GsonSerializationUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class GsonSerializationUnitTest {
+
+ @Test
+ public void whenSerializedUsingGson_ThenObjectIsSameAfterDeserialization() {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark");
+
+ String filePath = "src/test/resources/protocols/gson_user.json";
+
+ try (Writer writer = new FileWriter(filePath)) {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ gson.toJson(user, writer);
+
+ assertTrue(Files.exists(Paths.get(filePath)));
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ User deserializedUser = gson.fromJson(new FileReader(filePath), User.class);
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark", deserializedUser.getName());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java
new file mode 100644
index 0000000000..4bfb88b290
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JacksonSerializationUnitTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JacksonSerializationUnitTest {
+
+ @Test
+ public void whenUsingJacksonAPIForJSONSerialization_thenDeserializeCorrectObject() throws IOException {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark Jonson");
+
+ String filePath = "src/test/resources/protocols/jackson_user.json";
+
+ File file = new File(filePath);
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(file, user);
+
+ User deserializedUser = mapper.readValue(new File(filePath), User.class);
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark Jonson", deserializedUser.getName());
+ }
+
+ public static List populateListOfUsers() {
+ User user1 = new User();
+ user1.setId(1);
+ user1.setName("Mark Jonson");
+
+ User user2 = new User();
+ user2.setId(2);
+ user2.setName("Johny Beth");
+
+ User user3 = new User();
+ user3.setId(3);
+ user3.setName("Eliza Green");
+
+ User user4 = new User();
+ user4.setId(4);
+ user4.setName("Monica Doe");
+
+ List users = new ArrayList<>();
+ users.add(user1);
+ users.add(user2);
+ users.add(user3);
+ users.add(user4);
+
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java
new file mode 100644
index 0000000000..bb4cc0df0e
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/JavaNativeSerializationUnitTest.java
@@ -0,0 +1,38 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.junit.jupiter.api.Test;
+
+public class JavaNativeSerializationUnitTest {
+
+ @Test
+ public void whenUsingJavaNativeSerialization_ThenObjectIsTheSameAfterDeserialization() throws IOException, ClassNotFoundException {
+
+ User user = new User();
+ user.setId(1);
+ user.setName("Mark");
+
+ String filePath = "src/test/resources/protocols/user.txt";
+
+ FileOutputStream fileOutputStream = new FileOutputStream(filePath);
+ ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
+ objectOutputStream.writeObject(user);
+ objectOutputStream.flush();
+ objectOutputStream.close();
+
+ FileInputStream fileInputStream = new FileInputStream(filePath);
+ ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
+ User deserializedUser = (User) objectInputStream.readObject();
+ objectInputStream.close();
+
+ assertEquals(1, deserializedUser.getId());
+ assertEquals("Mark", deserializedUser.getName());
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java
new file mode 100644
index 0000000000..7ef3f88511
--- /dev/null
+++ b/libraries-data-io/src/test/java/com/baeldung/serialization/protocols/YAMLSerializationUnitTest.java
@@ -0,0 +1,66 @@
+package com.baeldung.serialization.protocols;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import com.esotericsoftware.yamlbeans.YamlReader;
+import com.esotericsoftware.yamlbeans.YamlWriter;
+
+public class YAMLSerializationUnitTest {
+
+ @Test
+ public void whenUsingYAMLBeansForSerialization_thenDeserializeCorrectMap() throws IOException {
+
+ String filePath = "src/test/resources/protocols/yamlbeans_user.yaml";
+
+ YamlWriter writer = new YamlWriter(new FileWriter(filePath));
+ writer.write(populateUserMap());
+ writer.close();
+
+ YamlReader reader = new YamlReader(new FileReader(filePath));
+ Object object = reader.read();
+ reader.close();
+
+ assertTrue(object instanceof Map);
+ Map deserializedUsers = (Map) object;
+
+ assertEquals(4, deserializedUsers.size());
+ assertEquals("Mark Jonson", (deserializedUsers.get("User1").getName()));
+ assertEquals(1, (deserializedUsers.get("User1").getId()));
+ }
+
+ private Map populateUserMap() {
+
+ User user1 = new User();
+ user1.setId(1);
+ user1.setName("Mark Jonson");
+
+ User user2 = new User();
+ user2.setId(2);
+ user2.setName("Johny Beth");
+
+ User user3 = new User();
+ user3.setId(3);
+ user3.setName("Eliza Green");
+
+ User user4 = new User();
+ user4.setId(4);
+ user4.setName("Monica Doe");
+
+ Map users = new LinkedHashMap<>();
+ users.put("User1", user1);
+ users.put("User2", user2);
+ users.put("User3", user3);
+ users.put("User4", user4);
+
+ return users;
+ }
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/gson_user.json b/libraries-data-io/src/test/resources/protocols/gson_user.json
new file mode 100644
index 0000000000..5440a774f7
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/gson_user.json
@@ -0,0 +1,4 @@
+{
+ "id": 1,
+ "name": "Mark"
+}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/jackson_user.json b/libraries-data-io/src/test/resources/protocols/jackson_user.json
new file mode 100644
index 0000000000..ce10cca7ea
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/jackson_user.json
@@ -0,0 +1 @@
+{"id":1,"name":"Mark Jonson"}
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/user.proto b/libraries-data-io/src/test/resources/protocols/user.proto
new file mode 100644
index 0000000000..d4a231d6ec
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/user.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package protobuf;
+
+option java_package = "com.baeldung.serialization.protocols";
+option java_outer_classname = "UserProtos";
+
+message User {
+ int32 id = 1;
+ string name = 2;
+ }
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/user.txt b/libraries-data-io/src/test/resources/protocols/user.txt
new file mode 100644
index 0000000000..c9f53bfd49
Binary files /dev/null and b/libraries-data-io/src/test/resources/protocols/user.txt differ
diff --git a/libraries-data-io/src/test/resources/protocols/usersproto b/libraries-data-io/src/test/resources/protocols/usersproto
new file mode 100644
index 0000000000..83dd650be6
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/usersproto
@@ -0,0 +1 @@
+ John Doe
\ No newline at end of file
diff --git a/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml
new file mode 100644
index 0000000000..83726897b9
--- /dev/null
+++ b/libraries-data-io/src/test/resources/protocols/yamlbeans_user.yaml
@@ -0,0 +1,13 @@
+!java.util.LinkedHashMap
+User1: !com.baeldung.serialization.protocols.User
+ id: 1
+ name: Mark Jonson
+User2: !com.baeldung.serialization.protocols.User
+ id: 2
+ name: Johny Beth
+User3: !com.baeldung.serialization.protocols.User
+ id: 3
+ name: Eliza Green
+User4: !com.baeldung.serialization.protocols.User
+ id: 4
+ name: Monica Doe
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java
new file mode 100644
index 0000000000..3e0c47f793
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/Consts.java
@@ -0,0 +1,5 @@
+package com.baeldung.okhttp;
+
+public interface Consts {
+ int SSL_APPLICATION_PORT = 8443;
+}
\ No newline at end of file
diff --git a/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java
new file mode 100644
index 0000000000..3e7fad2a29
--- /dev/null
+++ b/libraries-http-2/src/test/java/com/baeldung/okhttp/ssl/OkHttpSSLSelfSignedCertLiveTest.java
@@ -0,0 +1,108 @@
+package com.baeldung.okhttp.ssl;
+
+import static com.baeldung.okhttp.Consts.SSL_APPLICATION_PORT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Execute spring-security-web-boot-2 module before running this live test
+ * @see com.baeldung.ssl.HttpsEnabledApplication
+ */
+public class OkHttpSSLSelfSignedCertLiveTest {
+
+ private final String HTTPS_WELCOME_URL = "https://localhost:" + SSL_APPLICATION_PORT + "/welcome";
+
+ private OkHttpClient.Builder builder;
+
+ @Before
+ public void init() {
+ builder = new OkHttpClient.Builder();
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void whenHTTPSSelfSignedCertGET_thenException() throws IOException {
+ builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ }
+
+ @Test(expected = SSLPeerUnverifiedException.class)
+ public void givenTrustAllCerts_whenHTTPSSelfSignedCertGET_thenException() throws GeneralSecurityException, IOException {
+ final TrustManager TRUST_ALL_CERTS = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[] {};
+ }
+ };
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom());
+ builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS);
+ builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ }
+
+ @Test
+ public void givenTrustAllCertsSkipHostnameVerification_whenHTTPSSelfSignedCertGET_then200OK() throws GeneralSecurityException, IOException {
+ final TrustManager TRUST_ALL_CERTS = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return new java.security.cert.X509Certificate[] {};
+ }
+ };
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TRUST_ALL_CERTS }, new java.security.SecureRandom());
+ builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) TRUST_ALL_CERTS);
+ builder.hostnameVerifier(new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ });
+ Response response = builder.build()
+ .newCall(new Request.Builder().url(HTTPS_WELCOME_URL)
+ .build())
+ .execute();
+ assertEquals(200, response.code());
+ assertNotNull(response.body());
+ assertEquals("Welcome to Secured Site
", response.body()
+ .string());
+ }
+}
diff --git a/maven-modules/maven-builder-plugin/README.md b/maven-modules/maven-builder-plugin/README.md
new file mode 100644
index 0000000000..47cd99d281
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Additional Source Directories in Maven](https://www.baeldung.com/maven-add-src-directories)
diff --git a/maven-modules/maven-builder-plugin/pom.xml b/maven-modules/maven-builder-plugin/pom.xml
new file mode 100644
index 0000000000..0eaa858db7
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ org.example
+ maven-builder-plugin
+ 1.0-SNAPSHOT
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.2.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ src/main/newsrc/
+
+
+
+
+
+
+
+
diff --git a/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java
new file mode 100644
index 0000000000..176951e21a
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/src/main/java/com/baeldung/maven/plugin/MainApp.java
@@ -0,0 +1,12 @@
+package com.baeldung.maven.plugin;
+
+import com.baeldung.database.DataConnection;
+
+public class MainApp {
+
+ public static void main(String args[]){
+
+ System.out.println(DataConnection.temp());
+
+ }
+}
diff --git a/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java
new file mode 100644
index 0000000000..8ab05a5658
--- /dev/null
+++ b/maven-modules/maven-builder-plugin/src/main/newsrc/com/baeldung/database/DataConnection.java
@@ -0,0 +1,7 @@
+package com.baeldung.database;
+public class DataConnection {
+
+ public static String temp(){
+ return "secondary source directory";
+ }
+}
diff --git a/maven-modules/maven-integration-test/README.md b/maven-modules/maven-integration-test/README.md
index e73a73e61e..708cb3bf23 100644
--- a/maven-modules/maven-integration-test/README.md
+++ b/maven-modules/maven-integration-test/README.md
@@ -7,4 +7,5 @@ This module contains articles about Integration Testing with Maven and related p
- [Integration Testing with Maven](https://www.baeldung.com/maven-integration-test)
- [Build a Jar with Maven and Ignore the Test Results](https://www.baeldung.com/maven-ignore-test-results)
- [Quick Guide to the Maven Surefire Plugin](https://www.baeldung.com/maven-surefire-plugin)
-- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin)
\ No newline at end of file
+- [The Maven Failsafe Plugin](https://www.baeldung.com/maven-failsafe-plugin)
+- [Difference Between Maven Surefire and Failsafe Plugins](https://www.baeldung.com/maven-surefire-vs-failsafe)
diff --git a/maven-modules/maven-integration-test/pom.xml b/maven-modules/maven-integration-test/pom.xml
index 4ab8de783d..692751366d 100644
--- a/maven-modules/maven-integration-test/pom.xml
+++ b/maven-modules/maven-integration-test/pom.xml
@@ -6,6 +6,7 @@
maven-integration-test
0.0.1-SNAPSHOT
maven-integration-test
+ war
com.baeldung
@@ -116,7 +117,9 @@
verify
-
+
+ **/*IntegrationTest
+
diff --git a/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java
new file mode 100644
index 0000000000..a50fa16394
--- /dev/null
+++ b/maven-modules/maven-integration-test/src/integration-test/java/com/baeldung/maven/it/FailsafeBuildPhaseIntegrationTest.java
@@ -0,0 +1,13 @@
+package com.baeldung.maven.it;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class FailsafeBuildPhaseIntegrationTest {
+
+ @Test
+ public void whenTestExecutes_thenPreAndPostIntegrationBuildPhasesAreExecuted() {
+ assertTrue(true);
+ }
+}
diff --git a/maven-modules/plugin-management/README.md b/maven-modules/plugin-management/README.md
new file mode 100644
index 0000000000..dec3a71cfd
--- /dev/null
+++ b/maven-modules/plugin-management/README.md
@@ -0,0 +1,3 @@
+## Relevant Articles:
+
+- [Plugin Management in Maven](https://www.baeldung.com/maven-plugin-management)
diff --git a/maven-modules/plugin-management/pom.xml b/maven-modules/plugin-management/pom.xml
new file mode 100644
index 0000000000..4a999a1aae
--- /dev/null
+++ b/maven-modules/plugin-management/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+ 0.0.1-SNAPSHOT
+
+ maven-modules
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+ plugin-management
+ pom
+
+
+ submodule-1
+ submodule-2
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${maven.bulid.helper.plugin}
+
+
+ add-resource
+ generate-resources
+
+ add-resource
+
+
+
+
+ src/resources
+ json
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin}
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+
+ 3.8.1
+ 3.2.0
+
+
+
\ No newline at end of file
diff --git a/maven-modules/plugin-management/submodule-1/pom.xml b/maven-modules/plugin-management/submodule-1/pom.xml
new file mode 100644
index 0000000000..915e4dfe60
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+ submodule-1
+
+
+ plugin-management
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+
+
+
diff --git a/maven-modules/plugin-management/submodule-1/src/resources/include.json b/maven-modules/plugin-management/submodule-1/src/resources/include.json
new file mode 100644
index 0000000000..d5df76e7e0
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/src/resources/include.json
@@ -0,0 +1,3 @@
+{
+ "key": "value"
+}
\ No newline at end of file
diff --git a/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java
new file mode 100644
index 0000000000..ab6b781790
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-1/src/test/java/com/baeldung/CopiesAdditionalResourcesUnitTest.java
@@ -0,0 +1,19 @@
+package com.baeldung;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CopiesAdditionalResourcesUnitTest {
+
+ @Test
+ void givenAdditionalResource_whenCopyingFromSourceToDestination_thenShouldBeInDestination() {
+ URL resource = getClass().getClassLoader().getResource("json/include.json");
+ File destinationFile = new File(resource.getFile());
+
+ assertTrue(destinationFile.exists());
+ }
+}
diff --git a/maven-modules/plugin-management/submodule-2/pom.xml b/maven-modules/plugin-management/submodule-2/pom.xml
new file mode 100644
index 0000000000..327bdcebb1
--- /dev/null
+++ b/maven-modules/plugin-management/submodule-2/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ submodule-2
+
+
+ plugin-management
+ com.baeldung
+ 0.0.1-SNAPSHOT
+
+
+
diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml
index 0f146e26da..0700c6e637 100644
--- a/maven-modules/pom.xml
+++ b/maven-modules/pom.xml
@@ -32,6 +32,8 @@
version-overriding-plugins
versions-maven-plugin
maven-printing-plugins
+ maven-builder-plugin
+ plugin-management
diff --git a/persistence-modules/hibernate-jpa/README.md b/persistence-modules/hibernate-jpa/README.md
index 64ec9dcae3..8379ad1626 100644
--- a/persistence-modules/hibernate-jpa/README.md
+++ b/persistence-modules/hibernate-jpa/README.md
@@ -13,3 +13,4 @@ This module contains articles specific to use of Hibernate as a JPA implementati
- [Enabling Transaction Locks in Spring Data JPA](https://www.baeldung.com/java-jpa-transaction-locks)
- [JPA/Hibernate Persistence Context](https://www.baeldung.com/jpa-hibernate-persistence-context)
- [Quick Guide to EntityManager#getReference()](https://www.baeldung.com/jpa-entity-manager-get-reference)
+- [JPA Entities and the Serializable Interface](https://www.baeldung.com/jpa-entities-serializable)
diff --git a/persistence-modules/java-cassandra/src/main/resources/data_types.cql b/persistence-modules/java-cassandra/src/main/resources/data_types.cql
new file mode 100644
index 0000000000..67e6fb4634
--- /dev/null
+++ b/persistence-modules/java-cassandra/src/main/resources/data_types.cql
@@ -0,0 +1,75 @@
+CREATE
+ KEYSPACE baeldung
+ WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
+USE baeldung;
+
+CREATE TABLE numeric_types
+(
+ type1 int PRIMARY KEY,
+ type2 bigint,
+ type3 smallint,
+ type4 tinyint,
+ type5 varint,
+ type6 float,
+ type7 double,
+ type8 decimal
+);
+
+CREATE TABLE text_types
+(
+ primaryKey int PRIMARY KEY,
+ type2 text,
+ type3 varchar,
+ type4 ascii
+);
+
+CREATE TABLE date_types
+(
+ primaryKey int PRIMARY KEY,
+ type1 timestamp,
+ type2 time,
+ type3 date,
+ type4 timeuuid,
+ type5 duration
+);
+
+CREATE TABLE other_types
+(
+ primaryKey int PRIMARY KEY,
+ type1 boolean,
+ type2 uuid,
+ type3 blob,
+ type4 inet
+);
+
+CREATE TABLE counter_type
+(
+ primaryKey uuid PRIMARY KEY,
+ type1 counter
+);
+
+CREATE TABLE collection_types
+(
+ primaryKey int PRIMARY KEY,
+ email set
+);
+
+ALTER TABLE collection_types
+ ADD scores list;
+
+ALTER TABLE collection_types
+ ADD address map;
+
+CREATE TABLE tuple_type
+(
+ primaryKey int PRIMARY KEY,
+ type1 tuple
+);
+
+CREATE TYPE user_defined_type ( type1 timestamp, type2 text, type3 text, type4 text);
+
+CREATE TABLE user_type
+(
+ primaryKey int PRIMARY KEY,
+ our_type user_defined_type
+);
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
index 18ef8d4e60..3829f676d3 100644
--- a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
@@ -1,5 +1,3 @@
-spring.main.allow-bean-definition-overriding=true
-
spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
index 29326c6061..32d3e640f9 100644
--- a/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-enterprise/src/main/resources/application.properties
@@ -10,7 +10,6 @@ spring.datasource.url=jdbc:h2:mem:baeldung
#spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true
-spring.main.allow-bean-definition-overriding=true
#hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.id.new_generator_mappings=false
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java
new file mode 100644
index 0000000000..c976590966
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-enterprise/src/test/java/com/baeldung/boot/daos/UserRepositoryTCJdbcLiveTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.boot.daos;
+
+import com.baeldung.boot.Application;
+import com.baeldung.boot.daos.user.UserRepository;
+import com.baeldung.boot.domain.User;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@ActiveProfiles("tc-jdbc")
+@SpringBootTest(classes = Application.class)
+public class UserRepositoryTCJdbcLiveTest {
+
+ final String USER_EMAIL = "email@example.com";
+ final String USER_EMAIL2 = "email2@example.com";
+ final String USER_EMAIL3 = "email3@example.com";
+ final String USER_EMAIL4 = "email4@example.com";
+ final Integer INACTIVE_STATUS = 0;
+ final Integer ACTIVE_STATUS = 1;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Test
+ @Transactional
+ public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers() {
+ userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE1", LocalDate.now(), USER_EMAIL2, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE", LocalDate.now(), USER_EMAIL3, ACTIVE_STATUS));
+ userRepository.save(new User("SAMPLE3", LocalDate.now(), USER_EMAIL4, ACTIVE_STATUS));
+ userRepository.flush();
+
+ int updatedUsersSize = userRepository.updateUserSetStatusForNameNativePostgres(INACTIVE_STATUS, "SAMPLE");
+
+ assertThat(updatedUsersSize).isEqualTo(2);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml
new file mode 100644
index 0000000000..ad5906fa6e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-enterprise/src/test/resources/application-tc-jdbc.yml
@@ -0,0 +1,6 @@
+spring:
+ datasource:
+ url: jdbc:tc:postgresql:11.1:///integration-tests-db
+ jpa:
+ hibernate:
+ ddl-auto: create
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index eac534697a..abdb851734 100644
--- a/pom.xml
+++ b/pom.xml
@@ -428,7 +428,6 @@
httpclient-simple
hystrix
- image-processing
immutables
jackson-modules
@@ -660,6 +659,7 @@
spring-mobile
spring-mockito
+ spring-native
spring-protobuf
spring-quartz
@@ -750,6 +750,8 @@
parent-spring-5
parent-java
+ image-processing
+
jenkins/plugins
jhipster
jws
@@ -894,7 +896,6 @@
httpclient-simple
hystrix
- image-processing
immutables
jackson-modules
@@ -1117,7 +1118,7 @@
spring-mobile
spring-mockito
-
+ spring-native
spring-protobuf
spring-quartz
@@ -1199,6 +1200,8 @@
parent-spring-5
parent-java
+ image-processing
+
jenkins/plugins
jhipster
jws
@@ -1283,14 +1286,17 @@
core-java-modules/core-java-9-streams
core-java-modules/core-java-10
+ core-java-modules/core-java-11
core-java-modules/core-java-11-2
+
core-java-modules/core-java-collections-set
core-java-modules/core-java-date-operations-1
core-java-modules/core-java-datetime-conversion
core-java-modules/core-java-datetime-string
+ core-java-modules/core-java-io-conversions-2
core-java-modules/core-java-jpms
core-java-modules/core-java-os
core-java-modules/core-java-string-operations-3
@@ -1333,10 +1339,12 @@
+
core-java-modules/core-java-collections-set
core-java-modules/core-java-date-operations-1
core-java-modules/core-java-datetime-conversion
core-java-modules/core-java-datetime-string
+ core-java-modules/core-java-io-conversions-2
core-java-modules/core-java-jpms
core-java-modules/core-java-os
core-java-modules/core-java-string-operations-3
diff --git a/rabbitmq/README.md b/rabbitmq/README.md
index 218b1a4b14..7fea2e85a0 100644
--- a/rabbitmq/README.md
+++ b/rabbitmq/README.md
@@ -4,4 +4,6 @@ This module contains articles about RabbitMQ.
### Relevant articles
- [Introduction to RabbitMQ](https://www.baeldung.com/rabbitmq)
+- [Exchanges, Queues, and Bindings in RabbitMQ](https://www.baeldung.com/java-rabbitmq-exchanges-queues-bindings)
+- [Pub-Sub vs. Message Queues](https://www.baeldung.com/pub-sub-vs-message-queues)
diff --git a/rabbitmq/pom.xml b/rabbitmq/pom.xml
index ed45029d3c..ae38d697f6 100644
--- a/rabbitmq/pom.xml
+++ b/rabbitmq/pom.xml
@@ -9,20 +9,42 @@
com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
com.rabbitmq
amqp-client
${amqp-client.version}
+
+ org.springframework.boot
+ spring-boot
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
5.12.0
+ 2020.0.3
\ No newline at end of file
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java
new file mode 100644
index 0000000000..e8acb90f00
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/ClientApplication.java
@@ -0,0 +1,43 @@
+package com.baeldung.pubsubmq.client;
+
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
+import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+
+@SpringBootApplication
+public class ClientApplication {
+ private static final String MESSAGE_QUEUE = "pizza-message-queue";
+
+ @Bean
+ public Queue queue() {
+ return new Queue(MESSAGE_QUEUE);
+ }
+
+ @Bean
+ public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
+ SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
+ container.setConnectionFactory(connectionFactory);
+ container.setQueueNames(MESSAGE_QUEUE);
+ container.setMessageListener(listenerAdapter);
+ return container;
+ }
+
+ @Bean
+ public Consumer consumer() {
+ return new Consumer();
+ }
+
+ @Bean
+ public MessageListenerAdapter listenerAdapter(Consumer consumer) {
+ return new MessageListenerAdapter(consumer, "receiveOrder");
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(ClientApplication.class, args);
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java
new file mode 100644
index 0000000000..fadc60572d
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/client/Consumer.java
@@ -0,0 +1,7 @@
+package com.baeldung.pubsubmq.client;
+
+public class Consumer {
+ public void receiveOrder(String message) {
+ System.out.printf("Order received: %s%n", message);
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java
new file mode 100644
index 0000000000..24c4be0a70
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/Publisher.java
@@ -0,0 +1,28 @@
+package com.baeldung.pubsubmq.server;
+
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+
+import javax.annotation.PostConstruct;
+
+public class Publisher {
+
+ private RabbitTemplate rabbitTemplate;
+ private String queue;
+ private String topic;
+
+ public Publisher(RabbitTemplate rabbitTemplate, String queue, String topic) {
+ this.rabbitTemplate = rabbitTemplate;
+ this.queue = queue;
+ this.topic = topic;
+ }
+
+ @PostConstruct
+ public void postMessages() {
+ rabbitTemplate.convertAndSend(queue, "1 Pepperoni");
+ rabbitTemplate.convertAndSend(queue, "3 Margarita");
+ rabbitTemplate.convertAndSend(queue, "1 Ham and Pineapple (yuck)");
+
+ rabbitTemplate.convertAndSend(topic, "notification", "New Deal on T-Shirts: 95% off!");
+ rabbitTemplate.convertAndSend(topic, "notification", "2 for 1 on all Jeans!");
+ }
+}
diff --git a/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java
new file mode 100644
index 0000000000..f2387f6ae5
--- /dev/null
+++ b/rabbitmq/src/main/java/com/baeldung/pubsubmq/server/ServerApplication.java
@@ -0,0 +1,57 @@
+package com.baeldung.pubsubmq.server;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.TopicExchange;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class ServerApplication {
+ private static final String MESSAGE_QUEUE = "pizza-message-queue";
+ private static final String PUB_SUB_TOPIC = "notification-topic";
+ private static final String PUB_SUB_EMAIL_QUEUE = "email-queue";
+ private static final String PUB_SUB_TEXT_QUEUE = "text-queue";
+
+ @Bean
+ public Queue queue() {
+ return new Queue(MESSAGE_QUEUE);
+ }
+
+ @Bean
+ public Queue emailQueue() {
+ return new Queue(PUB_SUB_EMAIL_QUEUE);
+ }
+
+ @Bean
+ public Queue textQueue() {
+ return new Queue(PUB_SUB_TEXT_QUEUE);
+ }
+
+ @Bean
+ public TopicExchange exchange() {
+ return new TopicExchange(PUB_SUB_TOPIC);
+ }
+
+ @Bean
+ public Binding emailBinding(Queue emailQueue, TopicExchange exchange) {
+ return BindingBuilder.bind(emailQueue).to(exchange).with("notification");
+ }
+
+ @Bean
+ public Binding textBinding(Queue textQueue, TopicExchange exchange) {
+ return BindingBuilder.bind(textQueue).to(exchange).with("notification");
+ }
+
+ @Bean
+ public Publisher publisher(RabbitTemplate rabbitTemplate) {
+ return new Publisher(rabbitTemplate, MESSAGE_QUEUE, PUB_SUB_TOPIC);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(ServerApplication.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties b/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties
deleted file mode 100644
index 709574239b..0000000000
--- a/spring-boot-modules/spring-boot-artifacts/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-cassandre/pom.xml b/spring-boot-modules/spring-boot-cassandre/pom.xml
index 75163d2c2b..c4672c8887 100644
--- a/spring-boot-modules/spring-boot-cassandre/pom.xml
+++ b/spring-boot-modules/spring-boot-cassandre/pom.xml
@@ -3,66 +3,50 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.4.5
-
-
- com.example
- demo
- 0.0.1-SNAPSHOT
- Cassandre trading bot tutorial
+ spring-boot-cassandre
+ jar
+ spring-boot-cassandre
Cassandre trading bot tutorial
-
- 11
-
-
-
- org.springframework.boot
- spring-boot-starter
-
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
tech.cassandre.trading.bot
cassandre-trading-bot-spring-boot-starter
- 4.2.1
+ ${cassandre.trading.bot.version}
org.knowm.xchange
xchange-kucoin
- 5.0.7
+ ${xchange-kucoin.version}
org.hsqldb
hsqldb
- 2.5.1
+ ${hsqldb.version}
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
tech.cassandre.trading.bot
cassandre-trading-bot-spring-boot-starter-test
- 4.2.1
+ ${cassandre.trading.bot.version}
test
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- 2.4.5
-
-
-
+
+ 11
+ 4.2.1
+ 5.0.7
+ 2.5.1
+
diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
similarity index 98%
rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java
rename to spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
index ea8ae74aa6..92ef379a66 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/MyFirstStrategy.java
+++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/MyFirstStrategy.java
@@ -1,4 +1,4 @@
-package com.example.demo;
+package com.baeldung.trading;
import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.CLOSED;
import static tech.cassandre.trading.bot.dto.position.PositionStatusDTO.OPENED;
diff --git a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java
similarity index 60%
rename from spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java
rename to spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java
index 094d95b93f..021c6c1d3b 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/main/java/com/example/demo/DemoApplication.java
+++ b/spring-boot-modules/spring-boot-cassandre/src/main/java/com/baeldung/trading/TradingBotApplication.java
@@ -1,13 +1,13 @@
-package com.example.demo;
+package com.baeldung.trading;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
-public class DemoApplication {
+public class TradingBotApplication {
public static void main(String[] args) {
- SpringApplication.run(DemoApplication.class, args);
+ SpringApplication.run(TradingBotApplication.class, args);
}
}
diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
similarity index 94%
rename from spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java
rename to spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
index bf7c353821..74986df971 100644
--- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/MyFirstStrategyUnitTest.java
+++ b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/baeldung/trading/MyFirstStrategyLiveTest.java
@@ -1,4 +1,4 @@
-package com.example.demo;
+package com.baeldung.trading;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -22,9 +22,9 @@ import tech.cassandre.trading.bot.test.mock.TickerFluxMock;
@SpringBootTest
@Import(TickerFluxMock.class)
@DisplayName("Simple strategy test")
-public class MyFirstStrategyUnitTest {
+public class MyFirstStrategyLiveTest {
- private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyUnitTest.class);
+ private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyLiveTest.class);
@Autowired
private MyFirstStrategy strategy;
diff --git a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java b/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java
deleted file mode 100644
index eaa99696e2..0000000000
--- a/spring-boot-modules/spring-boot-cassandre/src/test/java/com/example/demo/DemoApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.example.demo;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class DemoApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}
diff --git a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
index 6378a48506..969464a41c 100644
--- a/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-data/src/main/resources/application.properties
@@ -6,7 +6,6 @@ spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
-spring.main.allow-bean-definition-overriding=true
javers.mappingStyle=FIELD
javers.algorithm=SIMPLE
javers.commitIdGenerator=synchronized_sequence
diff --git a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
index 27b7915cff..2bf15543f0 100644
--- a/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-deployment/src/main/resources/application.properties
@@ -3,5 +3,4 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
index 3d6f37230c..4ffb414e92 100644
--- a/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-environment/src/main/resources/application.properties
@@ -3,6 +3,5 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.tomcat.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
spring.config.import=file:./additional.properties,optional:file:/Users/home/config/jdbc.properties
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/README.md b/spring-boot-modules/spring-boot-logging-log4j2/README.md
index aa6bb9b6e1..9688f8f83c 100644
--- a/spring-boot-modules/spring-boot-logging-log4j2/README.md
+++ b/spring-boot-modules/spring-boot-logging-log4j2/README.md
@@ -6,3 +6,4 @@ This module contains articles about logging in Spring Boot projects with Log4j 2
- [Logging in Spring Boot](https://www.baeldung.com/spring-boot-logging)
- [Logging to Graylog with Spring Boot](https://www.baeldung.com/graylog-with-spring-boot)
- [Log Groups in Spring Boot 2.1](https://www.baeldung.com/spring-boot-log-groups)
+- [Writing Log Data to Syslog Using Log4j2](https://www.baeldung.com/log4j-to-syslog)
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java
new file mode 100644
index 0000000000..56c3b62c5d
--- /dev/null
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/syslog/SpringBootSyslogApplication.java
@@ -0,0 +1,23 @@
+package com.baeldung.syslog;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringBootSyslogApplication {
+
+ private static final Logger logger = LogManager.getLogger(SpringBootSyslogApplication.class);
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootSyslogApplication.class, args);
+
+ logger.debug("Debug log message");
+ logger.info("Info log message");
+ logger.error("Error log message");
+ logger.warn("Warn log message");
+ logger.fatal("Fatal log message");
+ logger.trace("Trace log message");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
index b08cd2d22d..77a2074b30 100644
--- a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
+++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml
@@ -22,6 +22,10 @@
+
+
+
@@ -29,6 +33,7 @@
+
diff --git a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
index 7070d4c2f0..3ee7660e8b 100644
--- a/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-mvc-2/src/main/resources/application.properties
@@ -1,4 +1,3 @@
-spring.main.allow-bean-definition-overriding=true
spring.mvc.static-path-pattern=/content/**
spring.webflux.static-path-pattern=/content/**
spring.resources.static-locations=classpath:/files/,classpath:/static-files
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
index 6dab470c84..7f399bb11d 100644
--- a/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-mvc/src/main/resources/application.properties
@@ -1,2 +1 @@
-spring.main.allow-bean-definition-overriding=true
spring.thymeleaf.view-names=thymeleaf/*
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
index 27b7915cff..2bf15543f0 100644
--- a/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-runtime/src/main/resources/application.properties
@@ -3,5 +3,4 @@ management.metrics.enable.root=true
management.metrics.enable.jvm=true
management.endpoint.restart.enabled=true
spring.datasource.jmx-enabled=false
-spring.main.allow-bean-definition-overriding=true
management.endpoint.shutdown.enabled=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
index cf0f0ab74c..7bf9450088 100644
--- a/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
+++ b/spring-boot-modules/spring-boot-runtime/src/test/resources/application.properties
@@ -8,5 +8,4 @@ endpoints.shutdown.enabled=true
management.endpoint.restart.enabled=true
-spring.main.allow-bean-definition-overriding=true
spring.jmx.unique-names=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-springdoc/README.md b/spring-boot-modules/spring-boot-springdoc/README.md
index 608e4afa2e..4ac4147da6 100644
--- a/spring-boot-modules/spring-boot-springdoc/README.md
+++ b/spring-boot-modules/spring-boot-springdoc/README.md
@@ -3,3 +3,4 @@
- [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation)
- [Spring REST Docs vs OpenAPI](https://www.baeldung.com/spring-rest-docs-vs-openapi)
- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)
+- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated)
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java
new file mode 100644
index 0000000000..3141428514
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.tagopenapi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication()
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java
new file mode 100644
index 0000000000..780b202d3c
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/config/OpenApi.java
@@ -0,0 +1,20 @@
+package com.baeldung.tagopenapi.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+
+public class OpenApi {
+
+ @Bean
+ public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) {
+ return new OpenAPI().info(new Info().title("Controller API")
+ .version(appVersion)
+ .description("This is a sample server created using springdocs - a library for OpenAPI 3 with spring boot.")
+ .termsOfService("http://swagger.io/terms/")
+ .license(new License().name("Apache 2.0")
+ .url("http://springdoc.org")));
+ }
+}
diff --git a/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java
new file mode 100644
index 0000000000..3c81c1cee7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-springdoc/src/main/java/com/baeldung/tagopenapi/controller/BookController.java
@@ -0,0 +1,20 @@
+package com.baeldung.tagopenapi.controller;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/book")
+@Tag(name = "book service", description = "the book API with description tag annotation")
+public class BookController {
+
+ @GetMapping("/")
+ public List getBooks() {
+ return Arrays.asList("book1", "book2");
+ }
+}
diff --git a/spring-boot-modules/spring-boot-swagger/README.md b/spring-boot-modules/spring-boot-swagger/README.md
index 1038031210..f94ae75c41 100644
--- a/spring-boot-modules/spring-boot-swagger/README.md
+++ b/spring-boot-modules/spring-boot-swagger/README.md
@@ -1,3 +1,4 @@
## Relevant Articles:
- [Hiding Endpoints From Swagger Documentation in Spring Boot](https://www.baeldung.com/spring-swagger-hiding-endpoints)
+- [Swagger @Api Description Is Deprecated](https://www.baeldung.com/java-swagger-api-description-deprecated)
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java
new file mode 100644
index 0000000000..9232152b5b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.apiswagger;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication()
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java
new file mode 100644
index 0000000000..d9f51698c3
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/config/SwaggerConfiguration.java
@@ -0,0 +1,26 @@
+package com.baeldung.apiswagger.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Tag;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+@Configuration
+public class SwaggerConfiguration {
+
+ public static final String BOOK_TAG = "book service";
+
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any())
+ .build()
+ .tags(new Tag(BOOK_TAG, "the book API with description api tag"));
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java
new file mode 100644
index 0000000000..394c640751
--- /dev/null
+++ b/spring-boot-modules/spring-boot-swagger/src/main/java/com/baeldung/apiswagger/controller/BookController.java
@@ -0,0 +1,21 @@
+package com.baeldung.apiswagger.controller;
+
+import com.baeldung.apiswagger.config.SwaggerConfiguration;
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/book")
+@Api(tags = {SwaggerConfiguration.BOOK_TAG})
+public class BookController {
+
+ @GetMapping("/")
+ public List getBooks() {
+ return Arrays.asList("book1", "book2");
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
index daab3e8d2c..70cae370a4 100644
--- a/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-boot-testing/src/main/resources/application.properties
@@ -6,4 +6,3 @@ spring.redis.port= 6379
spring.security.user.name=john
spring.security.user.password=123
-spring.main.allow-bean-definition-overriding=true
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java
index 417e9a32b7..9d79e4e226 100644
--- a/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java
+++ b/spring-boot-modules/spring-boot-validation/src/main/java/com/baeldung/spring/servicevalidation/service/UserAccountService.java
@@ -31,11 +31,10 @@ public class UserAccountService {
sb.append(constraintViolation.getMessage());
}
- dao.addUserAccount(useraccount);
-
throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
}
+ dao.addUserAccount(useraccount);
return "Account for " + useraccount.getName() + " Added!";
}
diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java
new file mode 100644
index 0000000000..42bf50ac5f
--- /dev/null
+++ b/spring-boot-rest/src/main/java/com/baeldung/spring/ConverterExtensionsConfig.java
@@ -0,0 +1,29 @@
+package com.baeldung.spring;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
+import org.springframework.oxm.xstream.XStreamMarshaller;
+
+/**
+ * Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations.
+ *
+ * ATTENTION: Multiple converter registration of the same type most likely causes problem (serialize twice, etc.)
+ * Therefore, be sure to remove manually added XML message converter first then uncomment
+ * this @{@link org.springframework.context.annotation.Configuration} to use
+ */
+//@Configuration
+public class ConverterExtensionsConfig {
+
+ @Bean
+ public HttpMessageConverter