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 createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java index 13a9933fa6..69724fda29 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java +++ b/spring-boot-rest/src/main/java/com/baeldung/spring/WebConfig.java @@ -5,7 +5,6 @@ import java.util.List; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; @@ -16,35 +15,24 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { - // @Override - // public void configureMessageConverters(final List> messageConverters) { - // messageConverters.add(new MappingJackson2HttpMessageConverter()); - // messageConverters.add(createXmlHttpMessageConverter()); - // } - // - // private HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + @Override + public void configureMessageConverters(final List> messageConverters) { + messageConverters.add(new MappingJackson2HttpMessageConverter()); + messageConverters.add(createXmlHttpMessageConverter()); + } - // Another possibility is to create a bean which will be automatically added to the Spring Boot Autoconfigurations - // @Bean - // public HttpMessageConverter createXmlHttpMessageConverter() { - // final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); - // - // final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); - // xstreamMarshaller.setAutodetectAnnotations(true); - // xmlConverter.setMarshaller(xstreamMarshaller); - // xmlConverter.setUnmarshaller(xstreamMarshaller); - // - // return xmlConverter; - // } + /** + * There is another possibility to add a message converter, see {@link ConverterExtensionsConfig} + */ + private HttpMessageConverter createXmlHttpMessageConverter() { + final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter(); + + final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller(); + xmlConverter.setMarshaller(xstreamMarshaller); + xmlConverter.setUnmarshaller(xstreamMarshaller); + + return xmlConverter; + } // Etags @@ -52,16 +40,17 @@ public class WebConfig implements WebMvcConfigurer { // AbstractAnnotationConfigDispatcherServletInitializer#getServletFilters @Bean public FilterRegistrationBean shallowEtagHeaderFilter() { - FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( new ShallowEtagHeaderFilter()); + FilterRegistrationBean filterRegistrationBean = + new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); filterRegistrationBean.addUrlPatterns("/foos/*"); filterRegistrationBean.setName("etagFilter"); return filterRegistrationBean; } - + // We can also just declare the filter directly // @Bean // public ShallowEtagHeaderFilter shallowEtagHeaderFilter() { // return new ShallowEtagHeaderFilter(); // } -} \ No newline at end of file +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java b/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java deleted file mode 100644 index 9cba7f8fc1..0000000000 --- a/spring-boot-rest/src/test/java/com/baeldung/SpringContextTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {SpringBootRestApplication.class}) -public class SpringContextTest { - - @Test - public void contextLoads() { - } - -} diff --git a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java index ecf938be50..6e50f828de 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/common/web/AbstractBasicLiveTest.java @@ -24,6 +24,7 @@ import com.google.common.net.HttpHeaders; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; +import org.springframework.http.MediaType; public abstract class AbstractBasicLiveTest extends AbstractLiveTest { @@ -36,7 +37,7 @@ public abstract class AbstractBasicLiveTest extends Abst @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -54,7 +55,8 @@ public abstract class AbstractBasicLiveTest extends Abst public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE).get(getURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } @@ -64,7 +66,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response response = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); @@ -95,7 +97,7 @@ public abstract class AbstractBasicLiveTest extends Abst create(); create(); create(); - + final Response first = RestAssured.get(getURL() + "?page=0&size=2"); final String uriToLastPage = extractURIByRel(first.getHeader(HttpHeaders.LINK), "last"); @@ -104,7 +106,7 @@ public abstract class AbstractBasicLiveTest extends Abst final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next"); assertNull(uriToNextPage); } - + // etags @Test diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java index 3300b91fde..b1a84b47a7 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerAppIntegrationTest.java @@ -1,21 +1,22 @@ package com.baeldung.web; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.baeldung.persistence.dao.IFooDao; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - /** - * - * We'll start the whole context, but not the server. We'll mock the REST calls instead. - * + * We'll start the whole context, but not the server. We'll mock the REST calls instead. */ @RunWith(SpringRunner.class) @SpringBootTest @@ -25,12 +26,22 @@ public class FooControllerAppIntegrationTest { @Autowired private MockMvc mockMvc; + @Autowired + private IFooDao fooDao; + + @Before + public void setup() { + this.fooDao.deleteAll(); + } + @Test public void whenFindPaginatedRequest_thenEmptyResponse() throws Exception { - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(content().json("[]")); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().json("[]")); } } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java index 9e7b60ed8c..e472d308e8 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerCustomEtagIntegrationTest.java @@ -40,7 +40,7 @@ public class FooControllerCustomEtagIntegrationTest { private static String createFooJson() throws Exception { return serializeFoo(new Foo(randomAlphabetic(6))); } - + private static Foo deserializeFoo(String fooJson) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(fooJson, Foo.class); @@ -97,7 +97,8 @@ public class FooControllerCustomEtagIntegrationTest { .getResponse() .getHeader(HttpHeaders.LOCATION); ResultActions findOneResponse = this.mvc - .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX).contentType(MediaType.APPLICATION_JSON)); + .perform(get(createdResourceUri + CUSTOM_ETAG_ENDPOINT_SUFFIX) + .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)); String etag = findOneResponse.andReturn().getResponse().getHeader(HttpHeaders.ETAG); Foo createdFoo = deserializeFoo(findOneResponse.andReturn().getResponse().getContentAsString()); createdFoo.setName("updated name"); diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java index 4d4a274b1a..070625b7d4 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooControllerWebLayerIntegrationTest.java @@ -20,6 +20,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; +import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; @@ -30,7 +31,7 @@ import com.baeldung.web.exception.CustomException1; import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent; /** - * + * * We'll start only the web layer. * */ @@ -54,20 +55,22 @@ public class FooControllerWebLayerIntegrationTest { doNothing().when(publisher) .publishEvent(any(PaginatedResultsRetrievedEvent.class)); - this.mockMvc.perform(get("/foos").param("page", "0") - .param("size", "2")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$",Matchers.hasSize(1))); + this.mockMvc.perform(get("/foos") + .param("page", "0") + .param("size", "2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", Matchers.hasSize(1))); } - + @Test public void delete_forException_fromService() throws Exception { Mockito.when(service.findAll()).thenThrow(new CustomException1()); this.mockMvc.perform(get("/foos")).andDo(h -> { final Exception expectedException = h.getResolvedException(); Assert.assertTrue(expectedException instanceof CustomException1); - + }); } - + } diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java new file mode 100644 index 0000000000..9b1a9e9733 --- /dev/null +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooMessageConvertersLiveTest.java @@ -0,0 +1,149 @@ +package com.baeldung.web; + +import static com.baeldung.Consts.APPLICATION_PORT; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.baeldung.common.web.AbstractLiveTest; +import com.baeldung.persistence.model.Foo; +import com.baeldung.spring.ConfigIntegrationTest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.xstream.XStreamMarshaller; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.web.client.RestTemplate; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") +public class FooMessageConvertersLiveTest extends AbstractLiveTest { + + private static final String BASE_URI = "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/"; + + public FooMessageConvertersLiveTest() { + super(Foo.class); + } + + @Override + public final void create() { + create(new Foo(randomAlphabetic(6))); + } + + @Override + public final String createAsUri() { + return createAsUri(new Foo(randomAlphabetic(6))); + } + + @Before + public void setup(){ + create(); + } + + /** + * Without specifying Accept Header, uses the default response from the + * server (in this case json) + */ + @Test + public void whenRetrievingAFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + final Foo resource = restTemplate.getForObject(URI, Foo.class, "1"); + + assertThat(resource, notNullValue()); + } + + @Test + public void givenConsumingXml_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getXmlMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(headers); + + final ResponseEntity + response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getXmlMessageConverters() { + final XStreamMarshaller marshaller = new XStreamMarshaller(); + marshaller.setAnnotatedClasses(Foo.class); + final MarshallingHttpMessageConverter marshallingConverter = new MarshallingHttpMessageConverter(marshaller); + + final List> converters = new ArrayList<>(); + converters.add(marshallingConverter); + return converters; + } + + @Test + public void givenConsumingJson_whenReadingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos/{id}"; + + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonMessageConverters()); + + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + final HttpEntity entity = new HttpEntity(headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1"); + final Foo resource = response.getBody(); + + assertThat(resource, notNullValue()); + } + + private List> getJsonMessageConverters() { + final List> converters = new ArrayList<>(); + converters.add(new MappingJackson2HttpMessageConverter()); + return converters; + } + + @Test + public void givenConsumingXml_whenWritingTheFoo_thenCorrect() { + final String URI = BASE_URI + "foos"; + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setMessageConverters(getJsonAndXmlMessageConverters()); + + final Foo resource = new Foo("jason"); + final HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType((MediaType.APPLICATION_XML)); + final HttpEntity entity = new HttpEntity<>(resource, headers); + + final ResponseEntity response = restTemplate.exchange(URI, HttpMethod.POST, entity, Foo.class); + final Foo fooResponse = response.getBody(); + + assertThat(fooResponse, notNullValue()); + assertEquals(resource.getName(), fooResponse.getName()); + } + + private List> getJsonAndXmlMessageConverters() { + final List> converters = getJsonMessageConverters(); + converters.addAll(getXmlMessageConverters()); + return converters; + } + +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java index 6a365f3bd5..242fbb609e 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/FooPageableLiveTest.java @@ -11,6 +11,7 @@ import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -43,12 +44,12 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public final String createAsUri() { return createAsUri(new Foo(randomAlphabetic(6))); } - + @Override @Test public void whenResourcesAreRetrievedPaged_then200IsReceived() { this.create(); - + final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); assertThat(response.getStatusCode(), is(200)); @@ -68,13 +69,15 @@ public class FooPageableLiveTest extends AbstractBasicLiveTest { public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() { create(); - final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10"); + final Response response = RestAssured.given() + .accept(MediaType.APPLICATION_JSON_VALUE) + .get(getPageableURL() + "?page=0&size=10"); assertFalse(response.body().as(List.class).isEmpty()); } protected String getPageableURL() { - return "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/foos/pageable"; + return getURL() + "/pageable"; } - + } diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java new file mode 100644 index 0000000000..9416bd30f0 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/client/UserClient.java @@ -0,0 +1,13 @@ +package com.baeldung.cloud.openfeign.client; + +import com.baeldung.cloud.openfeign.config.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@FeignClient(name = "user-client", url="https://jsonplaceholder.typicode.com", configuration = FeignConfig.class) +public interface UserClient { + + @RequestMapping(value = "/users", method = RequestMethod.GET) + String getUsers(); +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java new file mode 100644 index 0000000000..d51e97b9e4 --- /dev/null +++ b/spring-cloud/spring-cloud-openfeign/src/main/java/com/baeldung/cloud/openfeign/config/FeignConfig.java @@ -0,0 +1,12 @@ +package com.baeldung.cloud.openfeign.config; + +import feign.Logger; +import org.springframework.context.annotation.Bean; + +public class FeignConfig { + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} \ No newline at end of file diff --git a/spring-kafka/README.md b/spring-kafka/README.md index ddb086c3bd..2b71beaac9 100644 --- a/spring-kafka/README.md +++ b/spring-kafka/README.md @@ -6,6 +6,8 @@ This module contains articles about Spring with Kafka - [Intro to Apache Kafka with Spring](https://www.baeldung.com/spring-kafka) - [Testing Kafka and Spring Boot](https://www.baeldung.com/spring-boot-kafka-testing) +- [Monitor the Consumer Lag in Apache Kafka](https://www.baeldung.com/java-kafka-consumer-lag) +- [Send Large Messages With Kafka](https://www.baeldung.com/java-kafka-send-large-message) ### Intro diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java new file mode 100644 index 0000000000..0af0a4b091 --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplicationLongMessage.java @@ -0,0 +1,75 @@ +package com.baeldung.spring.kafka; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@SpringBootApplication +public class KafkaApplicationLongMessage { + + public static void main(String[] args) throws Exception { + + ConfigurableApplicationContext context = SpringApplication.run(KafkaApplicationLongMessage.class, args); + + LongMessageProducer producer = context.getBean(LongMessageProducer.class); + + String fileData = readLongMessage(); + producer.sendMessage(fileData); + + //Deliberate delay to let listener consume produced message before main thread stops + Thread.sleep(5000); + context.close(); + } + + private static String readLongMessage() throws IOException { + String data = ""; + + //update complete location of large message here + data = new String(Files.readAllBytes(Paths.get("RandomTextFile.txt"))); + return data; + } + + @Bean + public LongMessageProducer longMessageProducer() { + return new LongMessageProducer(); + } + + @Bean + public LongMessageListener longMessageListener() { + return new LongMessageListener(); + } + + public static class LongMessageProducer { + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Value(value = "${long.message.topic.name}") + private String topicName; + + public void sendMessage(String message) { + kafkaTemplate.send(topicName, message); + System.out.println("Long message Sent"); + } + + } + + public static class LongMessageListener { + + @KafkaListener(topics = "${long.message.topic.name}", groupId = "longMessage", containerFactory = "longMessageKafkaListenerContainerFactory") + public void listenGroupLongMessage(String message) { + System.out.println("Received Message in group 'longMessage'"); + } + + } + +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java index abaa431eec..9495fcf508 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java @@ -27,6 +27,8 @@ public class KafkaConsumerConfig { props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "20971520"); + props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, "20971520"); return new DefaultKafkaConsumerFactory<>(props); } @@ -56,6 +58,11 @@ public class KafkaConsumerConfig { return kafkaListenerContainerFactory("partitions"); } + @Bean + public ConcurrentKafkaListenerContainerFactory longMessageKafkaListenerContainerFactory() { + return kafkaListenerContainerFactory("longMessage"); + } + @Bean public ConcurrentKafkaListenerContainerFactory filterKafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory factory = kafkaListenerContainerFactory("filter"); diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java index 0223bab0fe..9dff81a09d 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java @@ -25,6 +25,8 @@ public class KafkaProducerConfig { configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, "20971520"); + return new DefaultKafkaProducerFactory<>(configProps); } diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java index 00e4147cd0..8a006a72bc 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaTopicConfig.java @@ -19,6 +19,9 @@ public class KafkaTopicConfig { @Value(value = "${message.topic.name}") private String topicName; + @Value(value = "${long.message.topic.name}") + private String longMsgTopicName; + @Value(value = "${partitioned.topic.name}") private String partitionedTopicName; @@ -54,4 +57,13 @@ public class KafkaTopicConfig { public NewTopic topic4() { return new NewTopic(greetingTopicName, 1, (short) 1); } + + @Bean + public NewTopic topic5() { + NewTopic newTopic = new NewTopic(longMsgTopicName, 1, (short) 1); + Map configs = new HashMap<>(); + configs.put("max.message.bytes", "20971520"); + newTopic.configs(configs); + return newTopic; + } } diff --git a/spring-kafka/src/main/resources/application.properties b/spring-kafka/src/main/resources/application.properties index e6a4668da3..e1a983339b 100644 --- a/spring-kafka/src/main/resources/application.properties +++ b/spring-kafka/src/main/resources/application.properties @@ -1,5 +1,6 @@ kafka.bootstrapAddress=localhost:9092 message.topic.name=baeldung +long.message.topic.name=longMessage greeting.topic.name=greeting filtered.topic.name=filtered partitioned.topic.name=partitioned diff --git a/spring-native/README.md b/spring-native/README.md new file mode 100644 index 0000000000..0f193252d0 --- /dev/null +++ b/spring-native/README.md @@ -0,0 +1,3 @@ +## Relevant Articles: + +- [Introduction to Spring Native](https://www.baeldung.com/spring-native-intro) diff --git a/spring-native/pom-nativeimage.xml b/spring-native/pom-nativeimage.xml new file mode 100644 index 0000000000..1b2cc3944a --- /dev/null +++ b/spring-native/pom-nativeimage.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + baeldung-spring-native + baeldung-spring-native + jar + Intro to Spring Native + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + + + org.springframework.experimental + spring-native + ${spring-native.version} + + + org.springframework.experimental + spring-aot + ${spring-native.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.springframework.experimental + spring-aot-maven-plugin + ${spring-native.version} + + + test-generate + + test-generate + + + + generate + + generate + + + + + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native-maven-plugin.version} + + + build-native + + build + + package + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + + + + 2.5.1 + 0.10.0 + 0.9.0 + 1.8 + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/spring-native/pom.xml b/spring-native/pom.xml new file mode 100644 index 0000000000..1064800352 --- /dev/null +++ b/spring-native/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + baeldung-spring-native + baeldung-spring-native + jar + Intro to Spring Native + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + spring-release + Spring release + https://repo.spring.io/release + + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + + + org.springframework.experimental + spring-native + ${spring-native.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + ${builder} + + true + + IF_NOT_PRESENT + + + + + org.springframework.experimental + spring-aot-maven-plugin + ${spring-native.version} + + + + + + paketobuildpacks/builder:tiny + 2.5.1 + 0.10.0 + 1.8 + 1.8 + 1.8 + + + \ No newline at end of file diff --git a/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java new file mode 100644 index 0000000000..fa54d34f9f --- /dev/null +++ b/spring-native/src/main/java/com/baeldung/springnativeintro/SpringNativeApp.java @@ -0,0 +1,9 @@ +package com.baeldung.springnativeintro; + +public class SpringNativeApp { + + public static void main(String[] args) { + System.out.println("Hello, World! This is a Baledung Spring Native Application"); + } + +} diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index e482a67a0e..5847d6f28d 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -38,7 +38,7 @@ spring-security-web-mvc-custom spring-security-web-mvc spring-security-web-persistent-login - spring-security-web-react + spring-security-web-rest-basic-auth spring-security-web-rest-custom spring-security-web-rest @@ -49,4 +49,4 @@ spring-social-login - \ No newline at end of file + diff --git a/spring-security-modules/spring-security-core/src/main/resources/application.properties b/spring-security-modules/spring-security-core/src/main/resources/application.properties deleted file mode 100644 index 709574239b..0000000000 --- a/spring-security-modules/spring-security-core/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties index aca20f4e3c..fcdaabe007 100644 --- a/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties +++ b/spring-web-modules/spring-mvc-basics-3/src/main/resources/application.properties @@ -1,5 +1,3 @@ -spring.main.allow-bean-definition-overriding=true - spring.mail.host=localhost spring.mail.port=8025 diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java new file mode 100644 index 0000000000..fbf78b8a0e --- /dev/null +++ b/spring-web-modules/spring-mvc-basics-4/src/main/java/com/baeldung/controller/controller/GreetingsController.java @@ -0,0 +1,49 @@ +package com.baeldung.controller.controller; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class GreetingsController { + + @RequestMapping( + value = "/greetings-with-response-body", + method = RequestMethod.GET, + produces="application/json" + ) + @ResponseBody + public String getGreetingWhileReturnTypeIsString() { + return "{\"test\": \"Hello using @ResponseBody\"}"; + } + + @RequestMapping( + value = "/greetings-with-response-entity", + method = RequestMethod.GET, + produces = "application/json" + ) + public ResponseEntity getGreetingWithResponseEntity() { + final HttpHeaders httpHeaders= new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + return new ResponseEntity("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK); + } + @RequestMapping( + value = "/greetings-with-map-return-type", + method = RequestMethod.GET, + produces = "application/json" + ) + @ResponseBody + public Map getGreetingWhileReturnTypeIsMap() { + HashMap map = new HashMap(); + map.put("test", "Hello from map"); + return map; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties b/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties deleted file mode 100644 index 709574239b..0000000000 --- a/spring-web-modules/spring-mvc-basics-4/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java new file mode 100644 index 0000000000..ee9a8da8d4 --- /dev/null +++ b/spring-web-modules/spring-mvc-basics-4/src/test/java/com/baeldung/controller/GreetingsControllerUnitTest.java @@ -0,0 +1,58 @@ +package com.baeldung.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.baeldung.controller.controller.GreetingsController; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.AnnotationConfigWebContextLoader; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { GreetingsController.class }, loader = AnnotationConfigWebContextLoader.class) +public class GreetingsControllerUnitTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext wac; + + @Before + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() throws Exception { + + // Given + String expectedMimeType = "application/json"; + + // Then + String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1)).andReturn().getResponse().getContentType(); + + Assert.assertEquals(expectedMimeType, actualMimeType); + + } + + @Test + public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception { + + // Given + String expectedMimeType = "application/json"; + + // Then + String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1)).andReturn().getResponse().getContentType(); + + Assert.assertEquals(expectedMimeType, actualMimeType); + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java index 9b01328e45..efab434e31 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java +++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/Article.java @@ -1,4 +1,4 @@ -package main.java.com.baeldung.thymeleaf.articles; +package com.baeldung.thymeleaf.articles; public class Article { diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java index cfbf0fcaa6..e3972ffb51 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java +++ b/spring-web-modules/spring-thymeleaf-3/src/main/java/com/baeldung/thymeleaf/articles/ArticlesController.java @@ -1,4 +1,4 @@ -package main.java.com.baeldung.thymeleaf.articles; +package com.baeldung.thymeleaf.articles; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; diff --git a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties index 75770808da..730dded1b7 100644 --- a/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties +++ b/spring-web-modules/spring-thymeleaf-3/src/main/resources/application.properties @@ -2,5 +2,4 @@ spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.main.allow-bean-definition-overriding=true \ No newline at end of file +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect \ No newline at end of file diff --git a/testing-modules/junit-5/README.md b/testing-modules/junit-5/README.md index 984b79c29d..5101f92928 100644 --- a/testing-modules/junit-5/README.md +++ b/testing-modules/junit-5/README.md @@ -8,3 +8,4 @@ - [Guide to Dynamic Tests in JUnit 5](https://www.baeldung.com/junit5-dynamic-tests) - [Determine the Execution Time of JUnit Tests](https://www.baeldung.com/junit-test-execution-time) - [@BeforeAll and @AfterAll in Non-Static Methods](https://www.baeldung.com/java-beforeall-afterall-non-static) +- [The java.lang.NoClassDefFoundError in JUnit](https://www.baeldung.com/junit-noclassdeffounderror) diff --git a/testing-modules/selenium-junit-testng/README.md b/testing-modules/selenium-junit-testng/README.md index aa3f0e8005..6ae5e9c9ef 100644 --- a/testing-modules/selenium-junit-testng/README.md +++ b/testing-modules/selenium-junit-testng/README.md @@ -5,3 +5,4 @@ - [Using Cookies With Selenium WebDriver in Java](https://www.baeldung.com/java-selenium-webdriver-cookies) - [Clicking Elements in Selenium using JavaScript](https://www.baeldung.com/java-selenium-javascript) - [Taking Screenshots With Selenium WebDriver](https://www.baeldung.com/java-selenium-screenshots) +- [Running Selenium Scripts with JMeter](https://www.baeldung.com/selenium-jmeter) diff --git a/vaadin/src/main/resources/application.properties b/vaadin/src/main/resources/application.properties deleted file mode 100644 index 1cb7086b82..0000000000 --- a/vaadin/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Vaadin supports spring-boot 2.1 properly from V8 onwards (according to this comment https://github.com/vaadin/spring/issues/331#issuecomment-435128475) -spring.main.allow-bean-definition-overriding=true \ No newline at end of file