diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/rotatearray/RotateArray.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/rotatearray/RotateArray.java new file mode 100644 index 0000000000..36490d4899 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/rotatearray/RotateArray.java @@ -0,0 +1,102 @@ +package com.baeldung.algorithms.rotatearray; + +/** + * To speed up the rotation, we narrow k rotations to the remainder of k divided by the array length, or k module the array length. + * Therefore, a large rotation number will be translated into the relative smallest rotation. + * All solutions replace the original array, although they might use an extra array to compute the rotation. + */ +public class RotateArray { + + private RotateArray() { + throw new IllegalStateException("Rotate array algorithm utility methods class"); + } + + /** + * + * @param arr array to apply rotation to + * @param k number of rotations + */ + public static void bruteForce(int[] arr, int k) { + checkInvalidInput(arr, k); + + k %= arr.length; + int temp; + int previous; + for (int i = 0; i < k; i++) { + previous = arr[arr.length - 1]; + for (int j = 0; j < arr.length; j++) { + temp = arr[j]; + arr[j] = previous; + previous = temp; + } + } + } + + /** + * + * @param arr array to apply rotation to + * @param k number of rotations + */ + public static void withExtraArray(int[] arr, int k) { + checkInvalidInput(arr, k); + + int[] extraArray = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + extraArray[(i + k) % arr.length] = arr[i]; + } + System.arraycopy(extraArray, 0, arr, 0, arr.length); + } + + /** + * + * @param arr array to apply rotation to + * @param k number of rotations + */ + public static void cyclicReplacement(int[] arr, int k) { + checkInvalidInput(arr, k); + + k = k % arr.length; + int count = 0; + for (int start = 0; count < arr.length; start++) { + int current = start; + int prev = arr[start]; + do { + int next = (current + k) % arr.length; + int temp = arr[next]; + arr[next] = prev; + prev = temp; + current = next; + count++; + } while (start != current); + } + } + + /** + * + * @param arr array to apply rotation to + * @param k number of rotations + */ + public static void reverse(int[] arr, int k) { + checkInvalidInput(arr, k); + + k %= arr.length; + reverse(arr, 0, arr.length - 1); + reverse(arr, 0, k - 1); + reverse(arr, k, arr.length - 1); + } + + private static void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + + private static void checkInvalidInput(int[] arr, int rotation) { + if (rotation < 1 || arr == null) + throw new IllegalArgumentException("Rotation must be greater than zero or array must be not null"); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/rotatearray/RotateArrayUnitTest.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/rotatearray/RotateArrayUnitTest.java new file mode 100644 index 0000000000..4a98acdb75 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/rotatearray/RotateArrayUnitTest.java @@ -0,0 +1,124 @@ +package com.baeldung.algorithms.rotatearray; + +import static com.baeldung.algorithms.rotatearray.RotateArray.bruteForce; +import static com.baeldung.algorithms.rotatearray.RotateArray.cyclicReplacement; +import static com.baeldung.algorithms.rotatearray.RotateArray.reverse; +import static com.baeldung.algorithms.rotatearray.RotateArray.withExtraArray; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class RotateArrayUnitTest { + + private final int[] arr = { 1, 2, 3, 4, 5, 6 }; + private final int rotationLtArrayLength = 1; + private final int rotationGtArrayLength = arr.length + 2; + private final int[] ltArrayLengthRotation = { 6, 1, 2, 3, 4, 5 }; + private final int[] gtArrayLengthRotation = { 5, 6, 1, 2, 3, 4 }; + + @Test + void givenInputArray_whenNoRotationOrEmptyArray_thenThrowIllegalArgumentException() { + final int noRotation = 0; + final int someRotation = arr.length - 1; + + assertThrows(IllegalArgumentException.class, () -> bruteForce(arr, noRotation)); + assertThrows(IllegalArgumentException.class, () -> withExtraArray(arr, noRotation)); + assertThrows(IllegalArgumentException.class, () -> cyclicReplacement(arr, noRotation)); + assertThrows(IllegalArgumentException.class, () -> reverse(arr, noRotation)); + + assertThrows(IllegalArgumentException.class, () -> bruteForce(null, someRotation)); + assertThrows(IllegalArgumentException.class, () -> withExtraArray(null, someRotation)); + assertThrows(IllegalArgumentException.class, () -> cyclicReplacement(null, someRotation)); + assertThrows(IllegalArgumentException.class, () -> reverse(null, someRotation)); + } + + @Test + void givenInputArray_whenUseBruteForceRotationLtArrayLength_thenRotateArrayOk() { + + bruteForce(arr, rotationLtArrayLength); + assertArrayEquals(ltArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseBruteForceRotationGtArrayLength_thenRotateArrayOk() { + + bruteForce(arr, rotationGtArrayLength); + assertArrayEquals(gtArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseBruteForceRotationEqArrayLength_thenDoNothing() { + int[] expected = arr.clone(); + + bruteForce(arr, arr.length); + assertArrayEquals(expected, arr); + } + + @Test + void givenInputArray_whenUseExtraArrayRotationLtArrayLength_thenRotateArrayOk() { + + withExtraArray(arr, rotationLtArrayLength); + assertArrayEquals(ltArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseExtraArrayRotationGtArrayLength_thenRotateArrayOk() { + + withExtraArray(arr, rotationGtArrayLength); + assertArrayEquals(gtArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseExtraArrayWithRotationEqArrayLength_thenDoNothing() { + int[] clone = arr.clone(); + + withExtraArray(arr, arr.length); + assertArrayEquals(clone, arr); + } + + @Test + void givenInputArray_whenUseCyclicReplacementRotationLtArrayLength_thenRotateArrayOk() { + + cyclicReplacement(arr, rotationLtArrayLength); + assertArrayEquals(ltArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseCyclicReplacementRotationGtArrayLength_thenRotateArrayOk() { + + cyclicReplacement(arr, rotationGtArrayLength); + assertArrayEquals(gtArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseCyclicReplacementRotationEqArrayLength_thenDoNothing() { + int[] clone = arr.clone(); + + cyclicReplacement(arr, arr.length); + assertArrayEquals(clone, arr); + } + + @Test + void givenInputArray_whenUseReverseRotationLtArrayLength_thenRotateArrayOk() { + + reverse(arr, rotationLtArrayLength); + assertArrayEquals(ltArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseReverseRotationGtArrayLength_thenRotateArrayOk() { + + reverse(arr, rotationGtArrayLength); + assertArrayEquals(gtArrayLengthRotation, arr); + } + + @Test + void givenInputArray_whenUseReverseRotationEqArrayLength_thenDoNothing() { + + int[] clone = arr.clone(); + + reverse(arr, arr.length); + assertArrayEquals(clone, arr); + } +} diff --git a/apache-kafka-2/README.md b/apache-kafka-2/README.md index dc675a0811..40ee701be1 100644 --- a/apache-kafka-2/README.md +++ b/apache-kafka-2/README.md @@ -13,3 +13,4 @@ You can build the project from the command line using: *mvn clean install*, or i - [Read Data From the Beginning Using Kafka Consumer API](https://www.baeldung.com/java-kafka-consumer-api-read) - [Get Partition Count for a Topic in Kafka](https://www.baeldung.com/java-kafka-partition-count-topic) - [bootstrap-server in Kafka Configuration](https://www.baeldung.com/java-kafka-bootstrap-server) +- [Introduction to Apache Kafka](https://www.baeldung.com/apache-kafka) diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/multipletopics/MultipleTopicsLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/multipletopics/MultipleTopicsLiveTest.java new file mode 100644 index 0000000000..653456a678 --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/multipletopics/MultipleTopicsLiveTest.java @@ -0,0 +1,103 @@ +package com.baeldung.kafka.multipletopics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +// This live test needs a Docker Daemon running so that a kafka container can be created + +@Testcontainers +public class MultipleTopicsLiveTest { + + private final Logger log = LoggerFactory.getLogger(MultipleTopicsLiveTest.class); + + private static final String CARD_PAYMENTS_TOPIC = "card-payments"; + private static final String BANK_TRANSFERS_TOPIC = "bank-transfers"; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() { + KAFKA_CONTAINER.addExposedPort(9092); + producer = new KafkaProducer<>(getProducerProperties()); + consumer = new KafkaConsumer<>(getConsumerProperties()); + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + private static Properties getProducerProperties() { + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + return producerProperties; + } + + private static Properties getConsumerProperties() { + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "payments"); + return consumerProperties; + } + + @Test + void whenSendingMessagesOnTwoTopics_thenConsumerReceivesMessages() throws Exception { + publishMessages(); + + consumer.subscribe(Arrays.asList(CARD_PAYMENTS_TOPIC, BANK_TRANSFERS_TOPIC)); + + int eventsProcessed = 0; + for (ConsumerRecord record : consumer.poll(Duration.ofSeconds(10))) { + log.info("Event on topic={}, payload={}", record.topic(), record.value()); + eventsProcessed++; + } + + assertThat(eventsProcessed).isEqualTo(2); + } + + private void publishMessages() throws ExecutionException, InterruptedException { + ProducerRecord cardPayment = new ProducerRecord<>(CARD_PAYMENTS_TOPIC, createCardPayment()); + producer.send(cardPayment).get(); + + ProducerRecord bankTransfer = new ProducerRecord<>(BANK_TRANSFERS_TOPIC, createBankTransfer()); + producer.send(bankTransfer).get(); + } + + private String createCardPayment() { + return "{\"paymentReference\":\"A184028KM0013790\", \"type\":\"card\", \"amount\":\"275\", \"currency\":\"GBP\"}"; + } + + private String createBankTransfer() { + return "{\"paymentReference\":\"19ae2-18mk73-009\", \"type\":\"bank\", \"amount\":\"150\", \"currency\":\"EUR\"}"; + } +} diff --git a/core-java-modules/core-java-16/src/main/java/com/baeldung/value_based_class/Point.java b/core-java-modules/core-java-16/src/main/java/com/baeldung/value_based_class/Point.java index 13e2238274..a3055985af 100644 --- a/core-java-modules/core-java-16/src/main/java/com/baeldung/value_based_class/Point.java +++ b/core-java-modules/core-java-16/src/main/java/com/baeldung/value_based_class/Point.java @@ -30,6 +30,11 @@ public final class Point { return new Point(x, y, z); } + @Override + public String toString() { + return "Point{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; + } + @Override public boolean equals(Object other) { if (other == null || getClass() != other.getClass()) diff --git a/core-java-modules/core-java-16/src/test/java/com/baeldung/value_based_class/ValueBasedClassUnitTest.java b/core-java-modules/core-java-16/src/test/java/com/baeldung/value_based_class/ValueBasedClassUnitTest.java index 781f368982..fe031a652f 100644 --- a/core-java-modules/core-java-16/src/test/java/com/baeldung/value_based_class/ValueBasedClassUnitTest.java +++ b/core-java-modules/core-java-16/src/test/java/com/baeldung/value_based_class/ValueBasedClassUnitTest.java @@ -1,14 +1,17 @@ package com.baeldung.value_based_class; +import java.util.ArrayList; +import java.util.List; + import org.junit.Assert; import org.junit.Test; public class ValueBasedClassUnitTest { @Test public void givenAutoboxedAndPrimitive_whenCompared_thenReturnEquals() { - int primitive_a = 125; - Integer obj_a = 125; // this is autoboxed - Assert.assertSame(primitive_a, obj_a); + List list = new ArrayList<>(); + list.add(1); // this is autoboxed + Assert.assertEquals(list.get(0), Integer.valueOf(1)); } @Test diff --git a/core-java-modules/core-java-18/README.md b/core-java-modules/core-java-18/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core-java-modules/core-java-18/pom.xml b/core-java-modules/core-java-18/pom.xml new file mode 100644 index 0000000000..7af6258497 --- /dev/null +++ b/core-java-modules/core-java-18/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + core-java-18 + core-java-18 + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../../ + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.release} + --enable-preview + ${maven.compiler.source.version} + ${maven.compiler.target.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.plugin.version} + + 1 + + + + org.apache.maven.surefire + surefire-api + ${surefire.plugin.version} + + + + + + + + 18 + 18 + 18 + 3.0.0-M5 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/FinalizationExamples.java b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/FinalizationExamples.java new file mode 100644 index 0000000000..3ff77c37d6 --- /dev/null +++ b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/FinalizationExamples.java @@ -0,0 +1,27 @@ +package com.baeldung.finalization_closeable_cleaner; + +import java.io.FileInputStream; +import java.io.IOException; + +public class FinalizationExamples { + FileInputStream fis = null; + + public void readFileOperationWithFinalization() throws IOException { + try { + fis = new FileInputStream("input.txt"); + // perform operation on the file + System.out.println(fis.readAllBytes().length); + + } finally { + if (fis != null) + fis.close(); + } + } + + public void readFileOperationWithTryWith() throws IOException { + try (FileInputStream fis = new FileInputStream("input.txt")) { + // perform operations + System.out.println(fis.readAllBytes().length); + } + } +} diff --git a/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCleanerResourceClass.java b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCleanerResourceClass.java new file mode 100644 index 0000000000..1dcdd216cb --- /dev/null +++ b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCleanerResourceClass.java @@ -0,0 +1,48 @@ +package com.baeldung.finalization_closeable_cleaner; + +import java.lang.ref.Cleaner; + +public class MyCleanerResourceClass implements AutoCloseable { + private static Resource resource; + + private static final Cleaner cleaner = Cleaner.create(); + private final Cleaner.Cleanable cleanable; + + public MyCleanerResourceClass() { + resource = new Resource(); + this.cleanable = cleaner.register(this, new CleaningState()); + } + + public void useResource() { + // using the resource here + resource.use(); + } + + @Override + public void close() { + // perform actions to close all underlying resources + this.cleanable.clean(); + } + + static class CleaningState implements Runnable { + CleaningState() { + // constructor + } + + @Override + public void run() { + // some cleanup action + System.out.println("Cleanup done"); + } + } + + static class Resource { + void use() { + System.out.println("Using the resource"); + } + + void close() { + System.out.println("Cleanup done"); + } + } +} diff --git a/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCloseableResourceClass.java b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCloseableResourceClass.java new file mode 100644 index 0000000000..b11cb4e49e --- /dev/null +++ b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyCloseableResourceClass.java @@ -0,0 +1,25 @@ +package com.baeldung.finalization_closeable_cleaner; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class MyCloseableResourceClass implements AutoCloseable { + + private final FileInputStream fis; + + public MyCloseableResourceClass() throws FileNotFoundException { + this.fis = new FileInputStream("src/main/resources/file.txt"); + + } + + public int getByteLength() throws IOException { + System.out.println("Some operation"); + return this.fis.readAllBytes().length; + } + @Override + public void close() throws IOException { + System.out.println("Finalized object"); + this.fis.close(); + } +} diff --git a/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyFinalizableResourceClass.java b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyFinalizableResourceClass.java new file mode 100644 index 0000000000..a2c6a123b4 --- /dev/null +++ b/core-java-modules/core-java-18/src/main/java/com/baeldung/finalization_closeable_cleaner/MyFinalizableResourceClass.java @@ -0,0 +1,24 @@ +package com.baeldung.finalization_closeable_cleaner; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class MyFinalizableResourceClass { + private FileInputStream fis; + + public MyFinalizableResourceClass() throws FileNotFoundException { + this.fis = new FileInputStream("src/main/resources/file.txt"); + } + + public int getByteLength() throws IOException { + System.out.println("Some operation"); + return this.fis.readAllBytes().length; + } + + @Override + protected void finalize() throws Throwable { + System.out.println("Finalized object"); + this.fis.close(); + } +} diff --git a/core-java-modules/core-java-18/src/main/resources/file.txt b/core-java-modules/core-java-18/src/main/resources/file.txt new file mode 100644 index 0000000000..af27ff4986 --- /dev/null +++ b/core-java-modules/core-java-18/src/main/resources/file.txt @@ -0,0 +1 @@ +This is a test file. \ No newline at end of file diff --git a/core-java-modules/core-java-18/src/test/java/com/baeldung/finalization_closeable_cleaner/FinalizationCloseableCleanerUnitTest.java b/core-java-modules/core-java-18/src/test/java/com/baeldung/finalization_closeable_cleaner/FinalizationCloseableCleanerUnitTest.java new file mode 100644 index 0000000000..22ff228227 --- /dev/null +++ b/core-java-modules/core-java-18/src/test/java/com/baeldung/finalization_closeable_cleaner/FinalizationCloseableCleanerUnitTest.java @@ -0,0 +1,36 @@ +package com.baeldung.finalization_closeable_cleaner; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +public class FinalizationCloseableCleanerUnitTest { + + @Test + public void givenMyFinalizationResource_whenUsingFinalize_thenShouldClean() { + assertDoesNotThrow(() -> { + MyFinalizableResourceClass mfr = new MyFinalizableResourceClass(); + mfr.getByteLength(); + }); + } + @Test + public void givenMyCleanerResource_whenUsingCleanerAPI_thenShouldClean() { + assertDoesNotThrow(() -> { + try (MyCleanerResourceClass myCleanerResourceClass = new MyCleanerResourceClass()) { + myCleanerResourceClass.useResource(); + } + }); + } + + @Test + public void givenCloseableResource_whenUsingTryWith_thenShouldClose() throws IOException { + int length = 0; + try (MyCloseableResourceClass mcr = new MyCloseableResourceClass()) { + length = mcr.getByteLength(); + } + Assert.assertEquals(20, length); + } +} diff --git a/core-java-modules/core-java-21/README.md b/core-java-modules/core-java-21/README.md index 2c51db275a..ffb999a4ba 100644 --- a/core-java-modules/core-java-21/README.md +++ b/core-java-modules/core-java-21/README.md @@ -1,3 +1,5 @@ ## Relevant Articles - [Sequenced Collections in Java 21](https://www.baeldung.com/java-21-sequenced-collections) - [String Templates in Java 21](https://www.baeldung.com/java-21-string-templates) +- [Unnamed Classes and Instance Main Methods in Java 21](https://www.baeldung.com/java-21-unnamed-class-instance-main) +- [Unnamed Patterns and Variables in Java 21](https://www.baeldung.com/java-unnamed-patterns-variables) diff --git a/core-java-modules/core-java-arrays-convert/src/main/java/com/baeldung/array/conversions/CharArrayToIntArrayUtils.java b/core-java-modules/core-java-arrays-convert/src/main/java/com/baeldung/array/conversions/CharArrayToIntArrayUtils.java new file mode 100644 index 0000000000..fbd76a0113 --- /dev/null +++ b/core-java-modules/core-java-arrays-convert/src/main/java/com/baeldung/array/conversions/CharArrayToIntArrayUtils.java @@ -0,0 +1,67 @@ +package com.baeldung.array.conversions; + +import java.util.Arrays; + +public class CharArrayToIntArrayUtils { + + static int[] usingGetNumericValueMethod(char[] chars) { + if (chars == null) { + return null; + } + + int[] ints = new int[chars.length]; + for (int i = 0; i < chars.length; i++) { + ints[i] = Character.getNumericValue(chars[i]); + } + + return ints; + } + + static int[] usingDigitMethod(char[] chars) { + if (chars == null) { + return null; + } + + int[] ints = new int[chars.length]; + for (int i = 0; i < chars.length; i++) { + ints[i] = Character.digit(chars[i], 10); + } + + return ints; + } + + static int[] usingStreamApiMethod(char[] chars) { + if (chars == null) { + return null; + } + + return new String(chars).chars() + .map(c -> c - 48) + .toArray(); + } + + static int[] usingParseIntMethod(char[] chars) { + if (chars == null) { + return null; + } + + int[] ints = new int[chars.length]; + for (int i = 0; i < chars.length; i++) { + ints[i] = Integer.parseInt(String.valueOf(chars[i])); + } + + return ints; + } + + static int[] usingArraysSetAllMethod(char[] chars) { + if (chars == null) { + return null; + } + + int[] ints = new int[chars.length]; + Arrays.setAll(ints, i -> Character.getNumericValue(chars[i])); + + return ints; + } + +} diff --git a/core-java-modules/core-java-arrays-convert/src/test/java/com/baeldung/array/conversions/CharArrayToIntArrayUtilsUnitTest.java b/core-java-modules/core-java-arrays-convert/src/test/java/com/baeldung/array/conversions/CharArrayToIntArrayUtilsUnitTest.java new file mode 100644 index 0000000000..534dc3893f --- /dev/null +++ b/core-java-modules/core-java-arrays-convert/src/test/java/com/baeldung/array/conversions/CharArrayToIntArrayUtilsUnitTest.java @@ -0,0 +1,54 @@ +package com.baeldung.array.conversions; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class CharArrayToIntArrayUtilsUnitTest { + + @Test + void givenCharArray_whenUsingGetNumericValueMethod_shouldGetIntArray() { + int[] expected = { 2, 3, 4, 5 }; + char[] chars = { '2', '3', '4', '5' }; + int[] result = CharArrayToIntArrayUtils.usingGetNumericValueMethod(chars); + + assertArrayEquals(expected, result); + } + + @Test + void givenCharArray_whenUsingDigitMethod_shouldGetIntArray() { + int[] expected = { 1, 2, 3, 6 }; + char[] chars = { '1', '2', '3', '6' }; + int[] result = CharArrayToIntArrayUtils.usingDigitMethod(chars); + + assertArrayEquals(expected, result); + } + + @Test + void givenCharArray_whenUsingStreamApi_shouldGetIntArray() { + int[] expected = { 9, 8, 7, 6 }; + char[] chars = { '9', '8', '7', '6' }; + int[] result = CharArrayToIntArrayUtils.usingStreamApiMethod(chars); + + assertArrayEquals(expected, result); + } + + @Test + void givenCharArray_whenUsingParseIntMethod_shouldGetIntArray() { + int[] expected = { 9, 8, 7, 6 }; + char[] chars = { '9', '8', '7', '6' }; + int[] result = CharArrayToIntArrayUtils.usingParseIntMethod(chars); + + assertArrayEquals(expected, result); + } + + @Test + void givenCharArray_whenUsingArraysSetAllMethod_shouldGetIntArray() { + int[] expected = { 4, 9, 2, 3 }; + char[] chars = { '4', '9', '2', '3' }; + int[] result = CharArrayToIntArrayUtils.usingArraysSetAllMethod(chars); + + assertArrayEquals(expected, result); + } + +} diff --git a/core-java-modules/core-java-char/README.md b/core-java-modules/core-java-char/README.md index e4af3121c5..56040a3ea5 100644 --- a/core-java-modules/core-java-char/README.md +++ b/core-java-modules/core-java-char/README.md @@ -5,3 +5,4 @@ This module contains articles about Java Character Class ### Relevant Articles: - [Character#isAlphabetic vs. Character#isLetter](https://www.baeldung.com/java-character-isletter-isalphabetic) - [Difference Between Java’s “char” and “String”](https://www.baeldung.com/java-char-vs-string) +- [Increment Character in Java](https://www.baeldung.com/java-char-sequence) diff --git a/core-java-modules/core-java-collections-5/README.md b/core-java-modules/core-java-collections-5/README.md index 1769d11686..e478d87ad0 100644 --- a/core-java-modules/core-java-collections-5/README.md +++ b/core-java-modules/core-java-collections-5/README.md @@ -5,4 +5,6 @@ ### Relevant Articles: - [Introduction to Roaring Bitmap](https://www.baeldung.com/java-roaring-bitmap-intro) - [Creating Custom Iterator in Java](https://www.baeldung.com/java-creating-custom-iterator) +- [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods) +- [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration) - More articles: [[<-- prev]](/core-java-modules/core-java-collections-4) diff --git a/core-java-modules/core-java-collections-array-list-2/README.md b/core-java-modules/core-java-collections-array-list-2/README.md index dbf5c47edb..575e0dbb07 100644 --- a/core-java-modules/core-java-collections-array-list-2/README.md +++ b/core-java-modules/core-java-collections-array-list-2/README.md @@ -3,4 +3,4 @@ This module contains articles about the Java ArrayList collection ### Relevant Articles: -- [Create an ArrayList with Multiple Object Types](https://www.baeldung.com/arraylist-with-multiple-object-types) +- [Create an ArrayList with Multiple Object Types](https://www.baeldung.com/java-arraylist-multiple-object-types) diff --git a/core-java-modules/core-java-collections-conversions-3/README.md b/core-java-modules/core-java-collections-conversions-3/README.md index f89cd199ff..653f518840 100644 --- a/core-java-modules/core-java-collections-conversions-3/README.md +++ b/core-java-modules/core-java-collections-conversions-3/README.md @@ -3,3 +3,4 @@ This module contains articles about conversions among Collection types in Java. ### Relevant Articles: +- [Converting HashMap Values to an ArrayList in Java](https://www.baeldung.com/java-hashmap-arraylist) diff --git a/core-java-modules/core-java-collections-maps-7/README.md b/core-java-modules/core-java-collections-maps-7/README.md new file mode 100644 index 0000000000..c63f3b360b --- /dev/null +++ b/core-java-modules/core-java-collections-maps-7/README.md @@ -0,0 +1 @@ +## Relevant Articles \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-7/pom.xml b/core-java-modules/core-java-collections-maps-7/pom.xml new file mode 100644 index 0000000000..bb7c6e9fb5 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-7/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + core-java-collections-maps-7 + core-java-collections-maps-7 + jar + + + core-java-modules + com.baeldung.core-java-modules + 0.0.1-SNAPSHOT + + + + 5.2.5.RELEASE + + + + com.fasterxml.jackson.core + jackson-databind + 2.12.4 + + + org.openjdk.jmh + jmh-core + 1.36 + + + com.google.code.gson + gson + 2.8.9 + + + org.json + json + 20230227 + + + junit + junit + 4.13.1 + test + + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + + junit + junit + 4.13.1 + test + + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + + junit + junit + 4.13.1 + test + + + + \ No newline at end of file diff --git a/core-java-modules/core-java-collections-maps-7/src/main/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoString.java b/core-java-modules/core-java-collections-maps-7/src/main/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoString.java new file mode 100644 index 0000000000..2b3d6db89c --- /dev/null +++ b/core-java-modules/core-java-collections-maps-7/src/main/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoString.java @@ -0,0 +1,60 @@ +package com.baeldung.map; + +import java.util.HashMap; +import java.util.Map; + +public class ConvertHashMapStringToHashMapObjectUsingtoString { + public String name; + public int age; + + public ConvertHashMapStringToHashMapObjectUsingtoString(String name, int age) { + this.name = name; + this.age = age; + } + + public static ConvertHashMapStringToHashMapObjectUsingtoString deserializeCustomObject(String valueString) { + if (valueString.startsWith("{") && valueString.endsWith("}")) { + valueString = valueString.substring(1, valueString.length() - 1); + String[] parts = valueString.split(","); + String name = null; + int age = -1; + for (String part : parts) { + String[] keyValue = part.split("="); + if (keyValue.length == 2) { + String key = keyValue[0].trim(); + String val = keyValue[1].trim(); + if (key.equals("name")) { + name = val; + } else if (key.equals("age")) { + age = Integer.parseInt(val); + } + } + } + if (name != null && age >= 0) { + return new ConvertHashMapStringToHashMapObjectUsingtoString(name, age); + } + } + return new ConvertHashMapStringToHashMapObjectUsingtoString("", -1); + } + + public static void main(String[] args) { + String hashMapString = "{key1={name=John, age=30}, key2={name=Alice, age=25}}"; + String keyValuePairs = hashMapString.replaceAll("[{}\\s]", ""); + String[] pairs = keyValuePairs.split(","); + Map actualHashMap = new HashMap<>(); + for (String pair : pairs) { + String[] keyValue = pair.split("="); + if (keyValue.length == 2) { + String key = keyValue[0]; + ConvertHashMapStringToHashMapObjectUsingtoString value = deserializeCustomObject(keyValue[1]); + actualHashMap.put(key, value); + } + } + System.out.println(actualHashMap); + } + + @Override + public String toString() { + return "{name=" + name + ", age=" + age + "}"; + } +} diff --git a/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoStringUnitTest.java b/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoStringUnitTest.java new file mode 100644 index 0000000000..7d31402131 --- /dev/null +++ b/core-java-modules/core-java-collections-maps-7/src/test/java/com/baeldung/map/ConvertHashMapStringToHashMapObjectUsingtoStringUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.map; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ConvertHashMapStringToHashMapObjectUsingtoStringUnitTest { + + @Test + void givenValidCustomObject_whenSerializing_thenSerializedStringIsCorrect() { + ConvertHashMapStringToHashMapObjectUsingtoString customObject = new ConvertHashMapStringToHashMapObjectUsingtoString("John", 30); + String expectedSerializedString = "{name=John, age=30}"; + assertEquals(expectedSerializedString, customObject.toString()); + } + + @Test + void givenValidSerializedString_whenDeserializing_thenCustomObjectIsCorrect() { + String serializedString = "{name=Alice, age=25}"; + ConvertHashMapStringToHashMapObjectUsingtoString customObject = ConvertHashMapStringToHashMapObjectUsingtoString.deserializeCustomObject(serializedString); + assertEquals("Alice", customObject.name); + assertEquals(25, customObject.age); + } + + @Test + void givenInvalidSerializedString_whenDeserializing_thenDefaultCustomObjectIsCreated() { + String invalidSerializedString = "{invalidString}"; + ConvertHashMapStringToHashMapObjectUsingtoString customObject = ConvertHashMapStringToHashMapObjectUsingtoString.deserializeCustomObject(invalidSerializedString); + assertEquals("", customObject.name); + assertEquals(-1, customObject.age); + } +} diff --git a/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java b/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java index 6e3b250938..80c403bb4e 100644 --- a/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java +++ b/core-java-modules/core-java-datetime-conversion/src/test/java/com/baeldung/epochtolocaldate/EpochTimeToLocalDateTimeConverterUnitTest.java @@ -12,10 +12,10 @@ public class EpochTimeToLocalDateTimeConverterUnitTest { @Test public void testConvertEpochTimeToLocalDateTime() { long epochTimeMillis = 1624962431000L; // Example epoch time in milliseconds - LocalDateTime expectedDateTime = LocalDateTime.of(2021, 6, 29, 12, 13, 51); + LocalDateTime expectedDateTime = LocalDateTime.of(2021, 6, 29, 10, 27, 11); Instant instant = Instant.ofEpochMilli(epochTimeMillis); - ZoneId zoneId = ZoneId.systemDefault(); + ZoneId zoneId = ZoneId.of("UTC"); LocalDateTime actualDateTime = instant.atZone(zoneId).toLocalDateTime(); assertEquals(expectedDateTime, actualDateTime); diff --git a/core-java-modules/core-java-io-5/README.md b/core-java-modules/core-java-io-5/README.md index 6072e0b6d9..9fb4b967a4 100644 --- a/core-java-modules/core-java-io-5/README.md +++ b/core-java-modules/core-java-io-5/README.md @@ -3,6 +3,6 @@ This module contains articles about core Java input and output (IO) ### Relevant Articles: - +- [Get File Extension From MIME Type in Java](https://www.baeldung.com/java-mime-type-file-extension) - [[<-- Prev]](/core-java-modules/core-java-io-4) diff --git a/core-java-modules/core-java-io-5/src/test/java/com/baeldung/rmlinebreaks/RemoveLinebreaksUnitTest.java b/core-java-modules/core-java-io-5/src/test/java/com/baeldung/rmlinebreaks/RemoveLinebreaksUnitTest.java new file mode 100644 index 0000000000..7cfc7ede79 --- /dev/null +++ b/core-java-modules/core-java-io-5/src/test/java/com/baeldung/rmlinebreaks/RemoveLinebreaksUnitTest.java @@ -0,0 +1,72 @@ +package com.baeldung.rmlinebreaks; + +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +public class RemoveLinebreaksUnitTest { + + private Path file1Path() throws Exception { + return Paths.get(this.getClass().getClassLoader().getResource("multiple-line-1.txt").toURI()); + } + + private Path file2Path() throws Exception { + return Paths.get(this.getClass().getClassLoader().getResource("multiple-line-2.txt").toURI()); + } + + @Test + void whenRemovingLineSeparatorFromFile1_thenGetTheExpectedResult() throws Exception { + String content = Files.readString(file1Path(), StandardCharsets.UTF_8); + + String result = content.replace(System.getProperty("line.separator"), ""); + assertEquals("A, B, C, D, E, F", result); + } + + @Test + void whenRemovingLineSeparatorFromFile2_thenNotGetTheExpectedResult() throws Exception { + String content = Files.readString(file2Path(), StandardCharsets.UTF_8); + + String result = content.replace(System.getProperty("line.separator"), ""); + assertNotEquals("A, B, C, D, E, F", result); // <-- NOT equals assertion! + } + + @Test + void whenRemovingAllLinebreaks_thenGetTheExpectedResult() throws Exception { + String content1 = Files.readString(file1Path(), StandardCharsets.UTF_8); + + // file contains CRLF + String content2 = Files.readString(file2Path(), StandardCharsets.UTF_8); + + String result1 = content1.replace("\r", "").replace("\n", ""); + String result2 = content2.replace("\r", "").replace("\n", ""); + + assertEquals("A, B, C, D, E, F", result1); + assertEquals("A, B, C, D, E, F", result2); + + String resultReplaceAll = content2.replaceAll("[\\n\\r]", ""); + assertEquals("A, B, C, D, E, F", resultReplaceAll); + + } + + @Test + void whenUsingReadAllLinesAndJoin_thenGetExpectedResult() throws Exception { + List lines1 = Files.readAllLines(file1Path(), StandardCharsets.UTF_8); + + // file contains CRLF + List lines2 = Files.readAllLines(file2Path(), StandardCharsets.UTF_8); + + String result1 = String.join("", lines1); + String result2 = String.join("", lines2); + + assertEquals("A, B, C, D, E, F", result1); + assertEquals("A, B, C, D, E, F", result2); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-io-5/src/test/resources/multiple-line-1.txt b/core-java-modules/core-java-io-5/src/test/resources/multiple-line-1.txt new file mode 100644 index 0000000000..42e87e734e --- /dev/null +++ b/core-java-modules/core-java-io-5/src/test/resources/multiple-line-1.txt @@ -0,0 +1,6 @@ +A, + B, + C, + D, + E, + F diff --git a/core-java-modules/core-java-io-5/src/test/resources/multiple-line-2.txt b/core-java-modules/core-java-io-5/src/test/resources/multiple-line-2.txt new file mode 100644 index 0000000000..4fe5d5f4ed --- /dev/null +++ b/core-java-modules/core-java-io-5/src/test/resources/multiple-line-2.txt @@ -0,0 +1,4 @@ +A, B, + C, + D, E, + F diff --git a/core-java-modules/core-java-networking/src/test/java/com/baeldung/networking/url/UrlUnitTest.java b/core-java-modules/core-java-networking/src/test/java/com/baeldung/networking/url/UrlUnitTest.java index 87d9d7a620..ca04263689 100644 --- a/core-java-modules/core-java-networking/src/test/java/com/baeldung/networking/url/UrlUnitTest.java +++ b/core-java-modules/core-java-networking/src/test/java/com/baeldung/networking/url/UrlUnitTest.java @@ -2,10 +2,15 @@ package com.baeldung.networking.url; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Map; import org.apache.http.client.utils.URIBuilder; @@ -149,4 +154,24 @@ public class UrlUnitTest { assertEquals("http://baeldung.com:9090/articles?topic=java&version=8", url.toString()); } + @Test + public void givenURI_whenConvertingToURL_thenCorrect() throws IOException, URISyntaxException { + String aURIString = "http://courses.baeldung.com"; + URI uri = new URI(aURIString); + URL url = uri.toURL(); + assertNotNull(url); + assertEquals(aURIString, url.toString()); + } + + @Test + public void givenPath_whenConvertingToURIAndThenURL_thenCorrect() throws IOException, URISyntaxException { + String finalPath = "file:/D:/baeldung/java-url"; + Path path = Paths.get("/baeldung/java-url"); + URI uri = path.toUri(); + URL url = uri.toURL(); + assertNotNull(url); + // Adapt the finalPath value to match your own path + // assertEquals(finalPath, url.toString()); + } + } \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-3/src/test/java/com/baeldung/integerToBinary/IntegerToBinaryUnitTest.java b/core-java-modules/core-java-numbers-3/src/test/java/com/baeldung/integerToBinary/IntegerToBinaryUnitTest.java index 38ae79f2f5..9439f012e3 100644 --- a/core-java-modules/core-java-numbers-3/src/test/java/com/baeldung/integerToBinary/IntegerToBinaryUnitTest.java +++ b/core-java-modules/core-java-numbers-3/src/test/java/com/baeldung/integerToBinary/IntegerToBinaryUnitTest.java @@ -1,6 +1,8 @@ package com.baeldung.integerToBinary; +import org.apache.commons.lang3.StringUtils; import org.junit.Test; + import static org.junit.Assert.assertEquals; public class IntegerToBinaryUnitTest { @@ -24,4 +26,18 @@ public class IntegerToBinaryUnitTest { String binaryString = Integer.toString(n, 2); assertEquals("111", binaryString); } -} + + @Test + public void givenAnInteger_whenFormatAndReplaceCalled_thenZeroPaddedBinaryString() { + int n = 7; + String binaryString = String.format("%8s", Integer.toBinaryString(n)).replace(" ", "0"); + assertEquals("00000111", binaryString); + } + + @Test + public void givenAnInteger_whenUsingApacheStringUtils_thenZeroPaddedBinaryString() { + int n = 7; + String binaryString = StringUtils.leftPad(Integer.toBinaryString(n), 8, "0"); + assertEquals("00000111", binaryString); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-6/pom.xml b/core-java-modules/core-java-numbers-6/pom.xml index 531f1293d1..7a3b3d4426 100644 --- a/core-java-modules/core-java-numbers-6/pom.xml +++ b/core-java-modules/core-java-numbers-6/pom.xml @@ -25,8 +25,12 @@ ${commons-codec} test + + com.google.guava + guava + ${guava.version} + - core-java-numbers-6 @@ -39,5 +43,6 @@ 1.15 + 32.1.2-jre \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java new file mode 100644 index 0000000000..2ac57a9c0c --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BenchmarkRunner.java @@ -0,0 +1,8 @@ +package com.baeldung.bigintegerroot; + +public class BenchmarkRunner { + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(args); + } +} + diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java new file mode 100644 index 0000000000..c2d2f30827 --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerHolder.java @@ -0,0 +1,10 @@ +package com.baeldung.bigintegerroot; + +public class BigIntegerHolder { + + private BigIntegerHolder() { + } + public static final String BIG_NUMBER = "179769313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + public static final String VERY_BIG_NUMBER = "32473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834"; + public static final String INSANELY_BIG_NUMBER = "3247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834"; +} diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java new file mode 100644 index 0000000000..645e4eb2dd --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/BigIntegerSquareRootBenchmark.java @@ -0,0 +1,60 @@ +package com.baeldung.bigintegerroot; + +import static com.baeldung.bigintegerroot.BigIntegerHolder.*; + +import com.baeldung.bigintegerroot.algorithms.Newton; +import com.baeldung.bigintegerroot.algorithms.NewtonPlus; +import com.google.common.math.BigIntegerMath; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@Warmup(iterations = 1) +@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.MINUTES) +@Fork(1) +@State(Scope.Benchmark) +public class BigIntegerSquareRootBenchmark { + + @Param({BIG_NUMBER, VERY_BIG_NUMBER, INSANELY_BIG_NUMBER}) + public String number; + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void calculateRootWithJava(Blackhole blackhole) { + final BigInteger integer = new BigInteger(number); + final BigInteger root = integer.sqrt(); + blackhole.consume(root); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void calculateRootWithGuava(Blackhole blackhole) { + final BigInteger integer = new BigInteger(number); + final BigInteger root = BigIntegerMath.sqrt(integer, RoundingMode.DOWN); + blackhole.consume(root); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void calculateRootWithNewtonPlus(Blackhole blackhole) { + final BigInteger integer = new BigInteger(number); + final BigInteger root = NewtonPlus.sqrt(integer); + blackhole.consume(root); + } + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void calculateRootWithNewton(Blackhole blackhole) { + final BigInteger integer = new BigInteger(number); + final BigInteger root = Newton.sqrt(integer); + blackhole.consume(root); + } +} diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java new file mode 100644 index 0000000000..07acba7537 --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/Newton.java @@ -0,0 +1,29 @@ +package com.baeldung.bigintegerroot.algorithms; + +import java.math.BigInteger; + +public class Newton { + + private Newton() { + } + + public static BigInteger sqrt(BigInteger n) { + // Initial approximation + BigInteger x = n.divide(BigInteger.TWO); + + // Tolerance level (small positive integer) + BigInteger tolerance = BigInteger.ONE; + + while (true) { + // x_new = 0.5 * (x + n / x) + BigInteger xNew = x.add(n.divide(x)).divide(BigInteger.TWO); + + // Check for convergence within tolerance + if (x.subtract(xNew).abs().compareTo(tolerance) <= 0) { + return xNew; + } + + x = xNew; + } + } +} diff --git a/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java new file mode 100644 index 0000000000..80d50c0ca5 --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/main/java/com/baeldung/bigintegerroot/algorithms/NewtonPlus.java @@ -0,0 +1,108 @@ +package com.baeldung.bigintegerroot.algorithms; + +import java.math.BigInteger; + +public class NewtonPlus { + + private NewtonPlus() { + } + + // A fast square root by Ryan Scott White. + public static BigInteger sqrt(BigInteger x) { + if (x.compareTo(BigInteger.valueOf(144838757784765629L)) < 0) { + long xAsLong = x.longValue(); + long vInt = (long)Math.sqrt(xAsLong); + if (vInt * vInt > xAsLong) + vInt--; + return BigInteger.valueOf(vInt); } + + double xAsDub = x.doubleValue(); + BigInteger val; + if (xAsDub < 2.1267e37) // 2.12e37 largest here + // since sqrt(long.max*long.max) > long.max + { + long vInt = (long)Math.sqrt(xAsDub); + val = BigInteger.valueOf + ((vInt + x.divide(BigInteger.valueOf(vInt)).longValue()) >> 1); + } + else if (xAsDub < 4.3322e127) { + // Convert a double to a BigInteger + long bits = Double.doubleToLongBits(Math.sqrt(xAsDub)); + int exp = ((int) (bits >> 52) & 0x7ff) - 1075; + val = BigInteger.valueOf((bits & ((1L << 52)) - 1) | (1L << 52)).shiftLeft(exp); + + val = x.divide(val).add(val).shiftRight(1); + if (xAsDub > 2e63) { + val = x.divide(val).add(val).shiftRight(1); } + } + else // handle large numbers over 4.3322e127 + { + int xLen = x.bitLength(); + int wantedPrecision = ((xLen + 1) / 2); + int xLenMod = xLen + (xLen & 1) + 1; + + //////// Do the first Sqrt on Hardware //////// + long tempX = x.shiftRight(xLenMod - 63).longValue(); + double tempSqrt1 = Math.sqrt(tempX); + long valLong = Double.doubleToLongBits(tempSqrt1) & 0x1fffffffffffffL; + + if (valLong == 0) + valLong = 1L << 53; + + //////// Classic Newton Iterations //////// + val = BigInteger.valueOf(valLong).shiftLeft(53 - 1) + .add((x.shiftRight(xLenMod - + (3 * 53))).divide(BigInteger.valueOf(valLong))); + + int size = 106; + for (; size < 256; size <<= 1) { + val = val.shiftLeft(size - 1).add(x.shiftRight + (xLenMod - (3*size)).divide(val));} + + if (xAsDub > 4e254) { // 4e254 = 1<<845.77 + int numOfNewtonSteps = 31 - + Integer.numberOfLeadingZeros(wantedPrecision / size)+1; + + ////// Apply Starting Size //////// + int wantedSize = (wantedPrecision >> numOfNewtonSteps) + 2; + int needToShiftBy = size - wantedSize; + val = val.shiftRight(needToShiftBy); + + size = wantedSize; + do { + //////// Newton Plus Iteration //////// + int shiftX = xLenMod - (3 * size); + BigInteger valSqrd = val.multiply(val).shiftLeft(size - 1); + BigInteger valSU = x.shiftRight(shiftX).subtract(valSqrd); + val = val.shiftLeft(size).add(valSU.divide(val)); + size *= 2; + } while (size < wantedPrecision); + } + val = val.shiftRight(size - wantedPrecision); + } + + // Detect a round ups. This function can be further optimized - see article. + // For a ~7% speed bump the following line can be removed but round-ups will occur. + if (val.multiply(val).compareTo(x) > 0) + val = val.subtract(BigInteger.ONE); + + // Enabling the below will guarantee an error is stopped for larger numbers. + // Note: As of this writing, there are no known errors. + BigInteger tmp = val.multiply(val); + if (tmp.compareTo(x) > 0) { + System.out.println("val^2(" + val.multiply(val).toString() + + ") ≥ x(" + x.toString()+")"); + System.console().readLine(); + //throw new Exception("Sqrt function had internal error - value too high"); + } + if (tmp.add(val.shiftLeft(1)).add(BigInteger.ONE).compareTo(x) <= 0) { + System.out.println("(val+1)^2(" + + val.add(BigInteger.ONE).multiply(val.add(BigInteger.ONE)).toString() + + ") ≥ x(" + x.toString() + ")"); + System.console().readLine(); + //throw new Exception("Sqrt function had internal error - value too low"); + } + + return val; + } +} diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java new file mode 100644 index 0000000000..edb75b16ef --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/bigintegerroot/BigIntegerSquareRootUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.bigintegerroot; + + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.baeldung.bigintegerroot.algorithms.Newton; +import com.baeldung.bigintegerroot.algorithms.NewtonPlus; +import com.google.common.math.BigIntegerMath; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.stream.Stream; +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BigIntegerSquareRootUnitTest { + + @ParameterizedTest + @ValueSource(strings = { + BigIntegerHolder.BIG_NUMBER, + BigIntegerHolder.VERY_BIG_NUMBER, + BigIntegerHolder.VERY_BIG_NUMBER + }) + void squareRootTest(String number) { + final BigInteger bigInteger = new BigInteger(number); + final BigInteger javaRoot = bigInteger.sqrt(); + final BigInteger guavaRoot = BigIntegerMath.sqrt(bigInteger, RoundingMode.DOWN); + final BigInteger newtonRoot = Newton.sqrt(bigInteger); + final BigInteger newtonPlusRoot = NewtonPlus.sqrt(bigInteger); + + assertTrue(Stream.of( + new Pair<>(javaRoot, guavaRoot), + new Pair<>(guavaRoot, newtonRoot), + new Pair<>(newtonRoot, newtonPlusRoot) + ).allMatch(pair -> pair.getFirst().equals(pair.getSecond()))); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalConversionUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalConversionUnitTest.java new file mode 100644 index 0000000000..76c218d85f --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalConversionUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.javadoublevsbigdecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import org.junit.jupiter.api.Test; + +public class BigDecimalConversionUnitTest { + + @Test + void whenConvertingDoubleToBigDecimal_thenConversionIsCorrect() { + double doubleValue = 123.456; + BigDecimal bigDecimalValue = BigDecimal.valueOf(doubleValue); + BigDecimal expected = new BigDecimal("123.456").setScale(3, RoundingMode.HALF_UP); + assertEquals(expected, bigDecimalValue.setScale(3, RoundingMode.HALF_UP)); + } + + @Test + void givenDecimalPlacesGreaterThan15_whenConvertingBigDecimalToDouble_thenPrecisionIsLost() { + BigDecimal bigDecimalValue = new BigDecimal("789.1234567890123456"); + double doubleValue = bigDecimalValue.doubleValue(); + BigDecimal convertedBackToBigDecimal = BigDecimal.valueOf(doubleValue); + assertNotEquals(bigDecimalValue, convertedBackToBigDecimal); + } +} diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalUnitTest.java new file mode 100644 index 0000000000..3ee611f315 --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/BigDecimalUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.javadoublevsbigdecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import org.junit.jupiter.api.Test; + +public class BigDecimalUnitTest { + + private BigDecimal bigDecimal1 = new BigDecimal("124567890.0987654321"); + private BigDecimal bigDecimal2 = new BigDecimal("987654321.123456789"); + + @Test + public void givenTwoBigDecimals_whenAdd_thenCorrect() { + BigDecimal expected = new BigDecimal("1112222211.2222222211"); + BigDecimal actual = bigDecimal1.add(bigDecimal2); + assertEquals(expected, actual); + } + + @Test + public void givenTwoBigDecimals_whenMultiply_thenCorrect() { + BigDecimal expected = new BigDecimal("123030014929277547.5030955772112635269"); + BigDecimal actual = bigDecimal1.multiply(bigDecimal2); + assertEquals(expected, actual); + } + + @Test + public void givenTwoBigDecimals_whenSubtract_thenCorrect() { + BigDecimal expected = new BigDecimal("-863086431.0246913569"); + BigDecimal actual = bigDecimal1.subtract(bigDecimal2); + assertEquals(expected, actual); + } + + @Test + public void givenTwoBigDecimals_whenDivide_thenCorrect() { + BigDecimal expected = new BigDecimal("0.13"); + BigDecimal actual = bigDecimal1.divide(bigDecimal2, 2, RoundingMode.HALF_UP); + assertEquals(expected, actual); + } +} diff --git a/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/JavaDoubleUnitTest.java b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/JavaDoubleUnitTest.java new file mode 100644 index 0000000000..8697e1bfce --- /dev/null +++ b/core-java-modules/core-java-numbers-6/src/test/java/com/baeldung/javadoublevsbigdecimal/JavaDoubleUnitTest.java @@ -0,0 +1,15 @@ +package com.baeldung.javadoublevsbigdecimal; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class JavaDoubleUnitTest { + + @Test + public void givenDoubleLiteral_whenAssigningToDoubleVariable_thenValueIsNotExactlyEqual() { + double doubleValue = 0.1; + double epsilon = 0.0000000000000001; + assertEquals(0.1, doubleValue, epsilon); + } +} diff --git a/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floatdoubleconversions/FloatDoubleConversionsTest.java b/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floatdoubleconversions/FloatDoubleConversionsTest.java index 10e9fd4c36..9c6b01e9de 100644 --- a/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floatdoubleconversions/FloatDoubleConversionsTest.java +++ b/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floatdoubleconversions/FloatDoubleConversionsTest.java @@ -9,25 +9,21 @@ public class FloatDoubleConversionsTest { public void whenDoubleType_thenFloatTypeSuccess(){ double interestRatesYearly = 13.333333333333334; float interest = (float) interestRatesYearly; - System.out.println(interest); //13.333333 - Assert.assertTrue(Float.class.isInstance(interest));//true + Assert.assertEquals(13.333333f, interest, 0.000004f); Double monthlyRates = 2.111111111111112; float rates = monthlyRates.floatValue(); - System.out.println(rates); //2.1111112 - Assert.assertTrue(Float.class.isInstance(rates));//true + Assert.assertEquals(2.1111112f, rates, 0.00000013); } @Test public void whenFloatType_thenDoubleTypeSuccess(){ float gradeAverage =2.05f; double average = gradeAverage; - System.out.println(average); //2.049999952316284 - Assert.assertTrue(Double.class.isInstance(average));//true + Assert.assertEquals(2.05, average, 0.06); Float monthlyRates = 2.1111112f; Double rates = monthlyRates.doubleValue(); - System.out.println(rates); //2.1111112 - Assert.assertTrue(Double.class.isInstance(rates));//true + Assert.assertEquals(2.11111112, rates, 0.0000002);//true } } diff --git a/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floattobigdecimal/FloatToBigDecimalUnitTest.java b/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floattobigdecimal/FloatToBigDecimalUnitTest.java new file mode 100644 index 0000000000..f783d00976 --- /dev/null +++ b/core-java-modules/core-java-numbers-conversions/src/test/java/com/baeldung/floattobigdecimal/FloatToBigDecimalUnitTest.java @@ -0,0 +1,49 @@ +package com.baeldung.floattobigdecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.math.BigDecimal; + +import org.junit.jupiter.api.Test; + +class FloatToBigDecimalUnitTest { + + @Test + public void whenFloatComparedWithDifferentValues_thenCouldMatch() { + assertNotEquals(1.1f, 1.09f); + assertEquals(1.1f, 1.09999999f); + } + + @Test + public void whenCreatedFromFloat_thenMatchesInternallyStoredValue() { + float floatToConvert = 1.10000002384185791015625f; + BigDecimal bdFromFloat = new BigDecimal(floatToConvert); + assertEquals("1.10000002384185791015625", bdFromFloat.toString()); + } + + @Test + public void whenCreatedFromString_thenPreservesTheOriginal() { + BigDecimal bdFromString = new BigDecimal("1.1"); + assertEquals("1.1", bdFromString.toString()); + } + + @Test + public void whenCreatedFromFloatConvertedToString_thenFloatInternalValueGetsTruncated() { + String floatValue = Float.toString(1.10000002384185791015625f); + BigDecimal bdFromString = new BigDecimal(floatValue); + assertEquals("1.1", floatValue); + assertEquals("1.1", bdFromString.toString()); + } + + @Test + public void whenCreatedByValueOf_thenFloatValueGetsTruncated() { + assertEquals("1.100000023841858", BigDecimal.valueOf(1.10000002384185791015625f).toString()); + } + + @Test + public void whenDoubleConvertsFloatToString_thenFloatValueGetsTruncated() { + assertEquals("1.100000023841858", Double.toString(1.10000002384185791015625f)); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-os/README.md b/core-java-modules/core-java-os/README.md index 6d477de70a..81e67e4663 100644 --- a/core-java-modules/core-java-os/README.md +++ b/core-java-modules/core-java-os/README.md @@ -14,5 +14,6 @@ This module contains articles about working with the operating system (OS) in Ja - [How to Run a Shell Command in Java](http://www.baeldung.com/run-shell-command-in-java) - [Taking Screenshots Using Java](https://www.baeldung.com/java-taking-screenshots) - [Java Sound API – Capturing Microphone](https://www.baeldung.com/java-sound-api-capture-mic) +- [How to Detect the Username Using Java](https://www.baeldung.com/java-get-username) This module uses Java 9, so make sure to have the JDK 9 installed to run it. diff --git a/core-java-modules/core-java-regex-2/README.md b/core-java-modules/core-java-regex-2/README.md index 02e470cce5..404b33b65f 100644 --- a/core-java-modules/core-java-regex-2/README.md +++ b/core-java-modules/core-java-regex-2/README.md @@ -9,4 +9,5 @@ - [Regular Expression: \z vs \Z Anchors in Java](https://www.baeldung.com/java-regular-expression-z-vs-z-anchors) - [Extract Text Between Square Brackets](https://www.baeldung.com/java-get-content-between-square-brackets) - [Get the Indexes of Regex Pattern Matches in Java](https://www.baeldung.com/java-indexes-regex-pattern-matches) +- [Check if a String is Strictly Alphanumeric With Java](https://www.baeldung.com/java-check-string-contains-only-letters-numbers) - More articles: [[<-- prev]](/core-java-modules/core-java-regex) diff --git a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/SecureConnection.java b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/SecureConnection.java index 36dee603eb..6f2e3250fc 100644 --- a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/SecureConnection.java +++ b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/SecureConnection.java @@ -7,7 +7,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class SecureConnection { - + public static void main(String[] args) { if (args.length != 2) { System.out.println("Use: SecureConnection host port"); @@ -20,20 +20,20 @@ public class SecureConnection { SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(host, port); InputStream in = sslsocket.getInputStream(); OutputStream out = sslsocket.getOutputStream(); - + out.write(1); - + while (in.available() > 0) { System.out.print(in.read()); } - + System.out.println("Secured connection performed successfully"); - + } catch (Exception exception) { exception.printStackTrace(); } } - + /** * Get the host from arguments * @param args the arguments @@ -42,7 +42,7 @@ public class SecureConnection { private static String getHost(String[] args) { return args[0]; } - + /** * Get the port from arguments * @param args the arguments diff --git a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleClient.java b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleClient.java index d6efc34c3e..cf9a76b39a 100644 --- a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleClient.java +++ b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleClient.java @@ -15,10 +15,8 @@ public class SimpleClient { SocketFactory factory = SSLSocketFactory.getDefault(); try (Socket connection = factory.createSocket(host, port)) { - ((SSLSocket) connection).setEnabledCipherSuites( - new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); - ((SSLSocket) connection).setEnabledProtocols( - new String[] { "TLSv1.2"}); + ((SSLSocket) connection).setEnabledCipherSuites(new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256" }); + ((SSLSocket) connection).setEnabledProtocols(new String[] { "TLSv1.2" }); SSLParameters sslParams = new SSLParameters(); sslParams.setEndpointIdentificationAlgorithm("HTTPS"); ((SSLSocket) connection).setSSLParameters(sslParams); @@ -28,6 +26,7 @@ public class SimpleClient { } public static void main(String[] args) throws IOException { + System.setProperty("javax.net.debug", "ssl:handshake"); System.out.println(startClient("localhost", 8443)); } } diff --git a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleServer.java b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleServer.java index 27d15d04d7..83946ccc1f 100644 --- a/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleServer.java +++ b/core-java-modules/core-java-security/src/main/java/com/baeldung/ssl/example/SimpleServer.java @@ -14,11 +14,10 @@ public class SimpleServer { ServerSocketFactory factory = SSLServerSocketFactory.getDefault(); try (ServerSocket listener = factory.createServerSocket(port)) { + System.setProperty("javax.net.debug", "ssl:handshake"); ((SSLServerSocket) listener).setNeedClientAuth(true); - ((SSLServerSocket) listener).setEnabledCipherSuites( - new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); - ((SSLServerSocket) listener).setEnabledProtocols( - new String[] { "TLSv1.2"}); + ((SSLServerSocket) listener).setEnabledCipherSuites(new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256" }); + ((SSLServerSocket) listener).setEnabledProtocols(new String[] { "TLSv1.2" }); while (true) { try (Socket socket = listener.accept()) { PrintWriter out = new PrintWriter(socket.getOutputStream(), true); @@ -29,6 +28,7 @@ public class SimpleServer { } public static void main(String[] args) throws IOException { + System.setProperty("javax.net.debug", "ssl:handshake"); startServer(8443); } } \ No newline at end of file diff --git a/core-java-modules/core-java-streams-5/pom.xml b/core-java-modules/core-java-streams-5/pom.xml index dc97d81b3d..d7baf84d30 100644 --- a/core-java-modules/core-java-streams-5/pom.xml +++ b/core-java-modules/core-java-streams-5/pom.xml @@ -43,6 +43,11 @@ vavr ${vavr.version} + + com.google.guava + guava + ${guava.version} + @@ -72,6 +77,7 @@ 12 12 0.10.2 + 32.1.2-jre \ No newline at end of file diff --git a/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/streams/partitioning/PartitionStream.java b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/streams/partitioning/PartitionStream.java new file mode 100644 index 0000000000..1ef3fa7707 --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/main/java/com/baeldung/streams/partitioning/PartitionStream.java @@ -0,0 +1,90 @@ +package com.baeldung.streams.partitioning; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import com.google.common.collect.Iterables; + +public class PartitionStream { + + public static Stream> partitionList(List source, int batchSize) { + if (batchSize <= 0) { + throw new IllegalArgumentException(String.format("Expected the batchSize to be greater than ZERO, actual value was: %s", batchSize)); + } + if (source.isEmpty()) { + return Stream.empty(); + } + int nrOfFullBatches = (source.size() - 1) / batchSize; + return IntStream.rangeClosed(0, nrOfFullBatches) + .mapToObj(batch -> { + int startIndex = batch * batchSize; + int endIndex = (batch == nrOfFullBatches) ? source.size() : (batch + 1) * batchSize; + return source.subList(startIndex, endIndex); + }); + } + + public static Iterable> partitionUsingGuava(Stream source, int batchSize) { + return Iterables.partition(source::iterator, batchSize); + } + + public static List> partitionStream(Stream source, int batchSize) { + return source.collect(partitionBySize(batchSize, Collectors.toList())); + } + + public static Collector partitionBySize(int batchSize, Collector, A, R> downstream) { + Supplier> supplier = () -> new Accumulator<>( + batchSize, + downstream.supplier().get(), + downstream.accumulator()::accept + ); + + BiConsumer, T> accumulator = (acc, value) -> acc.add(value); + + BinaryOperator> combiner = (acc1, acc2) -> acc1.combine(acc2, downstream.combiner()); + + Function, R> finisher = acc -> { + if (!acc.values.isEmpty()) { + downstream.accumulator().accept(acc.downstreamAccumulator, acc.values); + } + return downstream.finisher().apply(acc.downstreamAccumulator); + }; + + return Collector.of(supplier, accumulator, combiner, finisher, Collector.Characteristics.UNORDERED); + } + + static class Accumulator { + private final List values = new ArrayList<>(); + private final int batchSize; + private A downstreamAccumulator; + private final BiConsumer> batchFullListener; + + Accumulator(int batchSize, A accumulator, BiConsumer> onBatchFull) { + this.batchSize = batchSize; + this.downstreamAccumulator = accumulator; + this.batchFullListener = onBatchFull; + } + + void add(T value) { + values.add(value); + if (values.size() == batchSize) { + batchFullListener.accept(downstreamAccumulator, new ArrayList<>(values)); + values.clear(); + } + } + + Accumulator combine(Accumulator other, BinaryOperator accumulatorCombiner) { + this.downstreamAccumulator = accumulatorCombiner.apply(downstreamAccumulator, other.downstreamAccumulator); + other.values.forEach(this::add); + return this; + } + } + +} diff --git a/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/partitioning/PartitionStreamsUnitTest.java b/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/partitioning/PartitionStreamsUnitTest.java new file mode 100644 index 0000000000..df75e69783 --- /dev/null +++ b/core-java-modules/core-java-streams-5/src/test/java/com/baeldung/partitioning/PartitionStreamsUnitTest.java @@ -0,0 +1,82 @@ +package com.baeldung.partitioning; + +import static com.baeldung.streams.partitioning.PartitionStream.partitionList; +import static com.baeldung.streams.partitioning.PartitionStream.partitionStream; +import static com.baeldung.streams.partitioning.PartitionStream.partitionUsingGuava; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.atIndex; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; + +public class PartitionStreamsUnitTest { + + @Test + void whenPartitionList_thenReturnThreeSubLists() { + List source = List.of(1, 2, 3, 4, 5, 6, 7, 8); + + Stream> result = partitionList(source, 3); + + assertThat(result) + .containsExactlyInAnyOrder( + List.of(1, 2, 3), + List.of(4, 5, 6), + List.of(7, 8) + ); + } + + @Test + void whenPartitionEmptyList_thenReturnEmptyStream() { + Stream> result = partitionList(Collections.emptyList(), 3); + + assertThat(result).isEmpty(); + } + + @Test + void whenPartitionListWithNegativeBatchSize_thenThrowException() { + assertThatThrownBy(() -> partitionList(List.of(1,2,3), -1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Expected the batchSize to be greater than ZERO, actual value was: -1"); + } + + @Test + void whenPartitionParallelStream_thenReturnThreeSubLists() { + Stream source = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).parallel(); + + List> result = partitionStream(source, 3); + + assertThat(result) + .hasSize(3) + .satisfies(batch -> assertThat(batch).hasSize(3), atIndex(0)) + .satisfies(batch -> assertThat(batch).hasSize(3), atIndex(1)) + .satisfies(batch -> assertThat(batch).hasSize(2), atIndex(2)); + } + + @Test + void whenPartitionEmptyParallelStream_thenReturnEmptyList() { + Stream source = Stream.empty().parallel(); + + List> result = partitionStream(source, 3); + + assertThat(result).isEmpty(); + } + + @Test + void whenPartitionParallelStreamWithGuava_thenReturnThreeSubLists() { + Stream source = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).parallel(); + + Iterable> result = partitionUsingGuava(source, 3); + + assertThat(result) + .map(ArrayList::new) + .hasSize(3) + .satisfies(batch -> assertThat(batch).asList().hasSize(3), atIndex(0)) + .satisfies(batch -> assertThat(batch).asList().hasSize(3), atIndex(1)) + .satisfies(batch -> assertThat(batch).asList().hasSize(2), atIndex(2)); + } +} diff --git a/core-java-modules/core-java-string-apis/src/test/java/com/baeldung/charsequence/CharSequenceVsStringUnitTest.java b/core-java-modules/core-java-string-apis/src/test/java/com/baeldung/charsequence/CharSequenceVsStringUnitTest.java index aa15345bcb..13a0407a98 100644 --- a/core-java-modules/core-java-string-apis/src/test/java/com/baeldung/charsequence/CharSequenceVsStringUnitTest.java +++ b/core-java-modules/core-java-string-apis/src/test/java/com/baeldung/charsequence/CharSequenceVsStringUnitTest.java @@ -1,11 +1,11 @@ package com.baeldung.charsequence; -import org.junit.Test; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import org.junit.Test; + public class CharSequenceVsStringUnitTest { @Test @@ -44,4 +44,43 @@ public class CharSequenceVsStringUnitTest { assertEquals(firstAddressOfTest, secondAddressOfTest); } + + @Test + public void givenCharSequenceAsString_whenConvertingUsingCasting_thenCorrect() { + String expected = "baeldung"; + CharSequence charSequence = "baeldung"; + String explicitCastedString = (String) charSequence; + + assertEquals(expected, charSequence); + assertEquals(expected, explicitCastedString); + } + + @Test(expected = ClassCastException.class) + public void givenCharSequenceAsStringBuiler_whenConvertingUsingCasting_thenThrowException() { + CharSequence charSequence = new StringBuilder("baeldung"); + String castedString = (String) charSequence; + } + + @Test + public void givenCharSequence_whenConvertingUsingToString_thenCorrect() { + String expected = "baeldung"; + CharSequence charSequence1 = "baeldung"; + CharSequence charSequence2 = new StringBuilder("baeldung"); + + assertEquals(expected, charSequence1.toString()); + assertEquals(expected, charSequence2.toString()); + } + + @Test + public void givenCharSequence_whenConvertingUsingValueOf_thenCorrect() { + String expected = "baeldung"; + CharSequence charSequence1 = "baeldung"; + CharSequence charSequence2 = new StringBuilder("baeldung"); + CharSequence charSequence3 = null; + + assertEquals(expected, String.valueOf(charSequence1)); + assertEquals(expected, String.valueOf(charSequence2)); + assertEquals("null", String.valueOf(charSequence3)); + } + } diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index cd31bb5845..ae9ff59222 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -83,6 +83,7 @@ core-java-collections-maps core-java-collections-maps-2 core-java-collections-maps-3 + core-java-collections-maps-7 core-java-compiler core-java-concurrency-2 core-java-concurrency-advanced diff --git a/libraries-apache-commons-2/pom.xml b/libraries-apache-commons-2/pom.xml index d771aac9ab..ee9b51e6cc 100644 --- a/libraries-apache-commons-2/pom.xml +++ b/libraries-apache-commons-2/pom.xml @@ -28,12 +28,18 @@ commons-vfs2 ${commons-vfs2.version} + + org.apache.commons + commons-text + ${apache-commons-text.version} + 1.23.0 1.10.13 2.9.0 + 1.10.0 \ No newline at end of file diff --git a/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java new file mode 100644 index 0000000000..c788f6ee61 --- /dev/null +++ b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java @@ -0,0 +1,29 @@ +package com.baeldung.commons.convertunicode; + +import org.apache.commons.text.StringEscapeUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class UnicodeConverterUtil { + + public static String decodeWithApacheCommons(String input) { + return StringEscapeUtils.unescapeJava(input); + } + + public static String decodeWithPlainJava(String input) { + Pattern pattern = Pattern.compile("\\\\u[0-9a-fA-F]{4}"); + Matcher matcher = pattern.matcher(input); + + StringBuilder decodedString = new StringBuilder(); + + while (matcher.find()) { + String unicodeSequence = matcher.group(); + char unicodeChar = (char) Integer.parseInt(unicodeSequence.substring(2), 16); + matcher.appendReplacement(decodedString, Character.toString(unicodeChar)); + } + + matcher.appendTail(decodedString); + return decodedString.toString(); + } +} diff --git a/libraries-apache-commons-2/src/test/java/com/baeldung/commons/convertunicode/UnicodeConverterUnitTest.java b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/convertunicode/UnicodeConverterUnitTest.java new file mode 100644 index 0000000000..f4a9bbcb77 --- /dev/null +++ b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/convertunicode/UnicodeConverterUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.commons.convertunicode; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class UnicodeConverterUnitTest { + + @Test + public void whenInputHaveUnicodeSequences_ThenDecode() { + String encodedString = "\\u0048\\u0065\\u006C\\u006C\\u006F World"; + String expectedDecodedString = "Hello World"; + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithApacheCommons(encodedString)); + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithPlainJava(encodedString)); + } + + @Test + public void whenInputHaveNoUnicodeSequences_ThenDoNothing() { + String inputString = "Hello World"; + assertEquals(inputString, UnicodeConverterUtil.decodeWithApacheCommons(inputString)); + assertEquals(inputString, UnicodeConverterUtil.decodeWithPlainJava(inputString)); + } + + @Test + public void whenInputHaveUnicodeSequencesInMiddle_ThenDecode() { + String encodedString = "This is a test \\u0069\\u006E the middle."; + String expectedDecodedString = "This is a test in the middle."; + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithApacheCommons(encodedString)); + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithPlainJava(encodedString)); + } + + @Test + public void whenInputHaveMultipleUnicodeSequences_ThenDecode() { + String encodedString = "Unicode: \\u0048\\u0065\\u006C\\u006C\\u006F \\u0057\\u006F\\u0072\\u006C\\u0064"; + String expectedDecodedString = "Unicode: Hello World"; + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithApacheCommons(encodedString)); + assertEquals(expectedDecodedString, UnicodeConverterUtil.decodeWithPlainJava(encodedString)); + } +} diff --git a/libraries-io/pom.xml b/libraries-io/pom.xml index e92f6e11e1..fa89ebeabe 100644 --- a/libraries-io/pom.xml +++ b/libraries-io/pom.xml @@ -5,6 +5,18 @@ 4.0.0 libraries-io libraries-io + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + com.baeldung @@ -34,6 +46,11 @@ zip4j ${zip4j.version} + + com.opencsv + opencsv + ${opencsv.version} + @@ -42,6 +59,10 @@ 0.27.0 2.4 2.9.0 + 5.7.1 + 17 + 17 + UTF-8 \ No newline at end of file diff --git a/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/Application.java b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/Application.java new file mode 100644 index 0000000000..b84da6229b --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/Application.java @@ -0,0 +1,4 @@ +package com.baeldung.java.io.pojotocsv; + +public record Application(String id, String name, Integer age, String created_at) { +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/ApplicationWithAnnotation.java b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/ApplicationWithAnnotation.java new file mode 100644 index 0000000000..a0cd158843 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/ApplicationWithAnnotation.java @@ -0,0 +1,8 @@ +package com.baeldung.java.io.pojotocsv; + +import com.opencsv.bean.CsvBindByName; +import com.opencsv.bean.CsvBindByPosition; + +public record ApplicationWithAnnotation(@CsvBindByName(column = "id", required = true) @CsvBindByPosition(position = 1) String id, @CsvBindByName(column = "name", required = true) @CsvBindByPosition(position = 0) String name, + @CsvBindByName(column = "age", required = true) @CsvBindByPosition(position = 2) Integer age, @CsvBindByName(column = "position", required = true) @CsvBindByPosition(position = 3) String created_at) { +} \ No newline at end of file diff --git a/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/BeanToCsv.java b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/BeanToCsv.java new file mode 100644 index 0000000000..f66c16beda --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/BeanToCsv.java @@ -0,0 +1,55 @@ +package com.baeldung.java.io.pojotocsv; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import com.opencsv.CSVWriter; +import com.opencsv.bean.StatefulBeanToCsvBuilder; +import com.opencsv.exceptions.CsvDataTypeMismatchException; +import com.opencsv.exceptions.CsvRequiredFieldEmptyException; + +public class BeanToCsv { + + public void beanToCSVWithDefault(List applications) throws Exception { + try (FileWriter writer = new FileWriter("src/main/resources/application.csv")) { + var builder = new StatefulBeanToCsvBuilder(writer).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) + .withSeparator(',') + .build(); + builder.write(applications); + } + } + + public void beanToCSVWithCustomHeaderStrategy(List applications) throws IOException, CsvRequiredFieldEmptyException, CsvDataTypeMismatchException { + try (FileWriter writer = new FileWriter("src/main/resources/application2.csv")) { + var mappingStrategy = new CustomHeaderStrategy(); + mappingStrategy.setType(Application.class); + + var builder = new StatefulBeanToCsvBuilder(writer).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) + .withMappingStrategy(mappingStrategy) + .build(); + builder.write(applications); + } + } + + public void beanToCSVWithCustomPositionStrategy(List applications) throws Exception { + try (FileWriter writer = new FileWriter("src/main/resources/application3.csv")) { + var builder = new StatefulBeanToCsvBuilder(writer).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) + .build(); + builder.write(applications); + } + } + + public void beanToCSVWithCustomHeaderAndPositionStrategy(List applications) throws IOException, CsvRequiredFieldEmptyException, CsvDataTypeMismatchException { + try (FileWriter writer = new FileWriter("src/main/resources/application4.csv")) { + var mappingStrategy = new CustomColumnPositionStrategy(); + mappingStrategy.setType(ApplicationWithAnnotation.class); + + var builder = new StatefulBeanToCsvBuilder(writer).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER) + .withMappingStrategy(mappingStrategy) + .build(); + builder.write(applications); + } + } + +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomColumnPositionStrategy.java b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomColumnPositionStrategy.java new file mode 100644 index 0000000000..204bf3f917 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomColumnPositionStrategy.java @@ -0,0 +1,12 @@ +package com.baeldung.java.io.pojotocsv; + +import com.opencsv.bean.ColumnPositionMappingStrategy; +import com.opencsv.exceptions.CsvRequiredFieldEmptyException; + +public class CustomColumnPositionStrategy extends ColumnPositionMappingStrategy { + @Override + public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException { + super.generateHeader(bean); + return super.getColumnMapping(); + } +} diff --git a/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomHeaderStrategy.java b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomHeaderStrategy.java new file mode 100644 index 0000000000..8e81c8ec28 --- /dev/null +++ b/libraries-io/src/main/java/com/baeldung/java/io/pojotocsv/CustomHeaderStrategy.java @@ -0,0 +1,16 @@ +package com.baeldung.java.io.pojotocsv; + +import java.util.Arrays; + +import com.opencsv.bean.HeaderColumnNameMappingStrategy; +import com.opencsv.exceptions.CsvRequiredFieldEmptyException; + +public class CustomHeaderStrategy extends HeaderColumnNameMappingStrategy { + @Override + public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException { + String[] header = super.generateHeader(bean); + return Arrays.stream(header) + .map(String::toLowerCase) + .toArray(String[]::new); + } +} diff --git a/libraries-io/src/main/resources/application.csv b/libraries-io/src/main/resources/application.csv new file mode 100644 index 0000000000..d4040e18e1 --- /dev/null +++ b/libraries-io/src/main/resources/application.csv @@ -0,0 +1,4 @@ +AGE,CREATED_AT,ID,NAME +34,2023-08-11,123,Sam +44,2023-02-11,456,Tam +54,2023-03-11,890,Jam diff --git a/libraries-io/src/main/resources/application2.csv b/libraries-io/src/main/resources/application2.csv new file mode 100644 index 0000000000..92bbced703 --- /dev/null +++ b/libraries-io/src/main/resources/application2.csv @@ -0,0 +1,4 @@ +age,created_at,id,name +34,2023-08-11,123,Sam +44,2023-02-11,456,Tam +54,2023-03-11,890,Jam diff --git a/libraries-io/src/main/resources/application3.csv b/libraries-io/src/main/resources/application3.csv new file mode 100644 index 0000000000..165107b103 --- /dev/null +++ b/libraries-io/src/main/resources/application3.csv @@ -0,0 +1,3 @@ +Sam,123,34,2023-08-11 +Tam,456,44,2023-02-11 +Jam,789,54,2023-03-11 diff --git a/libraries-io/src/main/resources/application4.csv b/libraries-io/src/main/resources/application4.csv new file mode 100644 index 0000000000..b7999ae227 --- /dev/null +++ b/libraries-io/src/main/resources/application4.csv @@ -0,0 +1,4 @@ +name,id,age,created_at +Sam,123,34,2023-08-11 +Tam,456,44,2023-02-11 +Jam,789,54,2023-03-11 diff --git a/libraries-io/src/test/java/com/baeldung/java/io/pojotocsv/BeanToCsvUnitTest.java b/libraries-io/src/test/java/com/baeldung/java/io/pojotocsv/BeanToCsvUnitTest.java new file mode 100644 index 0000000000..50cd1b1e0e --- /dev/null +++ b/libraries-io/src/test/java/com/baeldung/java/io/pojotocsv/BeanToCsvUnitTest.java @@ -0,0 +1,80 @@ +package com.baeldung.java.io.pojotocsv; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BeanToCsvUnitTest { + + List applications = new ArrayList<>(); + List applicationsWithAnnotation = new ArrayList<>(); + + @BeforeEach + public void beforeEach() { + applications = List.of(new Application("123", "Sam", 34, "2023-08-11"), new Application("456", "Tam", 44, "2023-02-11"), new Application("890", "Jam", 54, "2023-03-11")); + + applicationsWithAnnotation = List.of(new ApplicationWithAnnotation("123", "Sam", 34, "2023-08-11"), new ApplicationWithAnnotation("456", "Tam", 44, "2023-02-11"), new ApplicationWithAnnotation("789", "Jam", 54, "2023-03-11")); + } + + @Test + public void givenApplicationPOJO_whenUsingDefaultStrategy_thenReceiveCSVFormatWithAscendingOrderOfField() throws Exception { + BeanToCsv beanToCsv = new BeanToCsv(); + beanToCsv.beanToCSVWithDefault(applications); + try (BufferedReader bufferedReader = Files.newBufferedReader(Path.of("src/main/resources/application.csv"))) { + List content = bufferedReader.lines() + .toList(); + assertThat(content.get(0)).isEqualTo("AGE,CREATED_AT,ID,NAME"); + assertThat(content.get(1)).isEqualTo("34,2023-08-11,123,Sam"); + assertThat(content.get(2)).isEqualTo("44,2023-02-11,456,Tam"); + assertThat(content.get(3)).isEqualTo("54,2023-03-11,890,Jam"); + } + } + + @Test + public void givenApplicationPOJO_whenUsingCustomHeaderStrategy_thenReceiveCSVFormatWithCustomHeaders() throws Exception { + BeanToCsv beanToCsv = new BeanToCsv(); + beanToCsv.beanToCSVWithCustomHeaderStrategy(applications); + try (BufferedReader bufferedReader = Files.newBufferedReader(Path.of("src/main/resources/application2.csv"))) { + List content = bufferedReader.lines() + .toList(); + assertThat(content.get(0)).isEqualTo("age,created_at,id,name"); + assertThat(content.get(1)).isEqualTo("34,2023-08-11,123,Sam"); + assertThat(content.get(2)).isEqualTo("44,2023-02-11,456,Tam"); + assertThat(content.get(3)).isEqualTo("54,2023-03-11,890,Jam"); + } + } + + @Test + public void givenApplicationPOJOWithAnnotation_whenUsingCustomPositionStrategy_thenReceiveCSVFormatWithCustomPosition() throws Exception { + BeanToCsv beanToCsv = new BeanToCsv(); + beanToCsv.beanToCSVWithCustomPositionStrategy(applicationsWithAnnotation); + try (BufferedReader bufferedReader = Files.newBufferedReader(Path.of("src/main/resources/application3.csv"))) { + List content = bufferedReader.lines() + .toList(); + assertThat(content.get(0)).isEqualTo("Sam,123,34,2023-08-11"); + assertThat(content.get(1)).isEqualTo("Tam,456,44,2023-02-11"); + assertThat(content.get(2)).isEqualTo("Jam,789,54,2023-03-11"); + } + } + + @Test + public void givenApplicationPOJOWithAnnotation_whenUsingCustomHeaderPositionStrategy_thenReceiveCSVFormatWithCustomHeaderPosition() throws Exception { + BeanToCsv beanToCsv = new BeanToCsv(); + beanToCsv.beanToCSVWithCustomHeaderAndPositionStrategy(applicationsWithAnnotation); + try (BufferedReader bufferedReader = Files.newBufferedReader(Path.of("src/main/resources/application4.csv"))) { + List content = bufferedReader.lines() + .toList(); + assertThat(content.get(0)).isEqualTo("name,id,age,created_at"); + assertThat(content.get(1)).isEqualTo("Sam,123,34,2023-08-11"); + assertThat(content.get(2)).isEqualTo("Tam,456,44,2023-02-11"); + assertThat(content.get(3)).isEqualTo("Jam,789,54,2023-03-11"); + } + } +} diff --git a/mapstruct/src/main/java/com/baeldung/expression/dto/LicenseDto.java b/mapstruct/src/main/java/com/baeldung/expression/dto/LicenseDto.java index 025df1a4f6..1d7cacaead 100644 --- a/mapstruct/src/main/java/com/baeldung/expression/dto/LicenseDto.java +++ b/mapstruct/src/main/java/com/baeldung/expression/dto/LicenseDto.java @@ -11,6 +11,8 @@ public class LicenseDto { private LocalDateTime endDate; + private String licenseType; + public UUID getId() { return id; } @@ -35,4 +37,12 @@ public class LicenseDto { this.endDate = endDate; } + public String getLicenseType() { + return licenseType; + } + + public void setLicenseType(String licenseType) { + this.licenseType = licenseType; + } + } diff --git a/mapstruct/src/main/java/com/baeldung/expression/mapper/LicenseMapper.java b/mapstruct/src/main/java/com/baeldung/expression/mapper/LicenseMapper.java index 100588b45d..0d8904e220 100644 --- a/mapstruct/src/main/java/com/baeldung/expression/mapper/LicenseMapper.java +++ b/mapstruct/src/main/java/com/baeldung/expression/mapper/LicenseMapper.java @@ -6,6 +6,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.mapstruct.AfterMapping; +import org.mapstruct.Condition; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; @@ -40,4 +41,13 @@ public interface LicenseMapper { .toDays() <= 14; } -} + @Condition + default boolean mapsToExpectedLicenseType(String licenseType) { + try { + return licenseType != null && License.LicenseType.valueOf(licenseType) != null; + } catch (IllegalArgumentException e) { + return false; + } + } + +} \ No newline at end of file diff --git a/mapstruct/src/main/java/com/baeldung/expression/model/License.java b/mapstruct/src/main/java/com/baeldung/expression/model/License.java index 71e4f086c0..e36e278a57 100644 --- a/mapstruct/src/main/java/com/baeldung/expression/model/License.java +++ b/mapstruct/src/main/java/com/baeldung/expression/model/License.java @@ -3,7 +3,6 @@ package com.baeldung.expression.model; import java.time.OffsetDateTime; import java.util.UUID; - public class License { private UUID id; @@ -16,6 +15,8 @@ public class License { private boolean renewalRequired; + private LicenseType licenseType; + public UUID getId() { return id; } @@ -55,4 +56,16 @@ public class License { public void setRenewalRequired(boolean renewalRequired) { this.renewalRequired = renewalRequired; } -} + + public enum LicenseType { + INDIVIDUAL, FAMILY + } + + public LicenseType getLicenseType() { + return licenseType; + } + + public void setLicenseType(LicenseType licenseType) { + this.licenseType = licenseType; + } +} \ No newline at end of file diff --git a/mapstruct/src/test/java/com/baeldung/expression/mapper/LicenseMapperUnitTest.java b/mapstruct/src/test/java/com/baeldung/expression/mapper/LicenseMapperUnitTest.java index dcdda5c1ac..d7521e81f5 100644 --- a/mapstruct/src/test/java/com/baeldung/expression/mapper/LicenseMapperUnitTest.java +++ b/mapstruct/src/test/java/com/baeldung/expression/mapper/LicenseMapperUnitTest.java @@ -6,6 +6,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.UUID; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; @@ -74,4 +75,31 @@ class LicenseMapperUnitTest { assertThat(license.getId()).isSameAs(id); } + @Test + void givenLicenseDtoWithoutLicenseTypeString_whenMapperMethodIsInvoked_thenLicenseShouldBePopulatedWithoutLicenseType() { + LicenseDto licenseDto = new LicenseDto(); + License license = licenseMapper.toLicense(licenseDto); + assertThat(license).isNotNull(); + Assertions.assertNull(license.getLicenseType()); + } + + @Test + void givenLicenseDtoWithInvalidLicenseTypeString_whenMapperMethodIsInvoked_thenLicenseShouldBePopulatedWithoutLicenseType() { + LicenseDto licenseDto = new LicenseDto(); + licenseDto.setLicenseType("invalid_license_type"); + License license = licenseMapper.toLicense(licenseDto); + assertThat(license).isNotNull(); + Assertions.assertNull(license.getLicenseType()); + } + + @Test + void givenLicenseDtoWithValidLicenseTypeString_whenMapperMethodIsInvoked_thenLicenseShouldBePopulatedWithMatchingLicenseType() { + LicenseDto licenseDto = new LicenseDto(); + licenseDto.setLicenseType("INDIVIDUAL"); + License license = licenseMapper.toLicense(licenseDto); + assertThat(license).isNotNull(); + Assertions.assertNotNull(license.getLicenseType()); + assertThat(license.getLicenseType()).isEqualTo(License.LicenseType.INDIVIDUAL); + } + } \ No newline at end of file diff --git a/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java b/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java index df7a57a984..ab5993d816 100644 --- a/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java +++ b/messaging-modules/apache-camel/src/test/java/com/apache/baeldung/camel/jackson/FruitArrayJacksonUnmarshalUnitTest.java @@ -45,8 +45,8 @@ public class FruitArrayJacksonUnmarshalUnitTest { return new RouteBuilder() { @Override public void configure() throws Exception { - from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class)) - .to("mock:marshalledObject"); + from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class)) + .to("mock:marshalledObject"); } }; } diff --git a/microservices-modules/micronaut/pom.xml b/microservices-modules/micronaut/pom.xml index 716b5497d8..d3f4326a85 100644 --- a/microservices-modules/micronaut/pom.xml +++ b/microservices-modules/micronaut/pom.xml @@ -7,6 +7,7 @@ micronaut 0.1 micronaut + ${packaging} com.baeldung @@ -18,7 +19,7 @@ io.micronaut - bom + micronaut-bom ${micronaut.version} pom import @@ -29,120 +30,136 @@ io.micronaut - http-client + micronaut-runtime + compile + + + io.micronaut + micronaut-jackson-databind + + + + + io.micronaut + micronaut-http-client compile io.micronaut - http-server-netty + micronaut-http-server-netty + compile + + + jakarta.annotation + jakarta.annotation-api compile io.micronaut - inject + micronaut-inject compile io.micronaut - runtime + micronaut-validation compile - - javax.annotation - javax.annotation-api - ${annotation.api.version} - compile - - - io.micronaut - inject-java - provided - ch.qos.logback logback-classic - ${logback.version} runtime - io.projectreactor - reactor-core - ${reactor.version} + io.micronaut.rxjava3 + micronaut-rxjava3 + compile + + + io.micronaut.rxjava3 + micronaut-rxjava3-http-client + compile + + + io.micronaut.serde + micronaut-serde-jackson + compile + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.assertj + assertj-core + test + + + io.micronaut.test + micronaut-test-junit5 + test + + io.micronaut.build + micronaut-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + ${jdk.version} + ${jdk.version} + + -parameters + + + + + + + + io.micronaut + micronaut-inject-java + ${micronaut.version} + + + + io.micronaut + micronaut-http-validation + ${micronaut.version} + + + + + org.apache.maven.plugins maven-shade-plugin ${shade.plugin.version} - package - - shade - - - - - ${exec.mainClass} - - - - + default-shade + none - - org.codehaus.mojo - exec-maven-plugin - - java - - -classpath - - ${exec.mainClass} - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.plugin.version} - - ${jdk.version} - ${jdk.version} - - -parameters - - - - io.micronaut - inject-java - ${micronaut.version} - - - - - - com.baeldung.micronaut.vs.springboot.CompareApplication - 1.0.0.RC2 + 3.10.1 17 - 1.3.2 - 3.1.6.RELEASE + 17 + jar 3.7.0 - 3.1.0 + netty + 3.2.0 \ No newline at end of file diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClient.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClient.java new file mode 100644 index 0000000000..d9e09b1c6e --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClient.java @@ -0,0 +1,42 @@ +package com.baeldung.micronaut.apiversioning.custom.client; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.async.annotation.SingleResult; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.QueryValue; +import io.micronaut.http.client.annotation.Client; +import io.reactivex.rxjava3.core.Flowable; + +@Client("/") +@Header(name = HttpHeaders.ACCEPT, value = "application/json") +public interface BirdCountClient { + + @Get( + uri = "/bird/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @Header(name = "api-key", value = "11") + @SingleResult + Flowable countV1(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/bird/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @Header(name = "api-key", value = "10") + @SingleResult + Flowable countV2(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/bird/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @Header(name = "api-key", value = "") + @SingleResult + Flowable countDefault(@QueryValue("max") @Nullable Integer max); +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/BirdCountController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/BirdCountController.java new file mode 100644 index 0000000000..c836761266 --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/BirdCountController.java @@ -0,0 +1,36 @@ +package com.baeldung.micronaut.apiversioning.custom.server; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.version.annotation.Version; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.QueryValue; +import io.reactivex.rxjava3.core.Flowable; + +import java.util.stream.Stream; + +@Controller("/bird") +public class BirdCountController { + + @Get(value = "/count", produces = {"application/json"}) + @Version("1") + public Flowable countV1(@QueryValue("max") @Nullable Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Bird " + index) + .limit(max == null ? 10 : max) + ); + } + + @Get(value = "/count", produces = {"application/json"}) + @Version("2") + public Flowable countV2(@QueryValue("max") @NonNull Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Bird " + index) + .limit(max) + ); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/CustomVersionResolver.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/CustomVersionResolver.java new file mode 100644 index 0000000000..3075a135c8 --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/custom/server/CustomVersionResolver.java @@ -0,0 +1,31 @@ +package com.baeldung.micronaut.apiversioning.custom.server; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.context.annotation.Value; +import io.micronaut.http.HttpRequest; +import io.micronaut.web.router.version.resolution.RequestVersionResolver; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import java.util.Optional; + +@Singleton +@Requires(property = "my.router.versioning.enabled", value = "true") +public class CustomVersionResolver implements RequestVersionResolver { + + @Inject + @Value("${micronaut.router.versioning.default-version}") + private String defaultVersion; + + @Override + public Optional resolve(HttpRequest request) { + var apiKey = Optional.ofNullable(request.getHeaders().get("api-key")); + + if (apiKey.isPresent() && !apiKey.get().isEmpty()) { + return Optional.of(Integer.parseInt(apiKey.get()) % 2 == 0 ? "2" : "1"); + } + + return Optional.of(defaultVersion); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClient.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClient.java new file mode 100644 index 0000000000..487bff4d57 --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClient.java @@ -0,0 +1,42 @@ +package com.baeldung.micronaut.apiversioning.header.client; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.async.annotation.SingleResult; +import io.micronaut.core.version.annotation.Version; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.QueryValue; +import io.micronaut.http.client.annotation.Client; +import io.reactivex.rxjava3.core.Flowable; + +@Client("/") +@Header(name = HttpHeaders.ACCEPT, value = "application/json") +public interface DogCountClient { + + @Get( + uri = "/dog/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @Version("1") + @SingleResult + Flowable countV1(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/dog/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @Version("2") + @SingleResult + Flowable countV2(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/dog/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @SingleResult + Flowable countDefault(@QueryValue("max") @Nullable Integer max); +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/server/DogCountController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/server/DogCountController.java new file mode 100644 index 0000000000..de0f6788cf --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/header/server/DogCountController.java @@ -0,0 +1,36 @@ +package com.baeldung.micronaut.apiversioning.header.server; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.version.annotation.Version; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.QueryValue; +import io.reactivex.rxjava3.core.Flowable; + +import java.util.stream.Stream; + +@Controller("/dog") +public class DogCountController { + + @Get(value = "/count", produces = {"application/json"}) + @Version("1") + public Flowable countV1(@QueryValue("max") @Nullable Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Dog " + index) + .limit(max == null ? 10 : max) + ); + } + + @Get(value = "/count", produces = {"application/json"}) + @Version("2") + public Flowable countV2(@QueryValue("max") @NonNull Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Dog " + index) + .limit(max) + ); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClient.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClient.java new file mode 100644 index 0000000000..2332ade98e --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClient.java @@ -0,0 +1,23 @@ +package com.baeldung.micronaut.apiversioning.param.client; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.async.annotation.SingleResult; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.QueryValue; +import io.micronaut.http.client.annotation.Client; +import io.reactivex.rxjava3.core.Flowable; + +@Client("/") +@Header(name = HttpHeaders.ACCEPT, value = "application/json") +public interface CatCountClient { + + @Get( + uri = "/cat/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @SingleResult + Flowable count(@QueryValue("max") @Nullable Integer max, @QueryValue(value = "v", defaultValue = "1") @Nullable Integer version); +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/server/CatCountController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/server/CatCountController.java new file mode 100644 index 0000000000..43a34a2272 --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/param/server/CatCountController.java @@ -0,0 +1,37 @@ +package com.baeldung.micronaut.apiversioning.param.server; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.version.annotation.Version; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.QueryValue; +import io.reactivex.rxjava3.core.Flowable; + +import java.util.stream.Stream; + +@Controller("/cat") +public class CatCountController { + + @Get(value = "/count", produces = {"application/json"}) + @Version("1") + public Flowable countV1(@QueryValue("max") @Nullable Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Cat " + index) + .limit(max == null ? 10 : max) + ); + } + + + @Get(value = "/count", produces = {"application/json"}) + @Version("2") + public Flowable countV2(@QueryValue("max") @NonNull Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Cat " + index) + .limit(max) + ); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClient.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClient.java new file mode 100644 index 0000000000..cdb344b4e5 --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClient.java @@ -0,0 +1,39 @@ +package com.baeldung.micronaut.apiversioning.url.client; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.async.annotation.SingleResult; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.QueryValue; +import io.micronaut.http.client.annotation.Client; +import io.reactivex.rxjava3.core.Flowable; + +@Client("/") +@Header(name = HttpHeaders.ACCEPT, value = "application/json") +public interface SheepCountClient { + + @Get( + uri = "/v1/sheep/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @SingleResult + Flowable countV1(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/v2/sheep/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @SingleResult + Flowable countV2(@QueryValue("max") @Nullable Integer max); + + @Get( + uri = "/sheep/count", + consumes = {"application/json"}, + produces = {"application/json"} + ) + @SingleResult + Flowable countDefault(@QueryValue("max") @Nullable Integer max); +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV1.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV1.java new file mode 100644 index 0000000000..5ee6a2a57d --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV1.java @@ -0,0 +1,26 @@ +package com.baeldung.micronaut.apiversioning.url.server; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.reactivex.rxjava3.core.Flowable; + +import java.util.stream.Stream; + +@Controller("/v1/sheep/count") +public class SheepCountControllerV1 { + + @Get( + uri = "{?max}", + consumes = {"application/json"}, + produces = {"application/json"} + ) + Flowable countV1(@Nullable Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Sheep " + index) + .limit(max == null ? 10 : max) + ); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV2.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV2.java new file mode 100644 index 0000000000..d95f4a3cca --- /dev/null +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/apiversioning/url/server/SheepCountControllerV2.java @@ -0,0 +1,27 @@ +package com.baeldung.micronaut.apiversioning.url.server; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.QueryValue; +import io.reactivex.rxjava3.core.Flowable; + +import java.util.stream.Stream; + +@Controller +public class SheepCountControllerV2 { + + @Get( + uris = {"/v2/sheep/count", "/sheep/count"}, + consumes = {"application/json"}, + produces = {"application/json"} + ) + Flowable countV2(@QueryValue("max") @NonNull Integer max) { + return Flowable.fromStream( + Stream.iterate(0, i -> i + 1) + .map(index -> "Sheep " + index) + .limit(max) + ); + } + +} diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClient.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClient.java index 96bc51f235..943b2f703e 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClient.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClient.java @@ -1,28 +1,27 @@ package com.baeldung.micronaut.helloworld.client; import io.micronaut.http.HttpRequest; +import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; -import io.micronaut.http.client.RxHttpClient; -import io.reactivex.Single; - -import javax.inject.Singleton; +import io.reactivex.rxjava3.core.Single; +import jakarta.inject.Singleton; @Singleton public class ConcreteGreetingClient { - private RxHttpClient httpClient; + private HttpClient httpClient; - public ConcreteGreetingClient(@Client("/") RxHttpClient httpClient) { + public ConcreteGreetingClient(@Client("/") HttpClient httpClient) { this.httpClient = httpClient; } public String greet(String name) { HttpRequest req = HttpRequest.GET("/greet/" + name); - return httpClient.retrieve(req).blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)).blockingGet(); } public Single greetAsync(String name) { HttpRequest req = HttpRequest.GET("/async/greet/" + name); - return httpClient.retrieve(req).first("An error as occurred"); + return Single.fromPublisher(httpClient.retrieve(req)); } } diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/AsyncGreetController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/AsyncGreetController.java index 4d86b9dfed..862a822573 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/AsyncGreetController.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/AsyncGreetController.java @@ -3,9 +3,9 @@ package com.baeldung.micronaut.helloworld.server.controller; import com.baeldung.micronaut.helloworld.server.service.GreetingService; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; -import io.reactivex.Single; -import javax.inject.Inject; +import io.reactivex.rxjava3.core.Single; +import jakarta.inject.Inject; @Controller("/async/greet") public class AsyncGreetController { diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/GreetController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/GreetController.java index c890c037e4..ba3b6197d8 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/GreetController.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/controller/GreetController.java @@ -7,7 +7,7 @@ import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.Post; -import javax.inject.Inject; +import jakarta.inject.Inject; @Controller("/greet") public class GreetController { diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/EnglishGreetingService.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/EnglishGreetingService.java index 8ea5172cf6..865ee0b639 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/EnglishGreetingService.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/EnglishGreetingService.java @@ -2,7 +2,7 @@ package com.baeldung.micronaut.helloworld.server.service; import io.micronaut.context.annotation.Primary; -import javax.inject.Singleton; +import jakarta.inject.Singleton; @Primary @Singleton diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/SpanishGreetingService.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/SpanishGreetingService.java index 1ec53d8b2d..e426c2911e 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/SpanishGreetingService.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/helloworld/server/service/SpanishGreetingService.java @@ -1,6 +1,6 @@ package com.baeldung.micronaut.helloworld.server.service; -import javax.inject.Singleton; +import jakarta.inject.Singleton; @Singleton public class SpanishGreetingService implements GreetingService { diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/CompareApplication.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/CompareApplication.java index 4654526b28..0fea9f970b 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/CompareApplication.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/CompareApplication.java @@ -1,9 +1,11 @@ package com.baeldung.micronaut.vs.springboot; + import io.micronaut.runtime.Micronaut; public class CompareApplication { + public static void main(String[] args) { - Micronaut.run(CompareApplication.class); + Micronaut.run(CompareApplication.class, args); } -} +} \ No newline at end of file diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/client/ArithmeticClientImpl.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/client/ArithmeticClientImpl.java index 6b7ae900be..95e3157bfe 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/client/ArithmeticClientImpl.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/client/ArithmeticClientImpl.java @@ -1,46 +1,46 @@ package com.baeldung.micronaut.vs.springboot.client; -import javax.inject.Singleton; - import io.micronaut.http.HttpRequest; -import io.micronaut.http.client.RxHttpClient; +import io.micronaut.http.client.HttpClient; import io.micronaut.http.client.annotation.Client; +import io.reactivex.rxjava3.core.Single; +import jakarta.inject.Singleton; @Singleton public class ArithmeticClientImpl { - private RxHttpClient httpClient; + private HttpClient httpClient; - public ArithmeticClientImpl(@Client("/") RxHttpClient httpClient) { + public ArithmeticClientImpl(@Client("/") HttpClient httpClient) { this.httpClient = httpClient; } public String sum(float number1, float number2) { HttpRequest req = HttpRequest.GET("/math/sum/" + number1 + "/" + number2); - return httpClient.retrieve(req) - .blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)) + .blockingGet(); } public String subtract(float number1, float number2) { HttpRequest req = HttpRequest.GET("/math/subtract/" + number1 + "/" + number2); - return httpClient.retrieve(req) - .blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)) + .blockingGet(); } public String multiply(float number1, float number2) { HttpRequest req = HttpRequest.GET("/math/multiply/" + number1 + "/" + number2); - return httpClient.retrieve(req) - .blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)) + .blockingGet(); } public String divide(float number1, float number2) { HttpRequest req = HttpRequest.GET("/math/divide/" + number1 + "/" + number2); - return httpClient.retrieve(req) - .blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)) + .blockingGet(); } public String memory() { HttpRequest req = HttpRequest.GET("/math/memory"); - return httpClient.retrieve(req) - .blockingFirst(); + return Single.fromPublisher(httpClient.retrieve(req)) + .blockingGet(); } } diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/controller/ArithmeticController.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/controller/ArithmeticController.java index 5bc0e865e1..b774556ece 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/controller/ArithmeticController.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/controller/ArithmeticController.java @@ -3,7 +3,7 @@ package com.baeldung.micronaut.vs.springboot.controller; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; -import javax.inject.Inject; +import jakarta.inject.Inject; import com.baeldung.micronaut.vs.springboot.service.ArithmeticService; diff --git a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/service/ArithmeticService.java b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/service/ArithmeticService.java index e0e4680495..599a6ee4cf 100644 --- a/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/service/ArithmeticService.java +++ b/microservices-modules/micronaut/src/main/java/com/baeldung/micronaut/vs/springboot/service/ArithmeticService.java @@ -1,6 +1,6 @@ package com.baeldung.micronaut.vs.springboot.service; -import javax.inject.Singleton; +import jakarta.inject.Singleton; @Singleton public class ArithmeticService { diff --git a/microservices-modules/micronaut/src/main/resources/application.yml b/microservices-modules/micronaut/src/main/resources/application.yml index 32daacd4aa..b265d245aa 100644 --- a/microservices-modules/micronaut/src/main/resources/application.yml +++ b/microservices-modules/micronaut/src/main/resources/application.yml @@ -1,5 +1,19 @@ +context-path: / + micronaut: - application: - name: hello-world-server - server: - port: ${random.port} \ No newline at end of file + router: + versioning: + enabled: true + default-version: 2 + parameter: + enabled: true + names: 'v,api-version' + header: + enabled: true + names: + - 'X-API-VERSION' + + application: + name: hello-world-server + server: + port: ${random.port} diff --git a/microservices-modules/micronaut/src/main/resources/logback.xml b/microservices-modules/micronaut/src/main/resources/logback.xml index afaebf8e17..4db42a7916 100644 --- a/microservices-modules/micronaut/src/main/resources/logback.xml +++ b/microservices-modules/micronaut/src/main/resources/logback.xml @@ -8,6 +8,12 @@ + + + diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClientUnitTest.java new file mode 100644 index 0000000000..a3a547f9e2 --- /dev/null +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/custom/client/BirdCountClientUnitTest.java @@ -0,0 +1,42 @@ +package com.baeldung.micronaut.apiversioning.custom.client; + +import io.micronaut.context.annotation.Property; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@MicronautTest +@Property(name = "my.router.versioning.enabled", value = "true") +class BirdCountClientUnitTest { + + @Inject + private EmbeddedApplication application; + + @Inject + private BirdCountClient client; + + @Test + void givenTheCountApi_whenUsingV1ViaCustomStrategy_shouldRouteToProperHandler() { + Assertions.assertEquals(10, client.countV1(null).blockingSingle().split(",").length); + Assertions.assertEquals(4, client.countV1(4).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingV2ViaCustomStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> client.countV2(null).count().blockingGet()); + + Assertions.assertEquals(6, client.countV2(6).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingDefaultVersionViaCustomStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> client.countDefault(null).count().blockingGet()); + + Assertions.assertEquals(6, client.countDefault(6).blockingSingle().split(",").length); + } +} \ No newline at end of file diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClientUnitTest.java new file mode 100644 index 0000000000..4a47c76943 --- /dev/null +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/header/client/DogCountClientUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.micronaut.apiversioning.header.client; + +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@MicronautTest +class DogCountClientUnitTest { + + @Inject + private EmbeddedApplication application; + + @Inject + private DogCountClient dogCountClient; + + @Test + void givenTheCountApi_whenUsingV1ViaHeaderStrategy_shouldRouteToProperHandler() { + Assertions.assertEquals(10, dogCountClient.countV1(null).blockingSingle().split(",").length); + Assertions.assertEquals(4, dogCountClient.countV1(4).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingV2ViaHeaderStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> dogCountClient.countV2(null).count().blockingGet()); + + Assertions.assertEquals(6, dogCountClient.countV2(6).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingDefaultVersionViaHeaderStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> dogCountClient.countDefault(null).count().blockingGet()); + + Assertions.assertEquals(6, dogCountClient.countDefault(6).blockingSingle().split(",").length); + } +} \ No newline at end of file diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClientUnitTest.java new file mode 100644 index 0000000000..266f72eed8 --- /dev/null +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/param/client/CatCountClientUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.micronaut.apiversioning.param.client; + +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@MicronautTest +class CatCountClientUnitTest { + + @Inject + private EmbeddedApplication application; + + @Inject + private CatCountClient client; + + @Test + void givenTheCountApi_whenUsingV1ViaParameterStrategy_shouldRouteToProperHandler() { + Assertions.assertEquals(10, client.count(null, 1).blockingSingle().split(",").length); + Assertions.assertEquals(5, client.count(5, 1).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingV2ViaParameterStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> client.count(null, 2).count().blockingGet()); + + Assertions.assertEquals(6, client.count(6, 2).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingDefaultVersionViaParameterStrategy_shouldRouteToProperHandler() { + Assertions.assertEquals(10, client.count(null, null).blockingSingle().split(",").length); + Assertions.assertEquals(6, client.count(6, null).blockingSingle().split(",").length); + } +} \ No newline at end of file diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClientUnitTest.java new file mode 100644 index 0000000000..e082793fe1 --- /dev/null +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/apiversioning/url/client/SheepCountClientUnitTest.java @@ -0,0 +1,44 @@ +package com.baeldung.micronaut.apiversioning.url.client; + +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +@MicronautTest +class SheepCountClientUnitTest { + + @Inject + private EmbeddedApplication application; + @Inject + private SheepCountClient client; + + @Test + void givenTheCountApi_whenUsingV1ViaUrlStrategy_shouldRouteToProperHandler() { + Assertions.assertEquals(10, client.countV1(null).blockingSingle().split(",").length); + Assertions.assertEquals(4, client.countV1(4).blockingSingle().split(",").length); + } + + @Test + void givenTheCountApi_whenUsingV2ViaUrlStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> client.countV2(null).count().blockingGet()); + + final var actual = client.countV2(4).blockingSingle().split(",").length; + Assertions.assertEquals(4, actual); + } + + @Test + void givenTheCountApi_whenUsingDefaultVersionViaUrlStrategy_shouldRouteToProperHandler() { + Assertions.assertThrows(HttpClientResponseException.class, + () -> client.countDefault(null).count().blockingGet()); + + final var actual = client.countDefault(4).blockingSingle().split(",").length; + Assertions.assertEquals(4, actual); + } +} \ No newline at end of file diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClientUnitTest.java index 336374d5a6..88a9782074 100644 --- a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClientUnitTest.java +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/ConcreteGreetingClientUnitTest.java @@ -1,31 +1,19 @@ package com.baeldung.micronaut.helloworld.client; -import io.micronaut.context.ApplicationContext; -import io.micronaut.runtime.server.EmbeddedServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; -import static junit.framework.TestCase.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class ConcreteGreetingClientUnitTest -{ - private EmbeddedServer server; +@MicronautTest +public class ConcreteGreetingClientUnitTest { + @Inject + private EmbeddedApplication application; + @Inject private ConcreteGreetingClient client; - @Before - public void setup() - { - server = ApplicationContext.run(EmbeddedServer.class); - client = server.getApplicationContext().getBean(ConcreteGreetingClient.class); - } - - @After - public void cleanup() - { - server.stop(); - } - @Test public void testGreeting() { assertEquals(client.greet("Mike"), "Hello Mike"); diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/GreetingClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/GreetingClientUnitTest.java index c47fb3a31d..5b269531e7 100644 --- a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/GreetingClientUnitTest.java +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/helloworld/client/GreetingClientUnitTest.java @@ -1,30 +1,21 @@ package com.baeldung.micronaut.helloworld.client; -import io.micronaut.context.ApplicationContext; -import io.micronaut.runtime.server.EmbeddedServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; -import static junit.framework.TestCase.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +@MicronautTest public class GreetingClientUnitTest { - private EmbeddedServer server; + + @Inject + private EmbeddedApplication application; + + @Inject private GreetingClient client; - @Before - public void setup() - { - server = ApplicationContext.run(EmbeddedServer.class); - client = server.getApplicationContext().getBean(GreetingClient.class); - } - - @After - public void cleanup() - { - server.stop(); - } - @Test public void testGreeting() { assertEquals(client.greet("Mike"), "Hello Mike"); diff --git a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/vs/springboot/ArithmeticClientUnitTest.java b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/vs/springboot/ArithmeticClientUnitTest.java index 9a1b095d22..fa191778f5 100644 --- a/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/vs/springboot/ArithmeticClientUnitTest.java +++ b/microservices-modules/micronaut/src/test/java/com/baeldung/micronaut/vs/springboot/ArithmeticClientUnitTest.java @@ -1,34 +1,21 @@ package com.baeldung.micronaut.vs.springboot; -import static org.junit.Assert.assertEquals; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; - -import io.micronaut.context.ApplicationContext; -import io.micronaut.runtime.server.EmbeddedServer; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.baeldung.micronaut.vs.springboot.client.ArithmeticClientImpl; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@MicronautTest public class ArithmeticClientUnitTest { - private EmbeddedServer server; + @Inject + private EmbeddedApplication server; + @Inject private ArithmeticClientImpl client; - @Before - public void setup() { - server = ApplicationContext.run(EmbeddedServer.class); - client = server.getApplicationContext() - .getBean(ArithmeticClientImpl.class); - } - - @After - public void cleanup() { - server.stop(); - } - @Test public void givenTwoNumbers_whenAdd_thenCorrectAnswerReturned() { String expected = Float.valueOf(10 + 20).toString(); @@ -56,6 +43,6 @@ public class ArithmeticClientUnitTest { @Test public void whenMemory_thenCorrectAnswerReturned() { String expected = "Initial:"; - assertThat(client.memory(), containsString(expected)); + assertThat(client.memory()).contains(expected); } } diff --git a/parent-spring-6/pom.xml b/parent-spring-6/pom.xml index 7b61ac524b..7b28afc9b1 100644 --- a/parent-spring-6/pom.xml +++ b/parent-spring-6/pom.xml @@ -35,7 +35,7 @@ - 6.0.10 + 6.0.12 diff --git a/persistence-modules/spring-data-shardingsphere/src/test/java/com/baeldung/shardingsphere/OrderServiceManualTest.java b/persistence-modules/spring-data-shardingsphere/src/test/java/com/baeldung/shardingsphere/OrderServiceManualTest.java new file mode 100644 index 0000000000..895dd52afb --- /dev/null +++ b/persistence-modules/spring-data-shardingsphere/src/test/java/com/baeldung/shardingsphere/OrderServiceManualTest.java @@ -0,0 +1,90 @@ +package com.baeldung.shardingsphere; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.util.List; + + +/** + * This Manual test requires: Docker service running. + */ +@Testcontainers +@SpringBootTest +class OrderServiceManualTest { + + @Container + static MySQLContainer mySQLContainer1 = new MySQLContainer<>("mysql:8.0.23") + .withDatabaseName("ds0") + .withUsername("test") + .withPassword("test"); + + @Container + static MySQLContainer mySQLContainer2 = new MySQLContainer<>("mysql:8.0.23") + .withDatabaseName("ds1") + .withUsername("test") + .withPassword("test"); + + static { + mySQLContainer2.setPortBindings(List.of("13307:3306")); + mySQLContainer1.setPortBindings(List.of("13306:3306")); + } + @Autowired + private OrderService orderService; + + @Autowired + private OrderRepository orderRepository; + + @DynamicPropertySource + static void setProperties(DynamicPropertyRegistry registry) { + registry.add("spring.jpa.hibernate.ddl-auto", () -> "create-drop"); + } + + @Test + void shouldFindOrderInCorrectShard() { + // given + Order order1 = new Order(1L, 1L, BigDecimal.TEN, Status.PROCESSING, LocalDate.now(), "123 Main St"); + Order order2 = new Order(2L, 2L, BigDecimal.valueOf(12.5), Status.SHIPPED, LocalDate.now(), "456 Main St"); + + // when + Order savedOrder1 = orderService.createOrder(order1); + Order savedOrder2 = orderService.createOrder(order2); + + // then + // Assuming the sharding strategy is based on the order id, data for order1 should be present only in ds0 + // and data for order2 should be present only in ds1 + Assertions.assertThat(orderService.getOrder(savedOrder1.getOrderId())).isEqualTo(savedOrder1); + Assertions.assertThat(orderService.getOrder(savedOrder2.getOrderId())).isEqualTo(savedOrder2); + + // Verify that the orders are not present in the wrong shards. + // You would need to implement these methods in your OrderService. + // They should use a JdbcTemplate or EntityManager to execute SQL directly against each shard. + Assertions.assertThat(assertOrderInShard(savedOrder1, mySQLContainer2)).isTrue(); + Assertions.assertThat(assertOrderInShard(savedOrder2, mySQLContainer1)).isTrue(); + } + + private boolean assertOrderInShard(Order order, MySQLContainer container) { + try (Connection conn = DriverManager.getConnection(container.getJdbcUrl(), container.getUsername(), container.getPassword())) { + PreparedStatement stmt = conn.prepareStatement("SELECT * FROM `order` WHERE order_id = ?"); + stmt.setLong(1, order.getOrderId()); + ResultSet rs = stmt.executeQuery(); + return rs.next(); + } catch (SQLException ex) { + throw new RuntimeException("Error querying order in shard", ex); + } + } +} diff --git a/spring-6-rsocket/README.md b/spring-6-rsocket/README.md index 21ab282bd1..95c805b0bf 100644 --- a/spring-6-rsocket/README.md +++ b/spring-6-rsocket/README.md @@ -3,5 +3,5 @@ This module contains articles about RSocket in Spring Framework 6. ### Relevant articles +- [RSocket Interface in Spring 6](https://www.baeldung.com/spring-rsocket) -- [Introduction to RSocket](#) \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/pom.xml b/spring-boot-modules/spring-boot-3/pom.xml index e28931243d..25927cc37b 100644 --- a/spring-boot-modules/spring-boot-3/pom.xml +++ b/spring-boot-modules/spring-boot-3/pom.xml @@ -60,7 +60,11 @@ org.junit.jupiter junit-jupiter-params ${jupiter.version} - test + + + org.junit.platform + junit-platform-commons + 1.10.0 org.mock-server @@ -273,7 +277,7 @@ 19 1.5.2.Final - 2.0.0 + 2.2.0 3.0.0-M7 com.baeldung.sample.TodoApplication 5.14.0 diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/Article.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/Article.java index a69d5989af..892cf3b5ae 100644 --- a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/Article.java +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/Article.java @@ -6,6 +6,8 @@ public class Article { Integer id; String title; + public Article() {} + public Article(Integer id, String title) { this.id = id; this.title = title; @@ -19,6 +21,14 @@ public class Article { return title; } + public void setId(Integer id) { + this.id = id; + } + + public void setTitle(String title) { + this.title = title; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleController.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleController.java index 62922bdcee..5e1dff6fd7 100644 --- a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleController.java +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleController.java @@ -1,5 +1,6 @@ package com.baeldung.restclient; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Collection; @@ -13,13 +14,21 @@ public class ArticleController { Map database = new HashMap<>(); @GetMapping - public Collection
getArticles() { - return database.values(); + public ResponseEntity> getArticles() { + Collection
values = database.values(); + if (values.isEmpty()) { + return ResponseEntity.noContent().build(); + } + return ResponseEntity.ok(values); } @GetMapping("/{id}") - public Article getArticle(@PathVariable Integer id) { - return database.get(id); + public ResponseEntity
getArticle(@PathVariable("id") Integer id) { + Article article = database.get(id); + if (article == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(article); } @PostMapping @@ -28,7 +37,7 @@ public class ArticleController { } @PutMapping("/{id}") - public void updateArticle(@PathVariable Integer id, @RequestBody Article article) { + public void updateArticle(@PathVariable("id") Integer id, @RequestBody Article article) { assert Objects.equals(id, article.getId()); database.remove(id); database.put(id, article); diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleNotFoundException.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleNotFoundException.java new file mode 100644 index 0000000000..cdd13b8330 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/ArticleNotFoundException.java @@ -0,0 +1,6 @@ +package com.baeldung.restclient; + +public class ArticleNotFoundException extends RuntimeException { + public ArticleNotFoundException() { + } +} diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/InvalidArticleResponseException.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/InvalidArticleResponseException.java new file mode 100644 index 0000000000..26ca75036e --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/InvalidArticleResponseException.java @@ -0,0 +1,6 @@ +package com.baeldung.restclient; + +public class InvalidArticleResponseException extends RuntimeException { + public InvalidArticleResponseException() { + } +} diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestClientIntegrationTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestClientIntegrationTest.java index 92474c88f0..1a615faf4e 100644 --- a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestClientIntegrationTest.java +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestClientIntegrationTest.java @@ -1,17 +1,23 @@ package com.baeldung.restclient; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestClient; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -22,7 +28,10 @@ public class RestClientIntegrationTest { private String uriBase; RestClient restClient = RestClient.create(); - @BeforeAll + @Autowired + ObjectMapper objectMapper; + + @BeforeEach public void setup() { uriBase = "http://localhost:" + port; } @@ -42,7 +51,7 @@ public class RestClientIntegrationTest { .retrieve() .body(String.class); - assertThat(articlesAsString).isEqualTo("[]"); + assertThat(articlesAsString).isEqualTo(""); } @Test @@ -63,6 +72,48 @@ public class RestClientIntegrationTest { assertThat(articles).isEqualTo(List.of(article)); } + @Test + void shouldPostAndGetArticlesWithExchange() { + assertThatThrownBy(this::getArticlesWithExchange).isInstanceOf(ArticleNotFoundException.class); + + Article article = new Article(1, "How to use RestClient"); + restClient.post() + .uri(uriBase + "/articles") + .contentType(MediaType.APPLICATION_JSON) + .body(article) + .retrieve() + .toBodilessEntity(); + + List
articles = getArticlesWithExchange(); + + assertThat(articles).isEqualTo(List.of(article)); + } + + private List
getArticlesWithExchange() { + return restClient.get() + .uri(uriBase + "/articles") + .exchange((request, response) -> { + if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) { + throw new ArticleNotFoundException(); + } else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) { + return objectMapper.readValue(response.getBody(), new TypeReference<>() {}); + } else { + throw new InvalidArticleResponseException(); + } + }); + } + + @Test + void shouldPostAndGetArticlesWithErrorHandling() { + assertThatThrownBy(() -> { + restClient.get() + .uri(uriBase + "/articles/1234") + .retrieve() + .onStatus(status -> status.value() == 404, (request, response) -> { throw new ArticleNotFoundException(); }) + .body(new ParameterizedTypeReference<>() {}); + }).isInstanceOf(ArticleNotFoundException.class); + } + @Test void shouldPostAndPutAndGetArticles() { Article article = new Article(1, "How to use RestClient"); @@ -79,7 +130,7 @@ public class RestClientIntegrationTest { .contentType(MediaType.APPLICATION_JSON) .body(articleChanged) .retrieve() - .toBodilessEntity(); + .toBodilessEntity(); List
articles = restClient.get() .uri(uriBase + "/articles") @@ -104,11 +155,12 @@ public class RestClientIntegrationTest { .retrieve() .toBodilessEntity(); - List
articles = restClient.get() + ResponseEntity entity = restClient.get() .uri(uriBase + "/articles") + .accept(MediaType.APPLICATION_JSON) .retrieve() - .body(new ParameterizedTypeReference<>() {}); + .toBodilessEntity(); - assertThat(articles).isEqualTo(List.of()); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(204)); } } diff --git a/spring-boot-modules/spring-boot-actuator/pom.xml b/spring-boot-modules/spring-boot-actuator/pom.xml index 1ccf436bbf..7f630fa96e 100644 --- a/spring-boot-modules/spring-boot-actuator/pom.xml +++ b/spring-boot-modules/spring-boot-actuator/pom.xml @@ -9,9 +9,10 @@ This is simple boot application for Spring boot actuator test - com.baeldung.spring-boot-modules - spring-boot-modules - 1.0.0-SNAPSHOT + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 @@ -39,16 +40,6 @@ com.h2database h2 - - javax.servlet - javax.servlet-api - provided - - - javax.servlet - jstl - runtime - org.springframework.boot spring-boot-starter-test diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java index 894c24693e..20ee834d52 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java +++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/enabling/SecurityConfiguration.java @@ -1,36 +1,56 @@ package com.baeldung.endpoints.enabling; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; + import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.web.servlet.handler.HandlerMappingIntrospector; -@Configuration @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +@Configuration +public class SecurityConfiguration { - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - auth.inMemoryAuthentication() - .withUser("user") - .password(encoder.encode("password")) - .roles("USER") - .and() - .withUser("admin") - .password(encoder.encode("admin")) - .roles("USER", "ADMIN"); + @Bean + MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { + return new MvcRequestMatcher.Builder(introspector); } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.requestMatcher(EndpointRequest.toAnyEndpoint()) - .authorizeRequests((requests) -> requests.anyRequest() - .hasRole("ADMIN")); - http.httpBasic(); + @Bean + public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception { + http.httpBasic(Customizer.withDefaults()); + http.securityMatcher(EndpointRequest.toAnyEndpoint()); + http.authorizeHttpRequests(authz -> { + authz.requestMatchers(mvc.pattern("/actuator/**")) + .hasRole("ADMIN") + .anyRequest() + .authenticated(); + }); + + return http.build(); + } + + + + @Bean + public InMemoryUserDetailsManager userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build(); + UserDetails admin = User.withDefaultPasswordEncoder() + .username("admin") + .password("password") + .roles("USER", "ADMIN") + .build(); + return new InMemoryUserDetailsManager(user, admin); } } diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java index db4e69127a..88f39d8ccc 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java +++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/endpoints/info/User.java @@ -1,9 +1,9 @@ package com.baeldung.endpoints.info; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; @Entity @Table(name = "users") diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java index 729b3c0b96..ee87412986 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java +++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/MetricsApplication.java @@ -15,7 +15,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.context.request.RequestContextListener; -import javax.servlet.ServletContext; +import jakarta.servlet.ServletContext; @EnableScheduling @ComponentScan("com.baeldung.metrics") diff --git a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java index 0f7579f060..a7aaddf0fb 100644 --- a/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java +++ b/spring-boot-modules/spring-boot-actuator/src/main/java/com/baeldung/metrics/filter/MetricFilter.java @@ -7,14 +7,14 @@ import org.springframework.stereotype.Component; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; @Component public class MetricFilter implements Filter { diff --git a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java index 8274619517..079195714b 100644 --- a/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java +++ b/spring-boot-modules/spring-boot-actuator/src/test/java/com/baeldung/endpoints/enabling/EndpointEnablingIntegrationTest.java @@ -13,20 +13,20 @@ import org.springframework.test.web.servlet.MockMvc; @SpringBootTest @AutoConfigureMockMvc -public class EndpointEnablingIntegrationTest { +class EndpointEnablingIntegrationTest { @Autowired private MockMvc mockMvc; @Test @WithMockUser(username = "user", password = "password", roles = "USER") - public void givenWrongAuthentication_whenCallingActuator_thenReturns401() throws Exception { + void givenWrongAuthentication_whenCallingActuator_thenReturns401() throws Exception { mockMvc.perform(get("/actuator")) .andExpect(status().isForbidden()); } @Test @WithMockUser(username = "admin", password = "admin", roles = "ADMIN") - public void givenProperAuthentication_whenCallingActuator_thenReturnsExpectedEndpoints() throws Exception { + void givenProperAuthentication_whenCallingActuator_thenReturnsExpectedEndpoints() throws Exception { mockMvc.perform(get("/actuator")) .andExpect(jsonPath("$._links").exists()) .andExpect(jsonPath("$._links.beans").exists()) diff --git a/spring-boot-modules/spring-boot-bootstrap/pom.xml b/spring-boot-modules/spring-boot-bootstrap/pom.xml index 4ceae26f60..da16f79a2a 100644 --- a/spring-boot-modules/spring-boot-bootstrap/pom.xml +++ b/spring-boot-modules/spring-boot-bootstrap/pom.xml @@ -9,9 +9,10 @@ Demo project for Spring Boot - com.baeldung.spring-boot-modules - spring-boot-modules - 1.0.0-SNAPSHOT + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 @@ -47,13 +48,12 @@ io.rest-assured rest-assured - ${rest-assured.version} test - javax.servlet - javax.servlet-api - ${servlet.version} + org.springframework.boot + spring-boot-starter-cloud-connectors + ${spring-boot-cloud-connectors.version} @@ -332,6 +332,7 @@ 4.0.0 Greenwich.RELEASE 1.0.0.RELEASE + 2.2.13.RELEASE \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/config/SecurityConfig.java b/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/config/SecurityConfig.java index ecb21cdf4b..c876e1c3d4 100644 --- a/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/config/SecurityConfig.java +++ b/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/config/SecurityConfig.java @@ -4,6 +4,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; @Configuration @@ -12,12 +13,11 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.authorizeRequests() - .anyRequest() - .permitAll() - .and() - .csrf() - .disable(); + http.authorizeHttpRequests(expressionInterceptUrlRegistry -> + expressionInterceptUrlRegistry + .anyRequest() + .permitAll()) + .csrf(AbstractHttpConfigurer::disable); return http.build(); } } diff --git a/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/persistence/model/Book.java b/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/persistence/model/Book.java index 6be27d4cf0..d599c64567 100644 --- a/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/persistence/model/Book.java +++ b/spring-boot-modules/spring-boot-bootstrap/src/main/java/com/baeldung/persistence/model/Book.java @@ -1,10 +1,10 @@ package com.baeldung.persistence.model; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; @Entity public class Book { diff --git a/spring-boot-modules/spring-boot-documentation/pom.xml b/spring-boot-modules/spring-boot-documentation/pom.xml index f0806b3c75..3ac2799258 100644 --- a/spring-boot-modules/spring-boot-documentation/pom.xml +++ b/spring-boot-modules/spring-boot-documentation/pom.xml @@ -93,8 +93,8 @@ 2.2.11 - 0.12.1 - 0.8.0 + 0.14.0 + 0.14.0 1.18.3 diff --git a/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json b/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json index 0198733c1c..45be27f571 100644 --- a/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json +++ b/spring-boot-modules/spring-boot-documentation/src/test/resources/asyncapi.json @@ -18,7 +18,9 @@ "operationId": "incoming-topic_publish", "description": "More details for the incoming topic", "bindings": { - "kafka": { } + "kafka": { + "bindingVersion": "0.4.0" + } }, "message": { "schemaFormat": "application/vnd.oai.openapi+json;version=3.0.0", @@ -32,7 +34,9 @@ "$ref": "#/components/schemas/SpringKafkaDefaultHeadersIncomingPayloadDto" }, "bindings": { - "kafka": { } + "kafka": { + "bindingVersion": "0.4.0" + } } } } @@ -42,7 +46,9 @@ "operationId": "outgoing-topic_subscribe", "description": "More details for the outgoing topic", "bindings": { - "kafka": { } + "kafka": { + "bindingVersion": "0.4.0" + } }, "message": { "schemaFormat": "application/vnd.oai.openapi+json;version=3.0.0", @@ -56,7 +62,9 @@ "$ref": "#/components/schemas/SpringKafkaDefaultHeadersOutgoingPayloadDto" }, "bindings": { - "kafka": { } + "kafka": { + "bindingVersion": "0.4.0" + } } } } diff --git a/spring-boot-modules/spring-boot-graalvm-docker/README.md b/spring-boot-modules/spring-boot-graalvm-docker/README.md new file mode 100644 index 0000000000..10a764053e --- /dev/null +++ b/spring-boot-modules/spring-boot-graalvm-docker/README.md @@ -0,0 +1,2 @@ +## Relevant Articles +- [Create a GraalVM Docker Image](https://www.baeldung.com/java-graalvm-docker-image) diff --git a/spring-boot-modules/spring-boot-mvc-4/pom.xml b/spring-boot-modules/spring-boot-mvc-4/pom.xml index dbbb03ad0e..0ae05a764b 100644 --- a/spring-boot-modules/spring-boot-mvc-4/pom.xml +++ b/spring-boot-modules/spring-boot-mvc-4/pom.xml @@ -9,9 +9,10 @@ Module For Spring Boot MVC Web - com.baeldung.spring-boot-modules - spring-boot-modules - 1.0.0-SNAPSHOT + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3 diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java index 80c75aa8b5..cedc60bd92 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/HelloWorldServlet.java @@ -4,10 +4,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { private static final long serialVersionUID = 1L; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java index f276f94b7c..dbb8ea1b32 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/boot/controller/servlet/SpringHelloWorldServlet.java @@ -4,10 +4,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; public class SpringHelloWorldServlet extends HttpServlet { private static final long serialVersionUID = 1L; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java index 3f51a4ab69..b2d6f249a5 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/common/error/SpringHelloServletRegistrationBean.java @@ -2,7 +2,7 @@ package com.baeldung.common.error; import org.springframework.boot.web.servlet.ServletRegistrationBean; -import javax.servlet.Servlet; +import jakarta.servlet.Servlet; public class SpringHelloServletRegistrationBean extends ServletRegistrationBean { diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java index eadd40355a..7fd83fd59a 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/configuration/WebAppInitializer.java @@ -4,9 +4,9 @@ import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRegistration; public class WebAppInitializer implements WebApplicationInitializer { diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java index 49dd9404b7..ee70a142be 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/GenericCustomServlet.java @@ -1,9 +1,9 @@ package com.baeldung.servlets.servlets; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java index 992976ca0e..62706be348 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/AnnotationServlet.java @@ -1,10 +1,10 @@ package com.baeldung.servlets.servlets.javaee; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "AnnotationServlet", description = "Example Servlet Using Annotations", urlPatterns = { "/annotationservlet" }) diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java index c7b373064f..299a1b5d95 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/servlets/servlets/javaee/EEWebXmlServlet.java @@ -1,9 +1,9 @@ package com.baeldung.servlets.servlets.javaee; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java index ce3eae7ce0..eab5869cd5 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/UtilsApplication.java @@ -1,6 +1,6 @@ package com.baeldung.utils; -import javax.annotation.security.RolesAllowed; +import jakarta.annotation.security.RolesAllowed; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java index 8c7f2f932a..40acc1801f 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/main/java/com/baeldung/utils/controller/UtilsController.java @@ -1,6 +1,6 @@ package com.baeldung.utils.controller; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; diff --git a/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java b/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java index 080f660c40..f59d00f871 100644 --- a/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java +++ b/spring-boot-modules/spring-boot-mvc-4/src/test/java/com/baeldung/utils/UtilsControllerIntegrationTest.java @@ -1,32 +1,34 @@ package com.baeldung.utils; import com.baeldung.utils.controller.UtilsController; -import org.junit.Before; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; -import org.mockito.MockitoAnnotations; + import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import static org.mockito.MockitoAnnotations.openMocks; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class UtilsControllerIntegrationTest { +class UtilsControllerIntegrationTest { @InjectMocks private UtilsController utilsController; private MockMvc mockMvc; - @Before + @BeforeEach public void setup() { - MockitoAnnotations.initMocks(this); + openMocks(this); this.mockMvc = MockMvcBuilders.standaloneSetup(utilsController).build(); } @Test - public void givenParameter_setRequestParam_andSetSessionAttribute() throws Exception { + void givenParameter_setRequestParam_andSetSessionAttribute() throws Exception { String param = "testparam"; this.mockMvc.perform(post("/setParam").param("param", param).sessionAttr("parameter", param)).andExpect(status().isOk()); } diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java new file mode 100644 index 0000000000..fd321a0e68 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/ExternalAlertService.java @@ -0,0 +1,6 @@ +package com.baeldung.spytest; + +public interface ExternalAlertService { + public boolean alert(Order order); + +} diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java new file mode 100644 index 0000000000..f0b2f92bee --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/NotificationService.java @@ -0,0 +1,18 @@ +package com.baeldung.spytest; + +import org.springframework.stereotype.Component; + +@Component +public class NotificationService { + + private ExternalAlertService externalAlertService; + + public void notify(Order order) { + System.out.println(order); + } + + public boolean raiseAlert(Order order) { + return externalAlertService.alert(order); + } + +} diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java new file mode 100644 index 0000000000..23f5a05e9d --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/Order.java @@ -0,0 +1,49 @@ +package com.baeldung.spytest; + +import java.util.UUID; + +public class Order { + + private UUID id; + + private String name; + + private OrderType orderType; + + private double orderQuantity; + + private String address; + + public Order(UUID id, String name, double orderQuantity, String address) { + this.id = id; + this.name = name; + this.orderQuantity = orderQuantity; + this.address = address; + } + + public enum OrderType { + INDIVIDUAL, BULK; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + + public double getOrderQuantity() { + return orderQuantity; + } + + public String getAddress() { + return address; + } +} + + diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java new file mode 100644 index 0000000000..90fe5ba2b8 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderRepository.java @@ -0,0 +1,19 @@ +package com.baeldung.spytest; + +import java.util.HashMap; +import java.util.UUID; + +import org.springframework.stereotype.Component; + +@Component +public class OrderRepository { + + public static final HashMap orders = new HashMap<>(); + + public Order save(Order order) { + UUID orderId = UUID.randomUUID(); + order.setId(orderId); + orders.put(UUID.randomUUID(), order); + return order; + } +} diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java new file mode 100644 index 0000000000..47647b8721 --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/OrderService.java @@ -0,0 +1,25 @@ +package com.baeldung.spytest; + +import org.springframework.stereotype.Service; + +@Service +public class OrderService { + + public final OrderRepository orderRepository; + + public final NotificationService notificationService; + + public OrderService(OrderRepository orderRepository, NotificationService notificationService) { + this.orderRepository = orderRepository; + this.notificationService = notificationService; + } + + public Order save(Order order) { + order = orderRepository.save(order); + notificationService.notify(order); + if (!notificationService.raiseAlert(order)) { + throw new RuntimeException("Alert not raised"); + } + return order; + } +} diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java new file mode 100644 index 0000000000..d8d81378df --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/spytest/SpyTestApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.spytest; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpyTestApplication { + + public static void main(String[] args) { + SpringApplication.run(SpyTestApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java new file mode 100644 index 0000000000..96c7b09dfc --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceIntegrationTest.java @@ -0,0 +1,35 @@ +package com.baeldung.spytest; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; + +@SpringBootTest +class OrderServiceIntegrationTest { + + @Autowired + OrderRepository orderRepository; + @SpyBean + NotificationService notificationService; + @SpyBean + OrderService orderService; + + @Test + void givenNotificationServiceIsUsingSpyBean_whenOrderServiceIsCalled_thenNotificationServiceSpyBeanShouldBeInvoked() { + + Order orderInput = new Order(null, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP"); + doReturn(true).when(notificationService) + .raiseAlert(any(Order.class)); + Order order = orderService.save(orderInput); + Assertions.assertNotNull(order); + Assertions.assertNotNull(order.getId()); + verify(notificationService).notify(any(Order.class)); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java new file mode 100644 index 0000000000..21ae849bcf --- /dev/null +++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/spytest/OrderServiceUnitTest.java @@ -0,0 +1,41 @@ +package com.baeldung.spytest; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import java.util.UUID; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +class OrderServiceUnitTest { + + @Spy + OrderRepository orderRepository; + @Spy + NotificationService notificationService; + @InjectMocks + OrderService orderService; + + @Test + void givenNotificationServiceIsUsingSpy_whenOrderServiceIsCalled_thenNotificationServiceSpyShouldBeInvoked() { + + UUID orderId = UUID.randomUUID(); + Order orderInput = new Order(orderId, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP"); + doReturn(orderInput).when(orderRepository) + .save(any()); + doReturn(true).when(notificationService) + .raiseAlert(any(Order.class)); + Order order = orderService.save(orderInput); + Assertions.assertNotNull(order); + Assertions.assertEquals(orderId, order.getId()); + verify(notificationService).notify(any(Order.class)); + } + +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md new file mode 100644 index 0000000000..b76ae19f26 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/README.md @@ -0,0 +1,7 @@ +## Spring Cloud Gateway + +This module contains articles about Spring Cloud Gateway + +### Relevant Articles: + +- [Exploring the New Spring Cloud Gateway](http://www.baeldung.com/spring-cloud-gateway) \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml new file mode 100644 index 0000000000..3d64edc338 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + gateway-2 + gateway-2 + jar + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../../parent-boot-2 + + + + + + org.junit + junit-bom + ${junit-jupiter.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + 2021.0.3 + + + \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java new file mode 100644 index 0000000000..e209b6cdf0 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.springcloudgateway.custompredicates; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class CustomPredicatesApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder(CustomPredicatesApplication.class) + .profiles("customroutes") + .run(args); + } + +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java new file mode 100644 index 0000000000..ea58eb7e46 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/config/CustomPredicatesConfig.java @@ -0,0 +1,43 @@ +package com.baeldung.springcloudgateway.custompredicates.config; + +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.baeldung.springcloudgateway.custompredicates.factories.GoldenCustomerRoutePredicateFactory; +import com.baeldung.springcloudgateway.custompredicates.factories.GoldenCustomerRoutePredicateFactory.Config; +import com.baeldung.springcloudgateway.custompredicates.service.GoldenCustomerService; + +@Configuration +public class CustomPredicatesConfig { + + + @Bean + public GoldenCustomerRoutePredicateFactory goldenCustomer(GoldenCustomerService goldenCustomerService) { + return new GoldenCustomerRoutePredicateFactory(goldenCustomerService); + } + + + //@Bean + public RouteLocator routes(RouteLocatorBuilder builder, GoldenCustomerRoutePredicateFactory gf ) { + + return builder.routes() + .route("dsl_golden_route", r -> + r.predicate(gf.apply(new Config(true, "customerId"))) + .and() + .path("/dsl_api/**") + .filters(f -> f.stripPrefix(1)) + .uri("https://httpbin.org") + ) + .route("dsl_common_route", r -> + r.predicate(gf.apply(new Config(false, "customerId"))) + .and() + .path("/dsl_api/**") + .filters(f -> f.stripPrefix(1)) + .uri("https://httpbin.org") + ) + .build(); + } + +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java new file mode 100644 index 0000000000..cb5c3a0b50 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/factories/GoldenCustomerRoutePredicateFactory.java @@ -0,0 +1,102 @@ +/** + * + */ +package com.baeldung.springcloudgateway.custompredicates.factories; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import javax.validation.constraints.NotEmpty; + +import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; +import org.springframework.http.HttpCookie; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.server.ServerWebExchange; + +import com.baeldung.springcloudgateway.custompredicates.service.GoldenCustomerService; + +/** + * @author Philippe + * + */ +public class GoldenCustomerRoutePredicateFactory extends AbstractRoutePredicateFactory { + + private final GoldenCustomerService goldenCustomerService; + + public GoldenCustomerRoutePredicateFactory(GoldenCustomerService goldenCustomerService ) { + super(Config.class); + this.goldenCustomerService = goldenCustomerService; + } + + + @Override + public List shortcutFieldOrder() { + return Arrays.asList("isGolden","customerIdCookie"); + } + + + @Override + public Predicate apply(Config config) { + + return (ServerWebExchange t) -> { + List cookies = t.getRequest() + .getCookies() + .get(config.getCustomerIdCookie()); + + boolean isGolden; + if ( cookies == null || cookies.isEmpty()) { + isGolden = false; + } + else { + String customerId = cookies.get(0).getValue(); + isGolden = goldenCustomerService.isGoldenCustomer(customerId); + } + + return config.isGolden()?isGolden:!isGolden; + }; + } + + + @Validated + public static class Config { + boolean isGolden = true; + + @NotEmpty + String customerIdCookie = "customerId"; + + + public Config() {} + + public Config( boolean isGolden, String customerIdCookie) { + this.isGolden = isGolden; + this.customerIdCookie = customerIdCookie; + } + + public boolean isGolden() { + return isGolden; + } + + public void setGolden(boolean value) { + this.isGolden = value; + } + + /** + * @return the customerIdCookie + */ + public String getCustomerIdCookie() { + return customerIdCookie; + } + + /** + * @param customerIdCookie the customerIdCookie to set + */ + public void setCustomerIdCookie(String customerIdCookie) { + this.customerIdCookie = customerIdCookie; + } + + + + } + +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java new file mode 100644 index 0000000000..82bf2e6ae9 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/custompredicates/service/GoldenCustomerService.java @@ -0,0 +1,26 @@ +/** + * + */ +package com.baeldung.springcloudgateway.custompredicates.service; + +import org.springframework.stereotype.Component; + +/** + * @author Philippe + * + */ +@Component +public class GoldenCustomerService { + + public boolean isGoldenCustomer(String customerId) { + + // TODO: Add some AI logic to check is this customer deserves a "golden" status ;^) + if ( "baeldung".equalsIgnoreCase(customerId)) { + return true; + } + else { + return false; + } + } + +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java new file mode 100644 index 0000000000..d276597a6b --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/java/com/baeldung/springcloudgateway/introduction/IntroductionGatewayApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.springcloudgateway.introduction; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@PropertySource("classpath:introduction-application.properties") +public class IntroductionGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(IntroductionGatewayApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml new file mode 100644 index 0000000000..859aa60bda --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application-customroutes.yml @@ -0,0 +1,26 @@ +spring: + cloud: + gateway: + routes: + - id: golden_route + uri: https://httpbin.org + predicates: + - Path=/api/** + - GoldenCustomer=true + filters: + - StripPrefix=1 + - AddRequestHeader=GoldenCustomer,true + - id: common_route + uri: https://httpbin.org + predicates: + - Path=/api/** + - name: GoldenCustomer + args: + golden: false + customerIdCookie: customerId + filters: + - StripPrefix=1 + - AddRequestHeader=GoldenCustomer,false + + + \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml new file mode 100644 index 0000000000..a33bca2055 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/application.yml @@ -0,0 +1,4 @@ +logging: + level: + org.springframework.cloud.gateway: DEBUG + reactor.netty.http.client: DEBUG diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties new file mode 100644 index 0000000000..d7a6c4e072 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/introduction-application.properties @@ -0,0 +1,7 @@ +spring.cloud.gateway.routes[0].id=baeldung_route +spring.cloud.gateway.routes[0].uri=http://www.baeldung.com +spring.cloud.gateway.routes[0].predicates[0]=Path=/baeldung + +management.endpoints.web.exposure.include=* + +server.port=80 diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/logback.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java new file mode 100644 index 0000000000..d9988ceb5e --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/custompredicates/CustomPredicatesApplicationLiveTest.java @@ -0,0 +1,67 @@ +package com.baeldung.springcloudgateway.custompredicates; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +import java.net.URI; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.Before; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ActiveProfiles; + +/** + * This test requires + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles("customroutes") +public class CustomPredicatesApplicationLiveTest { + + @LocalServerPort + String serverPort; + + @Autowired + private TestRestTemplate restTemplate; + + @Test + void givenNormalCustomer_whenCallHeadersApi_thenResponseForNormalCustomer() throws JSONException { + + String url = "http://localhost:" + serverPort + "/api/headers"; + ResponseEntity response = restTemplate.getForEntity(url, String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + + JSONObject json = new JSONObject(response.getBody()); + JSONObject headers = json.getJSONObject("headers"); + assertThat(headers.getString("Goldencustomer")).isEqualTo("false"); + + } + + @Test + void givenGoldenCustomer_whenCallHeadersApi_thenResponseForGoldenCustomer() throws JSONException { + + String url = "http://localhost:" + serverPort + "/api/headers"; + RequestEntity request = RequestEntity + .get(URI.create(url)) + .header("Cookie", "customerId=baeldung") + .build(); + + ResponseEntity response = restTemplate.exchange(request, String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + + JSONObject json = new JSONObject(response.getBody()); + JSONObject headers = json.getJSONObject("headers"); + assertThat(headers.getString("Goldencustomer")).isEqualTo("true"); + + } + +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java new file mode 100644 index 0000000000..33855cd15d --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/LoggerListAppender.java @@ -0,0 +1,25 @@ +package com.baeldung.springcloudgateway.introduction; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +public class LoggerListAppender extends AppenderBase { + + static private List events = new ArrayList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + events.add(eventObject); + } + + public static List getEvents() { + return events; + } + + public static void clearEventList() { + events.clear(); + } +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java new file mode 100644 index 0000000000..1550265f22 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/java/com/baeldung/springcloudgateway/introduction/SpringContextTest.java @@ -0,0 +1,15 @@ +package com.baeldung.springcloudgateway.introduction; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import com.baeldung.springcloudgateway.introduction.IntroductionGatewayApplication; + + +@SpringBootTest(classes = IntroductionGatewayApplication.class) +public class SpringContextTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..90c8f570aa --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway-2/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml index e7fe7e7485..f4d4073ef1 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml @@ -18,6 +18,7 @@ config discovery gateway + gateway-2 svc-book svc-rating customer-service diff --git a/spring-core/src/main/java/com/baeldung/config/scope/AppConfigFunctionBean.java b/spring-core/src/main/java/com/baeldung/config/scope/AppConfigFunctionBean.java index a3c5445698..aa526b5403 100644 --- a/spring-core/src/main/java/com/baeldung/config/scope/AppConfigFunctionBean.java +++ b/spring-core/src/main/java/com/baeldung/config/scope/AppConfigFunctionBean.java @@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonFunctionBean; +import com.baeldung.scope.singleton.SingletonFunctionBean; @Configuration public class AppConfigFunctionBean { diff --git a/spring-core/src/main/java/com/baeldung/scope/AppConfig.java b/spring-core/src/main/java/com/baeldung/scope/AppConfig.java index 33a9c5c21e..2ef775ee4f 100644 --- a/spring-core/src/main/java/com/baeldung/scope/AppConfig.java +++ b/spring-core/src/main/java/com/baeldung/scope/AppConfig.java @@ -1,10 +1,10 @@ package com.baeldung.scope; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonAppContextBean; -import com.baeldung.scope.singletone.SingletonBean; -import com.baeldung.scope.singletone.SingletonObjectFactoryBean; -import com.baeldung.scope.singletone.SingletonProviderBean; +import com.baeldung.scope.singleton.SingletonAppContextBean; +import com.baeldung.scope.singleton.SingletonBean; +import com.baeldung.scope.singleton.SingletonObjectFactoryBean; +import com.baeldung.scope.singleton.SingletonProviderBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; diff --git a/spring-core/src/main/java/com/baeldung/scope/AppProxyScopeConfig.java b/spring-core/src/main/java/com/baeldung/scope/AppProxyScopeConfig.java index 9f1874375e..0564dfb3c0 100644 --- a/spring-core/src/main/java/com/baeldung/scope/AppProxyScopeConfig.java +++ b/spring-core/src/main/java/com/baeldung/scope/AppProxyScopeConfig.java @@ -1,7 +1,7 @@ package com.baeldung.scope; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonBean; +import com.baeldung.scope.singleton.SingletonBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.*; diff --git a/spring-core/src/main/java/com/baeldung/scope/BeanInjectionStarter.java b/spring-core/src/main/java/com/baeldung/scope/BeanInjectionStarter.java index 5cf0c9170c..47014aa2d1 100644 --- a/spring-core/src/main/java/com/baeldung/scope/BeanInjectionStarter.java +++ b/spring-core/src/main/java/com/baeldung/scope/BeanInjectionStarter.java @@ -1,7 +1,7 @@ package com.baeldung.scope; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonBean; +import com.baeldung.scope.singleton.SingletonBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.util.Assert; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonAppContextBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonAppContextBean.java similarity index 94% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonAppContextBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonAppContextBean.java index f4d57a0f63..4f5b3274c8 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonAppContextBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonAppContextBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import com.baeldung.scope.prototype.PrototypeBean; import org.springframework.beans.BeansException; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonBean.java similarity index 93% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonBean.java index 8d3a09b8fd..9c4cea4439 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import com.baeldung.scope.prototype.PrototypeBean; import org.apache.log4j.Logger; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonFunctionBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonFunctionBean.java similarity index 91% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonFunctionBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonFunctionBean.java index 8cdc56a6fa..2788af1701 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonFunctionBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonFunctionBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import java.util.function.Function; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonLookupBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonLookupBean.java similarity index 88% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonLookupBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonLookupBean.java index 4c3c9b69da..e5461826ef 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonLookupBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonLookupBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import com.baeldung.scope.prototype.PrototypeBean; import org.springframework.beans.factory.annotation.Lookup; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonObjectFactoryBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonObjectFactoryBean.java similarity index 91% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonObjectFactoryBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonObjectFactoryBean.java index 55a91f8202..0e70d12e6e 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonObjectFactoryBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonObjectFactoryBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import com.baeldung.scope.prototype.PrototypeBean; import org.springframework.beans.factory.ObjectFactory; diff --git a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonProviderBean.java b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonProviderBean.java similarity index 90% rename from spring-core/src/main/java/com/baeldung/scope/singletone/SingletonProviderBean.java rename to spring-core/src/main/java/com/baeldung/scope/singleton/SingletonProviderBean.java index 37d0ad9404..01a4a0ff11 100644 --- a/spring-core/src/main/java/com/baeldung/scope/singletone/SingletonProviderBean.java +++ b/spring-core/src/main/java/com/baeldung/scope/singleton/SingletonProviderBean.java @@ -1,4 +1,4 @@ -package com.baeldung.scope.singletone; +package com.baeldung.scope.singleton; import com.baeldung.scope.prototype.PrototypeBean; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-core/src/test/java/com/baeldung/scope/PrototypeBeanInjectionIntegrationTest.java b/spring-core/src/test/java/com/baeldung/scope/PrototypeBeanInjectionIntegrationTest.java index d0c2733765..df04957992 100644 --- a/spring-core/src/test/java/com/baeldung/scope/PrototypeBeanInjectionIntegrationTest.java +++ b/spring-core/src/test/java/com/baeldung/scope/PrototypeBeanInjectionIntegrationTest.java @@ -1,10 +1,9 @@ package com.baeldung.scope; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonFunctionBean; -import com.baeldung.scope.singletone.SingletonLookupBean; -import com.baeldung.scope.singletone.SingletonObjectFactoryBean; -import com.baeldung.scope.singletone.SingletonProviderBean; +import com.baeldung.scope.singleton.SingletonLookupBean; +import com.baeldung.scope.singleton.SingletonObjectFactoryBean; +import com.baeldung.scope.singleton.SingletonProviderBean; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-core/src/test/java/com/baeldung/scope/PrototypeFunctionBeanIntegrationTest.java b/spring-core/src/test/java/com/baeldung/scope/PrototypeFunctionBeanIntegrationTest.java index 1e3c652599..dac3461ceb 100644 --- a/spring-core/src/test/java/com/baeldung/scope/PrototypeFunctionBeanIntegrationTest.java +++ b/spring-core/src/test/java/com/baeldung/scope/PrototypeFunctionBeanIntegrationTest.java @@ -11,7 +11,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.baeldung.config.scope.AppConfigFunctionBean; import com.baeldung.scope.prototype.PrototypeBean; -import com.baeldung.scope.singletone.SingletonFunctionBean; +import com.baeldung.scope.singleton.SingletonFunctionBean; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = AppConfigFunctionBean.class) diff --git a/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaConsumerConfig.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaConsumerConfig.java new file mode 100644 index 0000000000..741fb6bba4 --- /dev/null +++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaConsumerConfig.java @@ -0,0 +1,36 @@ +package com.baeldung.spring.kafka.multipletopics; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +@Configuration +public class KafkaConsumerConfig { + + @Value(value = "${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + @Bean + public ConsumerFactory consumerFactory() { + Map config = new HashMap<>(); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + + return new DefaultKafkaConsumerFactory<>(config, new StringDeserializer(), new JsonDeserializer<>(PaymentData.class)); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } +} \ No newline at end of file diff --git a/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsApplication.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsApplication.java new file mode 100644 index 0000000000..2135a27f39 --- /dev/null +++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.spring.kafka.multipletopics; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.kafka.annotation.EnableKafka; + +@EnableKafka +@SpringBootApplication +public class KafkaMultipleTopicsApplication { + public static void main(String[] args) { + SpringApplication.run(KafkaMultipleTopicsApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaProducerConfig.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaProducerConfig.java new file mode 100644 index 0000000000..2cb0117bf1 --- /dev/null +++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/KafkaProducerConfig.java @@ -0,0 +1,34 @@ +package com.baeldung.spring.kafka.multipletopics; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; + +@Configuration +public class KafkaProducerConfig { + + @Value(value = "${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + @Bean + public ProducerFactory producerFactory() { + Map config = new HashMap<>(); + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + + return new DefaultKafkaProducerFactory<>(config, new StringSerializer(), new JsonSerializer<>()); + } + + @Bean + public KafkaTemplate kafkaProducer() { + return new KafkaTemplate<>(producerFactory()); + } +} \ No newline at end of file diff --git a/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentData.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentData.java new file mode 100644 index 0000000000..e81138c089 --- /dev/null +++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentData.java @@ -0,0 +1,54 @@ +package com.baeldung.spring.kafka.multipletopics; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.StringJoiner; + +public class PaymentData { + private String paymentReference; + private String type; + private BigDecimal amount; + private Currency currency; + + public String getPaymentReference() { + return paymentReference; + } + + public void setPaymentReference(String paymentReference) { + this.paymentReference = paymentReference; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public Currency getCurrency() { + return currency; + } + + public void setCurrency(Currency currency) { + this.currency = currency; + } + + @Override + public String toString() { + return new StringJoiner(", ", PaymentData.class.getSimpleName() + "[", "]") + .add("paymentReference='" + paymentReference + "'") + .add("type='" + type + "'") + .add("amount=" + amount) + .add("currency=" + currency) + .toString(); + } +} \ No newline at end of file diff --git a/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentDataListener.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentDataListener.java new file mode 100644 index 0000000000..fb640cca25 --- /dev/null +++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/multipletopics/PaymentDataListener.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.kafka.multipletopics; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Service; + +@Service +public class PaymentDataListener { + private final Logger log = LoggerFactory.getLogger(PaymentDataListener.class); + + @KafkaListener(topics = { "card-payments", "bank-transfers" }, groupId = "payments") + public void handlePaymentEvents(PaymentData paymentData, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + log.info("Event on topic={}, payload={}", topic, paymentData); + } +} \ No newline at end of file diff --git a/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsIntegrationTest.java b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsIntegrationTest.java new file mode 100644 index 0000000000..345e84b65b --- /dev/null +++ b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/multipletopics/KafkaMultipleTopicsIntegrationTest.java @@ -0,0 +1,78 @@ +package com.baeldung.spring.kafka.multipletopics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.kafka.config.KafkaListenerEndpointRegistry; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.listener.MessageListenerContainer; +import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.kafka.test.utils.ContainerTestUtils; + +@SpringBootTest(classes = KafkaMultipleTopicsApplication.class) +@EmbeddedKafka(partitions = 1, brokerProperties = { "listeners=PLAINTEXT://localhost:9092", "port=9092" }) +public class KafkaMultipleTopicsIntegrationTest { + private static final String CARD_PAYMENTS_TOPIC = "card-payments"; + private static final String BANK_TRANSFERS_TOPIC = "bank-transfers"; + + @Autowired + private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry; + + @Autowired + private KafkaTemplate kafkaProducer; + + @SpyBean + private PaymentDataListener paymentsConsumer; + + @BeforeEach + void setUp() { + // wait for embedded Kafka + for (MessageListenerContainer messageListenerContainer : kafkaListenerEndpointRegistry.getListenerContainers()) { + ContainerTestUtils.waitForAssignment(messageListenerContainer, 2); + } + } + + @Test + public void whenSendingMessagesOnTwoTopics_thenConsumerReceivesMessages() throws Exception { + CountDownLatch countDownLatch = new CountDownLatch(2); + doAnswer(invocation -> { + countDownLatch.countDown(); + return null; + }).when(paymentsConsumer) + .handlePaymentEvents(any(), any()); + + kafkaProducer.send(CARD_PAYMENTS_TOPIC, createCardPayment()); + kafkaProducer.send(BANK_TRANSFERS_TOPIC, createBankTransfer()); + + assertThat(countDownLatch.await(5, TimeUnit.SECONDS)).isTrue(); + } + + private PaymentData createCardPayment() { + PaymentData cardPayment = new PaymentData(); + cardPayment.setAmount(BigDecimal.valueOf(275)); + cardPayment.setPaymentReference("A184028KM0013790"); + cardPayment.setCurrency(Currency.getInstance("GBP")); + cardPayment.setType("card"); + return cardPayment; + } + + private PaymentData createBankTransfer() { + PaymentData bankTransfer = new PaymentData(); + bankTransfer.setAmount(BigDecimal.valueOf(150)); + bankTransfer.setPaymentReference("19ae2-18mk73-009"); + bankTransfer.setCurrency(Currency.getInstance("EUR")); + bankTransfer.setType("bank"); + return bankTransfer; + } +} \ No newline at end of file diff --git a/testing-modules/junit-5-advanced/README.md b/testing-modules/junit-5-advanced/README.md index a89bcd2de2..873ab0835b 100644 --- a/testing-modules/junit-5-advanced/README.md +++ b/testing-modules/junit-5-advanced/README.md @@ -9,3 +9,4 @@ - [JUnit – Testing Methods That Call System.exit()](https://www.baeldung.com/junit-system-exit) - [Single Assert Call for Multiple Properties in Java Unit Testing](https://www.baeldung.com/java-testing-single-assert-multiple-properties) - [Creating a Test Suite With JUnit](https://www.baeldung.com/java-junit-test-suite) +- [Testing Interface Contract in Java](https://www.baeldung.com/java-junit-verify-interface-contract)