diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java
new file mode 100644
index 0000000000..0ea128c1d9
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java
@@ -0,0 +1,78 @@
+package com.baeldung.folding;
+
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Calculate a hash value for the strings using the folding technique.
+ *
+ * The implementation serves only to the illustration purposes and is far
+ * from being the most efficient.
+ *
+ * @author A.Shcherbakov
+ *
+ */
+public class FoldingHash {
+
+ /**
+ * Calculate the hash value of a given string.
+ *
+ * @param str Assume it is not null
+ * @param groupSize the group size in the folding technique
+ * @param maxValue defines a max value that the hash may acquire (exclusive)
+ * @return integer value from 0 (inclusive) to maxValue (exclusive)
+ */
+ public int hash(String str, int groupSize, int maxValue) {
+ final int[] codes = this.toAsciiCodes(str);
+ return IntStream.range(0, str.length())
+ .filter(i -> i % groupSize == 0)
+ .mapToObj(i -> extract(codes, i, groupSize))
+ .map(block -> concatenate(block))
+ .reduce(0, (a, b) -> (a + b) % maxValue);
+ }
+
+ /**
+ * Returns a new array of given length whose elements are take from
+ * the original one starting from the offset.
+ *
+ * If the original array has not enough elements, the returning array will contain
+ * element from the offset till the end of the original array.
+ *
+ * @param numbers original array. Assume it is not null.
+ * @param offset index of the element to start from. Assume it is less than the size of the array
+ * @param length max size of the resulting array
+ * @return
+ */
+ public int[] extract(int[] numbers, int offset, int length) {
+ final int defect = numbers.length - (offset + length);
+ final int s = defect < 0 ? length + defect : length;
+ int[] result = new int[s];
+ for (int index = 0; index < s; index++) {
+ result[index] = numbers[index + offset];
+ }
+ return result;
+ }
+
+ /**
+ * Concatenate the numbers into a single number as if they were strings.
+ * Assume that the procedure does not suffer from the overflow.
+ * @param numbers integers to concatenate
+ * @return
+ */
+ public int concatenate(int[] numbers) {
+ final String merged = IntStream.of(numbers)
+ .mapToObj(number -> "" + number)
+ .collect(Collectors.joining());
+ return Integer.parseInt(merged, 10);
+ }
+
+ /**
+ * Convert the string into its characters' ASCII codes.
+ * @param str input string
+ * @return
+ */
+ private int[] toAsciiCodes(String str) {
+ return str.chars()
+ .toArray();
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java
new file mode 100644
index 0000000000..3b055a0dbe
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java
@@ -0,0 +1,17 @@
+package com.baeldung.folding;
+
+/**
+ * Code snippet for article "A Guide to the Folding Technique".
+ *
+ * @author A.Shcherbakov
+ *
+ */
+public class Main {
+
+ public static void main(String... arg) {
+ FoldingHash hasher = new FoldingHash();
+ final String str = "Java language";
+ System.out.println(hasher.hash(str, 2, 100_000));
+ System.out.println(hasher.hash(str, 3, 1_000));
+ }
+}
diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java
new file mode 100644
index 0000000000..43e33d8378
--- /dev/null
+++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java
@@ -0,0 +1,54 @@
+package com.baeldung.folding;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class FoldingHashUnitTest {
+
+ @Test
+ public void givenStringJavaLanguage_whenSize2Capacity100000_then48933() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int value = hasher.hash("Java language", 2, 100_000);
+ assertEquals(value, 48933);
+ }
+
+ @Test
+ public void givenStringVaJaLanguage_whenSize2Capacity100000_thenSameAsJavaLanguage() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int java = hasher.hash("Java language", 2, 100_000);
+ final int vaja = hasher.hash("vaJa language", 2, 100_000);
+ assertTrue(java == vaja);
+ }
+
+ @Test
+ public void givenSingleElementArray_whenOffset0Size2_thenSingleElement() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int[] value = hasher.extract(new int[] { 5 }, 0, 2);
+ assertArrayEquals(new int[] { 5 }, value);
+ }
+
+ @Test
+ public void givenFiveElementArray_whenOffset0Size3_thenFirstThreeElements() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 0, 3);
+ assertArrayEquals(new int[] { 1, 2, 3 }, value);
+ }
+
+ @Test
+ public void givenFiveElementArray_whenOffset1Size2_thenTwoElements() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 1, 2);
+ assertArrayEquals(new int[] { 2, 3 }, value);
+ }
+
+ @Test
+ public void givenFiveElementArray_whenOffset2SizeTooBig_thenElementsToTheEnd() throws Exception {
+ final FoldingHash hasher = new FoldingHash();
+ final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 2, 2000);
+ assertArrayEquals(new int[] { 3, 4, 5 }, value);
+ }
+
+}
diff --git a/autovalue/pom.xml b/autovalue/pom.xml
index 3ec2d26b35..a10e8ef055 100644
--- a/autovalue/pom.xml
+++ b/autovalue/pom.xml
@@ -29,6 +29,12 @@
+
+ com.google.auto.service
+ auto-service
+ ${auto-service.version}
+ true
+
com.google.inject
@@ -40,6 +46,7 @@
1.3
1.0-beta5
+ 1.0-rc5
4.2.0
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java b/autovalue/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java
new file mode 100644
index 0000000000..86d42e80fa
--- /dev/null
+++ b/autovalue/src/main/java/com/baeldung/autoservice/BingTranslationServiceProvider.java
@@ -0,0 +1,14 @@
+package com.baeldung.autoservice;
+
+import com.google.auto.service.AutoService;
+
+import java.util.Locale;
+
+@AutoService(TranslationService.class)
+public class BingTranslationServiceProvider implements TranslationService {
+ @Override
+ public String translate(String message, Locale from, Locale to) {
+ // implementation details
+ return message + " (translated by Bing)";
+ }
+}
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java b/autovalue/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java
new file mode 100644
index 0000000000..0bf91ee5ec
--- /dev/null
+++ b/autovalue/src/main/java/com/baeldung/autoservice/GoogleTranslationServiceProvider.java
@@ -0,0 +1,14 @@
+package com.baeldung.autoservice;
+
+import com.google.auto.service.AutoService;
+
+import java.util.Locale;
+
+@AutoService(TranslationService.class)
+public class GoogleTranslationServiceProvider implements TranslationService {
+ @Override
+ public String translate(String message, Locale from, Locale to) {
+ // implementation details
+ return message + " (translated by Google)";
+ }
+}
diff --git a/autovalue/src/main/java/com/baeldung/autoservice/TranslationService.java b/autovalue/src/main/java/com/baeldung/autoservice/TranslationService.java
new file mode 100644
index 0000000000..580db46cd1
--- /dev/null
+++ b/autovalue/src/main/java/com/baeldung/autoservice/TranslationService.java
@@ -0,0 +1,7 @@
+package com.baeldung.autoservice;
+
+import java.util.Locale;
+
+public interface TranslationService {
+ String translate(String message, Locale from, Locale to);
+}
\ No newline at end of file
diff --git a/autovalue/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java b/autovalue/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java
new file mode 100644
index 0000000000..9e1bd6d291
--- /dev/null
+++ b/autovalue/src/test/java/com/baeldung/autoservice/TranslationServiceUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.autoservice;
+
+import com.baeldung.autoservice.TranslationService;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ServiceLoader;
+import java.util.stream.StreamSupport;
+
+import static org.junit.Assert.assertEquals;
+
+public class TranslationServiceUnitTest {
+
+ private ServiceLoader loader;
+
+ @Before
+ public void setUp() {
+ loader = ServiceLoader.load(TranslationService.class);
+ }
+
+ @Test
+ public void whenServiceLoaderLoads_thenLoadsAllProviders() {
+ long count = StreamSupport.stream(loader.spliterator(), false).count();
+ assertEquals(2, count);
+ }
+
+ @Test
+ public void whenServiceLoaderLoadsGoogleService_thenGoogleIsLoaded() {
+ TranslationService googleService = StreamSupport.stream(loader.spliterator(), false)
+ .filter(p -> p.getClass().getSimpleName().equals("GoogleTranslationServiceProvider"))
+ .findFirst()
+ .get();
+
+ String message = "message";
+ assertEquals(message + " (translated by Google)", googleService.translate(message, null, null));
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-8-2/src/main/java/com/baeldung/stream/SkipLimitComparison.java b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/stream/SkipLimitComparison.java
new file mode 100644
index 0000000000..65f12ada45
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/main/java/com/baeldung/stream/SkipLimitComparison.java
@@ -0,0 +1,46 @@
+package com.baeldung.stream;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class SkipLimitComparison {
+
+ public static void main(String[] args) {
+ skipExample();
+ limitExample();
+ limitInfiniteStreamExample();
+ getEvenNumbers(10, 10).stream()
+ .forEach(System.out::println);
+ }
+
+ public static void skipExample() {
+ Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ .filter(i -> i % 2 == 0)
+ .skip(2)
+ .forEach(i -> System.out.print(i + " "));
+ }
+
+ public static void limitExample() {
+ Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ .filter(i -> i % 2 == 0)
+ .limit(2)
+ .forEach(i -> System.out.print(i + " "));
+ }
+
+ public static void limitInfiniteStreamExample() {
+ Stream.iterate(0, i -> i + 1)
+ .filter(i -> i % 2 == 0)
+ .limit(10)
+ .forEach(System.out::println);
+ }
+
+ private static List getEvenNumbers(int offset, int limit) {
+ return Stream.iterate(0, i -> i + 1)
+ .filter(i -> i % 2 == 0)
+ .skip(offset)
+ .limit(limit)
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/forEach/ReverseList.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/forEach/ReverseList.java
new file mode 100644
index 0000000000..b2ce77a9f6
--- /dev/null
+++ b/core-java-modules/core-java-8/src/main/java/com/baeldung/forEach/ReverseList.java
@@ -0,0 +1,84 @@
+package com.baeldung.forEach;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+
+class ReverseList extends ArrayList {
+
+ List list = Arrays.asList("A", "B", "C", "D");
+
+ Consumer removeElement = s -> {
+ System.out.println(s + " " + list.size());
+ if (s != null && s.equals("A")) {
+ list.remove("D");
+ }
+ };
+
+ @Override
+ public Iterator iterator() {
+
+ final int startIndex = this.size() - 1;
+ final List list = this;
+ return new Iterator() {
+
+ int currentIndex = startIndex;
+
+ @Override
+ public boolean hasNext() {
+ return currentIndex >= 0;
+ }
+
+ @Override
+ public String next() {
+ String next = list.get(currentIndex);
+ currentIndex--;
+ return next;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public void forEach(Consumer super String> action) {
+ for (String s : this) {
+ action.accept(s);
+ }
+ }
+
+ public void iterateParallel() {
+ list.forEach(System.out::print);
+ System.out.print(" ");
+ list.parallelStream().forEach(System.out::print);
+ }
+
+ public void iterateReverse() {
+ List myList = new ReverseList();
+ myList.addAll(list);
+ myList.forEach(System.out::print);
+ System.out.print(" ");
+ myList.stream().forEach(System.out::print);
+ }
+
+ public void removeInCollectionForEach() {
+ list.forEach(removeElement);
+ }
+
+ public void removeInStreamForEach() {
+ list.stream().forEach(removeElement);
+ }
+
+ public static void main(String[] argv) {
+
+ ReverseList collectionForEach = new ReverseList();
+ collectionForEach.iterateParallel();
+ collectionForEach.iterateReverse();
+ collectionForEach.removeInCollectionForEach();
+ collectionForEach.removeInStreamForEach();
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/core-java-exceptions/pom.xml b/core-java-modules/core-java-exceptions/pom.xml
index 51c4e51341..37f65882c3 100644
--- a/core-java-modules/core-java-exceptions/pom.xml
+++ b/core-java-modules/core-java-exceptions/pom.xml
@@ -1,26 +1,35 @@
- 4.0.0
- com.baeldung.exception.numberformat
- core-java-exceptions
- 0.0.1-SNAPSHOT
- core-java-exceptions
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ com.baeldung.exception.numberformat
+ core-java-exceptions
+ 0.0.1-SNAPSHOT
+ core-java-exceptions
-
- com.baeldung
- parent-java
- 0.0.1-SNAPSHOT
- ../../parent-java
-
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
-
-
- junit
- junit
- 4.12
- test
-
-
+
+ 3.9
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
diff --git a/core-java-modules/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/RootCauseFinder.java
similarity index 100%
rename from core-java-modules/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java
rename to core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/RootCauseFinder.java
diff --git a/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java
index de56fb7113..6dcd0d72e0 100644
--- a/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java
+++ b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exception/error/ErrorGeneratorUnitTest.java
@@ -1,7 +1,6 @@
-package com.baeldung.error;
+package com.baeldung.exception.error;
import org.junit.Assert;
-import org.junit.Before;
import org.junit.Test;
public class ErrorGeneratorUnitTest {
diff --git a/core-java-modules/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java
similarity index 100%
rename from core-java-modules/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java
rename to core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java
diff --git a/core-java-modules/core-java-lambdas/README.MD b/core-java-modules/core-java-lambdas/README.MD
deleted file mode 100644
index 31790ffbb1..0000000000
--- a/core-java-modules/core-java-lambdas/README.MD
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant Articles
-
-- [Why Do Local Variables Used in Lambdas Have to Be Final or Effectively Final?](https://www.baeldung.com/java-lambda-effectively-final-local-variables)
diff --git a/core-java-modules/core-java-lang-oop-2/src/main/java/com/baeldung/rawtype/RawTypeDemo.java b/core-java-modules/core-java-lang-oop-2/src/main/java/com/baeldung/rawtype/RawTypeDemo.java
new file mode 100644
index 0000000000..e358219d24
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-2/src/main/java/com/baeldung/rawtype/RawTypeDemo.java
@@ -0,0 +1,25 @@
+package com.baeldung.rawtype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RawTypeDemo {
+
+ public static void main(String[] args) {
+ RawTypeDemo rawTypeDemo = new RawTypeDemo();
+ rawTypeDemo.methodA();
+ }
+
+ public void methodA() {
+ // parameterized type
+ List listStr = new ArrayList<>();
+ listStr.add("Hello Folks!");
+ methodB(listStr);
+ String s = listStr.get(1); // ClassCastException at run time
+ }
+
+ public void methodB(List rawList) { // Inexpressive raw type
+ rawList.add(1); // Unsafe operation
+ }
+
+}
diff --git a/java-collections-conversions/src/test/java/com/baeldung/convertiteratortolist/ConvertIteratorToListServiceUnitTest.java b/java-collections-conversions/src/test/java/com/baeldung/convertiteratortolist/ConvertIteratorToListServiceUnitTest.java
new file mode 100644
index 0000000000..4d6cba7d27
--- /dev/null
+++ b/java-collections-conversions/src/test/java/com/baeldung/convertiteratortolist/ConvertIteratorToListServiceUnitTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.convertiteratortolist;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
+import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.commons.collections4.IteratorUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class ConvertIteratorToListServiceUnitTest {
+
+ Iterator iterator;
+
+ @Before
+ public void setUp() throws Exception {
+ iterator = Arrays.asList(1, 2, 3)
+ .iterator();
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToListUsingWhileLoop_thenReturnAList() {
+
+ List actualList = new ArrayList();
+
+ // Convert Iterator to List using while loop dsf
+ while (iterator.hasNext()) {
+ actualList.add(iterator.next());
+ }
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToListAfterJava8_thenReturnAList() {
+ List actualList = new ArrayList();
+
+ // Convert Iterator to List using Java 8
+ iterator.forEachRemaining(actualList::add);
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToListJava8Stream_thenReturnAList() {
+
+ // Convert iterator to iterable
+ Iterable iterable = () -> iterator;
+
+ // Extract List from stream
+ List actualList = StreamSupport
+ .stream(iterable.spliterator(), false)
+ .collect(Collectors.toList());
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToImmutableListWithGuava_thenReturnAList() {
+
+ // Convert Iterator to an Immutable list using Guava library in Java
+ List actualList = ImmutableList.copyOf(iterator);
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToMutableListWithGuava_thenReturnAList() {
+
+ // Convert Iterator to a mutable list using Guava library in Java
+ List actualList = Lists.newArrayList(iterator);
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+
+ @Test
+ public void givenAnIterator_whenConvertIteratorToMutableListWithApacheCommons_thenReturnAList() {
+
+ // Convert Iterator to a mutable list using Apache Commons library in Java
+ List actualList = IteratorUtils.toList(iterator);
+
+ assertThat(actualList, hasSize(3));
+ assertThat(actualList, containsInAnyOrder(1, 2, 3));
+ }
+}
diff --git a/java-streams/src/test/java/com/baeldung/intstreams/conversion/IntStreamsConversionsUnitTest.java b/java-streams/src/test/java/com/baeldung/intstreams/conversion/IntStreamsConversionsUnitTest.java
new file mode 100644
index 0000000000..6cd773e634
--- /dev/null
+++ b/java-streams/src/test/java/com/baeldung/intstreams/conversion/IntStreamsConversionsUnitTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.intstreams.conversion;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IntStreamsConversionsUnitTest {
+
+ @Test
+ public void intStreamToArray() {
+ int[] first50EvenNumbers = IntStream.iterate(0, i -> i + 2)
+ .limit(50)
+ .toArray();
+
+ assertThat(first50EvenNumbers).hasSize(50);
+ assertThat(first50EvenNumbers[2]).isEqualTo(4);
+ }
+
+ @Test
+ public void intStreamToList() {
+ List first50IntegerNumbers = IntStream.range(0, 50)
+ .boxed()
+ .collect(Collectors.toList());
+
+ assertThat(first50IntegerNumbers).hasSize(50);
+ assertThat(first50IntegerNumbers.get(2)).isEqualTo(2);
+ }
+
+ @Test
+ public void intStreamToString() {
+ String first3numbers = IntStream.of(0, 1, 2)
+ .mapToObj(String::valueOf)
+ .collect(Collectors.joining(", ", "[", "]"));
+
+ assertThat(first3numbers).isEqualTo("[0, 1, 2]");
+ }
+}
diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml
index 53a0233cdd..859de10f13 100644
--- a/libraries-2/pom.xml
+++ b/libraries-2/pom.xml
@@ -1,113 +1,156 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- 4.0.0
- libraries2
- libraries2
+ 4.0.0
+ libraries2
+ libraries2
-
- com.baeldung
- parent-modules
- 1.0.0-SNAPSHOT
-
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
-
-
- jboss-public-repository-group
- JBoss Public Repository Group
- http://repository.jboss.org/nexus/content/groups/public/
-
- true
- never
-
-
- true
- daily
-
-
-
+
+
+ jboss-public-repository-group
+ JBoss Public Repository Group
+ http://repository.jboss.org/nexus/content/groups/public/
+
+ true
+ never
+
+
+ true
+ daily
+
+
+
-
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+
+
+ io.github.classgraph
+ classgraph
+ ${classgraph.version}
+
+
+ org.jbpm
+ jbpm-test
+ ${jbpm.version}
+
+
+ info.picocli
+ picocli
+ ${picocli.version}
+
- org.assertj
- assertj-core
- ${assertj.version}
+ org.ejml
+ ejml-all
+ ${ejml.version}
+
+
+ org.nd4j
+ nd4j-native
+ ${nd4j.version}
+
+
+ org.la4j
+ la4j
+ ${la4j.version}
- io.github.classgraph
- classgraph
- ${classgraph.version}
-
-
- org.jbpm
- jbpm-test
- ${jbpm.version}
-
-
- info.picocli
- picocli
- ${picocli.version}
-
-
- org.springframework.boot
- spring-boot-starter
- ${spring-boot-starter.version}
-
-
- net.openhft
- chronicle-map
- ${chronicle.map.version}
-
-
- com.sun.java
- tools
-
-
+ colt
+ colt
+ ${colt.version}
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-starter.version}
+
+
+ net.openhft
+ chronicle-map
+ ${chronicle.map.version}
+
+
+ com.sun.java
+ tools
+
+
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 3.14.2
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.9
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.5
+
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ 3.14.2
+ test
+
-
-
- com.squareup.okhttp3
- okhttp
- 3.14.2
-
-
- com.fasterxml.jackson.core
- jackson-databind
- 2.9.9
-
-
- com.google.code.gson
- gson
- 2.8.5
-
-
- com.squareup.okhttp3
- mockwebserver
- 3.14.2
- test
-
edu.uci.ics
crawler4j
${crawler4j.version}
-
-
- com.github.jknack
- handlebars
- 4.1.2
-
-
+
-
- 3.6.2
- 4.8.28
- 6.0.0.Final
- 3.9.6
- 3.17.2
+
+ com.github.jknack
+ handlebars
+ 4.1.2
+
+
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+
+
+
+
+
+ 3.6.2
+ 4.8.28
+ 6.0.0.Final
+ 3.9.6
+ 3.17.2
4.4.0
- 2.1.4.RELEASE
-
+ 2.1.4.RELEASE
+ 0.38
+ 1.0.0-beta4
+ 1.2.0
+ 0.6.0
+ 1.19
+
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/MatrixMultiplicationBenchmarking.java b/libraries-2/src/test/java/com/baeldung/matrices/MatrixMultiplicationBenchmarking.java
new file mode 100644
index 0000000000..1e3b183aa7
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/MatrixMultiplicationBenchmarking.java
@@ -0,0 +1,9 @@
+package com.baeldung.matrices;
+
+public class MatrixMultiplicationBenchmarking {
+
+ public static void main(String[] args) throws Exception {
+ org.openjdk.jmh.Main.main(args);
+ }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/apache/RealMatrixUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/apache/RealMatrixUnitTest.java
new file mode 100644
index 0000000000..05944e7b3a
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/apache/RealMatrixUnitTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.matrices.apache;
+
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.junit.jupiter.api.Test;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class RealMatrixUnitTest {
+
+ @Test
+ @Benchmark
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ RealMatrix firstMatrix = new Array2DRowRealMatrix(
+ new double[][] {
+ new double[] {1d, 5d},
+ new double[] {2d, 3d},
+ new double[] {1d ,7d}
+ }
+ );
+
+ RealMatrix secondMatrix = new Array2DRowRealMatrix(
+ new double[][] {
+ new double[] {1d, 2d, 3d, 7d},
+ new double[] {5d, 2d, 8d, 1d}
+ }
+ );
+
+ RealMatrix expected = new Array2DRowRealMatrix(
+ new double[][] {
+ new double[] {26d, 12d, 43d, 12d},
+ new double[] {17d, 10d, 30d, 17d},
+ new double[] {36d, 16d, 59d, 14d}
+ }
+ );
+
+ RealMatrix actual = firstMatrix.multiply(secondMatrix);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/colt/DoubleMatrix2DUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/colt/DoubleMatrix2DUnitTest.java
new file mode 100644
index 0000000000..fb4a419eb0
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/colt/DoubleMatrix2DUnitTest.java
@@ -0,0 +1,51 @@
+package com.baeldung.matrices.colt;
+
+import cern.colt.matrix.DoubleFactory2D;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.linalg.Algebra;
+import org.junit.jupiter.api.Test;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class DoubleMatrix2DUnitTest {
+
+ @Test
+ @Benchmark
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ DoubleFactory2D doubleFactory2D = DoubleFactory2D.dense;
+
+ DoubleMatrix2D firstMatrix = doubleFactory2D.make(
+ new double[][] {
+ new double[] {1d, 5d},
+ new double[] {2d, 3d},
+ new double[] {1d ,7d}
+ }
+ );
+
+ DoubleMatrix2D secondMatrix = doubleFactory2D.make(
+ new double[][] {
+ new double[] {1d, 2d, 3d, 7d},
+ new double[] {5d, 2d, 8d, 1d}
+ }
+ );
+
+ DoubleMatrix2D expected = doubleFactory2D.make(
+ new double[][] {
+ new double[] {26d, 12d, 43d, 12d},
+ new double[] {17d, 10d, 30d, 17d},
+ new double[] {36d, 16d, 59d, 14d}
+ }
+ );
+
+ Algebra algebra = new Algebra();
+ DoubleMatrix2D actual = algebra.mult(firstMatrix, secondMatrix);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/ejml/SimpleMatrixUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/ejml/SimpleMatrixUnitTest.java
new file mode 100644
index 0000000000..b025266a1d
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/ejml/SimpleMatrixUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.matrices.ejml;
+
+import org.ejml.simple.SimpleMatrix;
+import org.junit.jupiter.api.Test;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class SimpleMatrixUnitTest {
+
+ @Test
+ @Benchmark
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ SimpleMatrix firstMatrix = new SimpleMatrix(
+ new double[][] {
+ new double[] {1d, 5d},
+ new double[] {2d, 3d},
+ new double[] {1d ,7d}
+ }
+ );
+
+ SimpleMatrix secondMatrix = new SimpleMatrix(
+ new double[][] {
+ new double[] {1d, 2d, 3d, 7d},
+ new double[] {5d, 2d, 8d, 1d}
+ }
+ );
+
+ SimpleMatrix expected = new SimpleMatrix(
+ new double[][] {
+ new double[] {26d, 12d, 43d, 12d},
+ new double[] {17d, 10d, 30d, 17d},
+ new double[] {36d, 16d, 59d, 14d}
+ }
+ );
+
+ SimpleMatrix actual = firstMatrix.mult(secondMatrix);
+
+ assertThat(actual).matches(m -> m.isIdentical(expected, 0d));
+ }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/homemade/HomemadeMatrixUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/homemade/HomemadeMatrixUnitTest.java
new file mode 100644
index 0000000000..be9e483d5b
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/homemade/HomemadeMatrixUnitTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.matrices.homemade;
+
+import org.junit.jupiter.api.Test;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class HomemadeMatrixUnitTest {
+
+ @Test
+ @Benchmark
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ double[][] firstMatrix = {
+ new double[]{1d, 5d},
+ new double[]{2d, 3d},
+ new double[]{1d, 7d}
+ };
+
+ double[][] secondMatrix = {
+ new double[]{1d, 2d, 3d, 7d},
+ new double[]{5d, 2d, 8d, 1d}
+ };
+
+ double[][] expected = {
+ new double[]{26d, 12d, 43d, 12d},
+ new double[]{17d, 10d, 30d, 17d},
+ new double[]{36d, 16d, 59d, 14d}
+ };
+
+ double[][] actual = multiplyMatrices(firstMatrix, secondMatrix);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ private double[][] multiplyMatrices(double[][] firstMatrix, double[][] secondMatrix) {
+ double[][] result = new double[firstMatrix.length][secondMatrix[0].length];
+
+ for (int row = 0; row < result.length; row++) {
+ for (int col = 0; col < result[row].length; col++) {
+ result[row][col] = multiplyMatricesCell(firstMatrix, secondMatrix, row, col);
+ }
+ }
+
+ return result;
+ }
+
+ private double multiplyMatricesCell(double[][] firstMatrix, double[][] secondMatrix, int row, int col) {
+ double cell = 0;
+ for (int i = 0; i < secondMatrix.length; i++) {
+ cell += firstMatrix[row][i] * secondMatrix[i][col];
+ }
+ return cell;
+ }
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/la4j/Basic2DMatrixUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/la4j/Basic2DMatrixUnitTest.java
new file mode 100644
index 0000000000..afb84ff3db
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/la4j/Basic2DMatrixUnitTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.matrices.la4j;
+
+import org.junit.jupiter.api.Test;
+import org.la4j.Matrix;
+import org.la4j.matrix.dense.Basic2DMatrix;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class Basic2DMatrixUnitTest {
+
+ @Test
+ @Benchmark
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ Matrix firstMatrix = new Basic2DMatrix(
+ new double[][]{
+ new double[]{1d, 5d},
+ new double[]{2d, 3d},
+ new double[]{1d, 7d}
+ }
+ );
+
+ Matrix secondMatrix = new Basic2DMatrix(
+ new double[][]{
+ new double[]{1d, 2d, 3d, 7d},
+ new double[]{5d, 2d, 8d, 1d}
+ }
+ );
+
+ Matrix expected = new Basic2DMatrix(
+ new double[][]{
+ new double[]{26d, 12d, 43d, 12d},
+ new double[]{17d, 10d, 30d, 17d},
+ new double[]{36d, 16d, 59d, 14d}
+ }
+ );
+
+ Matrix actual = firstMatrix.multiply(secondMatrix);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/matrices/nd4j/INDArrayUnitTest.java b/libraries-2/src/test/java/com/baeldung/matrices/nd4j/INDArrayUnitTest.java
new file mode 100644
index 0000000000..fb3030bccf
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/matrices/nd4j/INDArrayUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.matrices.nd4j;
+
+import org.junit.jupiter.api.Test;
+import org.nd4j.linalg.api.ndarray.INDArray;
+import org.nd4j.linalg.factory.Nd4j;
+import org.openjdk.jmh.annotations.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@BenchmarkMode(Mode.AverageTime)
+@Fork(value = 2)
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+public class INDArrayUnitTest {
+
+ @Test
+ public void givenTwoMatrices_whenMultiply_thenMultiplicatedMatrix() {
+ INDArray firstMatrix = Nd4j.create(
+ new double[][]{
+ new double[]{1d, 5d},
+ new double[]{2d, 3d},
+ new double[]{1d, 7d}
+ }
+ );
+
+ INDArray secondMatrix = Nd4j.create(
+ new double[][] {
+ new double[] {1d, 2d, 3d, 7d},
+ new double[] {5d, 2d, 8d, 1d}
+ }
+ );
+
+ INDArray expected = Nd4j.create(
+ new double[][] {
+ new double[] {26d, 12d, 43d, 12d},
+ new double[] {17d, 10d, 30d, 17d},
+ new double[] {36d, 16d, 59d, 14d}
+ }
+ );
+
+ INDArray actual = firstMatrix.mmul(secondMatrix);
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+}
diff --git a/libraries-server/pom.xml b/libraries-server/pom.xml
index a6ead8fb31..873cca9b9b 100644
--- a/libraries-server/pom.xml
+++ b/libraries-server/pom.xml
@@ -96,6 +96,19 @@
smack-java7
${smack.version}
+
+
+
+ org.nanohttpd
+ nanohttpd
+ ${nanohttpd.version}
+
+
+ org.nanohttpd
+ nanohttpd-nanolets
+ ${nanohttpd.version}
+
+
@@ -108,6 +121,7 @@
8.5.24
4.3.1
1.2.0
+ 2.3.1
\ No newline at end of file
diff --git a/libraries-server/src/main/java/com/baeldung/nanohttpd/ApplicationController.java b/libraries-server/src/main/java/com/baeldung/nanohttpd/ApplicationController.java
new file mode 100644
index 0000000000..2fa54c3ae2
--- /dev/null
+++ b/libraries-server/src/main/java/com/baeldung/nanohttpd/ApplicationController.java
@@ -0,0 +1,38 @@
+package com.baeldung.nanohttpd;
+
+import fi.iki.elonen.NanoHTTPD;
+import fi.iki.elonen.router.RouterNanoHTTPD;
+
+import java.io.IOException;
+
+public class ApplicationController extends RouterNanoHTTPD {
+
+ ApplicationController() throws IOException {
+ super(8072);
+ addMappings();
+ start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
+ }
+
+ @Override
+ public void addMappings() {
+ addRoute("/", IndexHandler.class);
+ addRoute("/users", UserHandler.class);
+ }
+
+ public static class UserHandler extends DefaultHandler {
+ @Override
+ public String getText() {
+ return "UserA, UserB, UserC";
+ }
+
+ @Override
+ public String getMimeType() {
+ return MIME_PLAINTEXT;
+ }
+
+ @Override
+ public Response.IStatus getStatus() {
+ return Response.Status.OK;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries-server/src/main/java/com/baeldung/nanohttpd/ItemGetController.java b/libraries-server/src/main/java/com/baeldung/nanohttpd/ItemGetController.java
new file mode 100644
index 0000000000..4a9c48fbfd
--- /dev/null
+++ b/libraries-server/src/main/java/com/baeldung/nanohttpd/ItemGetController.java
@@ -0,0 +1,22 @@
+package com.baeldung.nanohttpd;
+
+import fi.iki.elonen.NanoHTTPD;
+
+import java.io.IOException;
+
+public class ItemGetController extends NanoHTTPD {
+
+ ItemGetController() throws IOException {
+ super(8071);
+ start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
+ }
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ if (session.getMethod() == Method.GET) {
+ String itemIdRequestParam = session.getParameters().get("itemId").get(0);
+ return newFixedLengthResponse("Requested itemId = " + itemIdRequestParam);
+ }
+ return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "The requested resource does not exist");
+ }
+}
\ No newline at end of file
diff --git a/libraries-server/src/test/java/com/baeldung/nanohttpd/ApplicationControllerUnitTest.java b/libraries-server/src/test/java/com/baeldung/nanohttpd/ApplicationControllerUnitTest.java
new file mode 100644
index 0000000000..003f6ee3b7
--- /dev/null
+++ b/libraries-server/src/test/java/com/baeldung/nanohttpd/ApplicationControllerUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.nanohttpd;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class ApplicationControllerUnitTest {
+
+ private static final String BASE_URL = "http://localhost:8072/";
+ private static final HttpClient CLIENT = HttpClientBuilder.create().build();
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ new ApplicationController();
+ }
+
+ @Test
+ public void givenServer_whenRootRouteRequested_thenHelloWorldReturned() throws IOException {
+ HttpResponse response = CLIENT.execute(new HttpGet(BASE_URL));
+ assertTrue(IOUtils.toString(response.getEntity().getContent()).contains("Hello world!"));
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void givenServer_whenUsersRequested_thenThenAllUsersReturned() throws IOException {
+ HttpResponse response = CLIENT.execute(new HttpGet(BASE_URL + "users"));
+ assertEquals("UserA, UserB, UserC", IOUtils.toString(response.getEntity().getContent()));
+ }
+}
\ No newline at end of file
diff --git a/libraries-server/src/test/java/com/baeldung/nanohttpd/ItemGetControllerUnitTest.java b/libraries-server/src/test/java/com/baeldung/nanohttpd/ItemGetControllerUnitTest.java
new file mode 100644
index 0000000000..3a4f0a4d98
--- /dev/null
+++ b/libraries-server/src/test/java/com/baeldung/nanohttpd/ItemGetControllerUnitTest.java
@@ -0,0 +1,37 @@
+package com.baeldung.nanohttpd;
+
+import static org.junit.Assert.*;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class ItemGetControllerUnitTest {
+
+ private static final String URL = "http://localhost:8071";
+ private static final HttpClient CLIENT = HttpClientBuilder.create().build();
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ new ItemGetController();
+ }
+
+ @Test
+ public void givenServer_whenDoingGet_thenParamIsReadCorrectly() throws IOException {
+ HttpResponse response = CLIENT.execute(new HttpGet(URL + "?itemId=1234"));
+ assertEquals("Requested itemId = 1234", IOUtils.toString(response.getEntity().getContent()));
+ }
+
+ @Test
+ public void givenServer_whenDoingPost_then404IsReturned() throws IOException {
+ HttpResponse response = CLIENT.execute(new HttpPost(URL));
+ assertEquals(404, response.getStatusLine().getStatusCode());
+ }
+}
\ No newline at end of file
diff --git a/parent-boot-performance/README.md b/parent-boot-performance/README.md
new file mode 100644
index 0000000000..fce9e101da
--- /dev/null
+++ b/parent-boot-performance/README.md
@@ -0,0 +1 @@
+This is a parent module for projects that want to take advantage of the latest Spring Boot improvements/features.
\ No newline at end of file
diff --git a/parent-boot-performance/pom.xml b/parent-boot-performance/pom.xml
new file mode 100644
index 0000000000..b1c6854eae
--- /dev/null
+++ b/parent-boot-performance/pom.xml
@@ -0,0 +1,91 @@
+
+ 4.0.0
+ parent-boot-performance
+ 0.0.1-SNAPSHOT
+ parent-boot-performance
+ pom
+ Parent for all modules that want to take advantage of the latest Spring Boot improvements/features. Current version: 2.2
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+ 3.1.0
+
+ 1.0.21.RELEASE
+ 2.2.0.M3
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+ io.rest-assured
+ rest-assured
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+ ${start-class}
+
+
+
+
+
+
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+
+
+
+
+ thin-jar
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.springframework.boot.experimental
+ spring-boot-thin-layout
+ ${thin.version}
+
+
+
+
+
+
+
+
diff --git a/persistence-modules/java-jpa/pom.xml b/persistence-modules/java-jpa/pom.xml
index f23040fbdc..51cc401332 100644
--- a/persistence-modules/java-jpa/pom.xml
+++ b/persistence-modules/java-jpa/pom.xml
@@ -19,6 +19,11 @@
hibernate-core
${hibernate.version}
+
+ org.hibernate
+ hibernate-jpamodelgen
+ ${hibernate.version}
+
com.h2database
h2
@@ -47,6 +52,58 @@
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ -proc:none
+
+
+
+ org.bsc.maven
+ maven-processor-plugin
+ 3.3.3
+
+
+ process
+
+ process
+
+ generate-sources
+
+ target/metamodel
+
+ org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+
5.4.0.Final
2.7.4-RC1
diff --git a/persistence-modules/java-jpa/src/main/java/com/baeldung/jpa/queryparams/Employee.java b/persistence-modules/java-jpa/src/main/java/com/baeldung/jpa/queryparams/Employee.java
new file mode 100644
index 0000000000..bf3d459530
--- /dev/null
+++ b/persistence-modules/java-jpa/src/main/java/com/baeldung/jpa/queryparams/Employee.java
@@ -0,0 +1,79 @@
+package com.baeldung.jpa.queryparams;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "employees")
+public class Employee {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private Long id;
+
+ @Column(name = "employee_number", unique = true)
+ private String empNumber;
+
+ @Column(name = "employee_name")
+ private String name;
+
+ @Column(name = "employee_age")
+ private int age;
+
+ public Employee() {
+ super();
+ }
+
+ public Employee(Long id, String empNumber) {
+ super();
+ this.id = id;
+ this.empNumber = empNumber;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmpNumber() {
+ return empNumber;
+ }
+
+ public void setEmpNumber(String empNumber) {
+ this.empNumber = empNumber;
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+}
diff --git a/persistence-modules/java-jpa/src/main/resources/META-INF/persistence.xml b/persistence-modules/java-jpa/src/main/resources/META-INF/persistence.xml
index 1f16bee3ba..6a236f0840 100644
--- a/persistence-modules/java-jpa/src/main/resources/META-INF/persistence.xml
+++ b/persistence-modules/java-jpa/src/main/resources/META-INF/persistence.xml
@@ -224,4 +224,26 @@
value="products_jpa.sql" />
+
+
+ org.hibernate.jpa.HibernatePersistenceProvider
+ com.baeldung.jpa.queryparams.Employee
+ true
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/java-jpa/src/test/java/com/baeldung/jpa/queryparams/JPAQueryParamsUnitTest.java b/persistence-modules/java-jpa/src/test/java/com/baeldung/jpa/queryparams/JPAQueryParamsUnitTest.java
new file mode 100644
index 0000000000..4f320935cf
--- /dev/null
+++ b/persistence-modules/java-jpa/src/test/java/com/baeldung/jpa/queryparams/JPAQueryParamsUnitTest.java
@@ -0,0 +1,109 @@
+package com.baeldung.jpa.queryparams;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.ParameterExpression;
+import javax.persistence.criteria.Root;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * JPAQueryParamsTest class tests.
+ *
+ * @author gmlopez.mackinnon@gmail.com
+ */
+public class JPAQueryParamsUnitTest {
+
+ private static EntityManager entityManager;
+
+ @BeforeClass
+ public static void setup() {
+ EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa-h2-queryparams");
+ entityManager = factory.createEntityManager();
+ }
+
+ @Test
+ public void givenEmpNumber_whenUsingPositionalParameter_thenReturnExpectedEmployee() {
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.empNumber = ?1", Employee.class);
+ String empNumber = "A123";
+ Employee employee = query.setParameter(1, empNumber)
+ .getSingleResult();
+ Assert.assertNotNull("Employee not found", employee);
+ }
+
+ @Test
+ public void givenEmpNumberList_whenUsingPositionalParameter_thenReturnExpectedEmployee() {
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.empNumber IN (?1)", Employee.class);
+ List empNumbers = Arrays.asList("A123", "A124");
+ List employees = query.setParameter(1, empNumbers)
+ .getResultList();
+ Assert.assertNotNull("Employees not found", employees);
+ Assert.assertFalse("Employees not found", employees.isEmpty());
+ }
+
+ @Test
+ public void givenEmpNumber_whenUsingNamedParameter_thenReturnExpectedEmployee() {
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.empNumber = :number", Employee.class);
+ String empNumber = "A123";
+ Employee employee = query.setParameter("number", empNumber)
+ .getSingleResult();
+ Assert.assertNotNull("Employee not found", employee);
+ }
+
+ @Test
+ public void givenEmpNumberList_whenUsingNamedParameter_thenReturnExpectedEmployee() {
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.empNumber IN (:numbers)", Employee.class);
+ List empNumbers = Arrays.asList("A123", "A124");
+ List employees = query.setParameter("numbers", empNumbers)
+ .getResultList();
+ Assert.assertNotNull("Employees not found", employees);
+ Assert.assertFalse("Employees not found", employees.isEmpty());
+ }
+
+ @Test
+ public void givenEmpNameAndEmpAge_whenUsingTwoNamedParameters_thenReturnExpectedEmployees() {
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.name = :name AND e.age = :empAge", Employee.class);
+ String empName = "John Doe";
+ int empAge = 55;
+ List employees = query.setParameter("name", empName)
+ .setParameter("empAge", empAge)
+ .getResultList();
+ Assert.assertNotNull("Employees not found!", employees);
+ Assert.assertTrue("Employees not found!", !employees.isEmpty());
+ }
+
+ @Test
+ public void givenEmpNumber_whenUsingCriteriaQuery_thenReturnExpectedEmployee() {
+ CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+
+ CriteriaQuery cQuery = cb.createQuery(Employee.class);
+ Root c = cQuery.from(Employee.class);
+ ParameterExpression paramEmpNumber = cb.parameter(String.class);
+ cQuery.select(c)
+ .where(cb.equal(c.get(Employee_.empNumber), paramEmpNumber));
+
+ TypedQuery query = entityManager.createQuery(cQuery);
+ String empNumber = "A123";
+ query.setParameter(paramEmpNumber, empNumber);
+ Employee employee = query.getSingleResult();
+ Assert.assertNotNull("Employee not found!", employee);
+ }
+
+ @Test
+ public void givenEmpNumber_whenUsingLiteral_thenReturnExpectedEmployee() {
+ String empNumber = "A123";
+ TypedQuery query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.empNumber = '" + empNumber + "'", Employee.class);
+ Employee employee = query.getSingleResult();
+ Assert.assertNotNull("Employee not found!", employee);
+ }
+
+}
diff --git a/persistence-modules/java-jpa/src/test/resources/employees2.sql b/persistence-modules/java-jpa/src/test/resources/employees2.sql
new file mode 100644
index 0000000000..d3ae46f6a0
--- /dev/null
+++ b/persistence-modules/java-jpa/src/test/resources/employees2.sql
@@ -0,0 +1,2 @@
+INSERT INTO employees (employee_number, employee_name, employee_age) VALUES ('111', 'John Doe', 55);
+INSERT INTO employees (employee_number, employee_name, employee_age) VALUES ('A123', 'John Doe Junior', 25);
\ No newline at end of file
diff --git a/ratpack/build.gradle b/ratpack/build.gradle
index 25af3ddb51..c997b4e697 100644
--- a/ratpack/build.gradle
+++ b/ratpack/build.gradle
@@ -14,6 +14,8 @@ if (!JavaVersion.current().java8Compatible) {
apply plugin: "io.ratpack.ratpack-java"
apply plugin: 'java'
+apply plugin: 'groovy'
+apply plugin: 'io.ratpack.ratpack-groovy'
repositories {
jcenter()
diff --git a/ratpack/pom.xml b/ratpack/pom.xml
index 7c145eff91..0df313c05e 100644
--- a/ratpack/pom.xml
+++ b/ratpack/pom.xml
@@ -26,11 +26,21 @@
ratpack-core
${ratpack.version}
+
+ org.codehaus.groovy
+ groovy-sql
+ ${groovy.sql.version}
+
io.ratpack
ratpack-hikari
${ratpack.version}
+
+ io.ratpack
+ ratpack-groovy-test
+ ${ratpack.test.latest.version}
+
io.ratpack
ratpack-hystrix
@@ -91,6 +101,7 @@
4.5.3
4.4.6
1.5.12
+ 2.4.15
+ 1.6.1
-
diff --git a/ratpack/src/main/groovy/com/baeldung/Ratpack.groovy b/ratpack/src/main/groovy/com/baeldung/Ratpack.groovy
new file mode 100644
index 0000000000..4d8814b627
--- /dev/null
+++ b/ratpack/src/main/groovy/com/baeldung/Ratpack.groovy
@@ -0,0 +1,76 @@
+package com.baeldung;
+
+@Grab('io.ratpack:ratpack-groovy:1.6.1')
+import static ratpack.groovy.Groovy.ratpack
+
+import com.baeldung.model.User
+import com.google.common.reflect.TypeToken
+import ratpack.exec.Promise
+import ratpack.handling.Context
+import ratpack.jackson.Jackson
+import groovy.sql.Sql
+import java.sql.Connection
+import java.sql.PreparedStatement
+import java.sql.ResultSet
+import ratpack.hikari.HikariModule
+import javax.sql.DataSource;
+
+ratpack {
+ serverConfig { port(5050) }
+ bindings {
+ module(HikariModule) { config ->
+ config.dataSourceClassName = 'org.h2.jdbcx.JdbcDataSource'
+ config.addDataSourceProperty('URL', "jdbc:h2:mem:devDB;INIT=RUNSCRIPT FROM 'classpath:/User.sql'")
+ }
+ }
+
+ handlers {
+
+ get { render 'Hello World from Ratpack with Groovy!!' }
+
+ get("greet/:name") { Context ctx ->
+ render "Hello " + ctx.getPathTokens().get("name") + "!!!"
+ }
+
+ get("data") {
+ render Jackson.json([title: "Mr", name: "Norman", country: "USA"])
+ }
+
+ post("user") {
+ Promise user = parse(Jackson.fromJson(User))
+ user.then { u -> render u.name }
+ }
+
+ get('fetchUserName/:id') { Context ctx ->
+ Connection connection = ctx.get(DataSource.class).getConnection()
+ PreparedStatement queryStatement = connection.prepareStatement("SELECT NAME FROM USER WHERE ID=?")
+ queryStatement.setInt(1, Integer.parseInt(ctx.getPathTokens().get("id")))
+ ResultSet resultSet = queryStatement.executeQuery()
+ resultSet.next()
+ render resultSet.getString(1)
+ }
+
+ get('fetchUsers') {
+ def db = [url:'jdbc:h2:mem:devDB']
+ def sql = Sql.newInstance(db.url, db.user, db.password)
+ def users = sql.rows("SELECT * FROM USER");
+ render(Jackson.json(users))
+ }
+
+ post('addUser') {
+ parse(Jackson.fromJson(User))
+ .then { u ->
+ def db = [url:'jdbc:h2:mem:devDB']
+ Sql sql = Sql.newInstance(db.url, db.user, db.password)
+ sql.executeInsert("INSERT INTO USER VALUES (?,?,?,?)", [
+ u.id,
+ u.title,
+ u.name,
+ u.country
+ ])
+ render "User $u.name inserted"
+ }
+ }
+ }
+}
+
diff --git a/ratpack/src/main/groovy/com/baeldung/RatpackGroovyApp.groovy b/ratpack/src/main/groovy/com/baeldung/RatpackGroovyApp.groovy
new file mode 100644
index 0000000000..95ada25e60
--- /dev/null
+++ b/ratpack/src/main/groovy/com/baeldung/RatpackGroovyApp.groovy
@@ -0,0 +1,12 @@
+package com.baeldung;
+
+public class RatpackGroovyApp {
+
+ public static void main(String[] args) {
+ File file = new File("src/main/groovy/com/baeldung/Ratpack.groovy");
+ def shell = new GroovyShell()
+ shell.evaluate(file)
+ }
+
+}
+
diff --git a/ratpack/src/main/groovy/com/baeldung/model/User.groovy b/ratpack/src/main/groovy/com/baeldung/model/User.groovy
new file mode 100644
index 0000000000..0fe4c6c367
--- /dev/null
+++ b/ratpack/src/main/groovy/com/baeldung/model/User.groovy
@@ -0,0 +1,9 @@
+package com.baeldung.model
+
+class User {
+
+ long id
+ String title
+ String name
+ String country
+}
diff --git a/ratpack/src/main/resources/User.sql b/ratpack/src/main/resources/User.sql
new file mode 100644
index 0000000000..a3e2242283
--- /dev/null
+++ b/ratpack/src/main/resources/User.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS USER;
+CREATE TABLE USER (
+ ID BIGINT AUTO_INCREMENT PRIMARY KEY,
+ TITLE VARCHAR(255),
+ NAME VARCHAR(255),
+ COUNTRY VARCHAR(255)
+);
+
+INSERT INTO USER VALUES(1,'Mr','Norman Potter', 'USA');
+INSERT INTO USER VALUES(2,'Miss','Ketty Smith', 'FRANCE');
\ No newline at end of file
diff --git a/ratpack/src/test/groovy/com/baeldung/RatpackGroovySpec.groovy b/ratpack/src/test/groovy/com/baeldung/RatpackGroovySpec.groovy
new file mode 100644
index 0000000000..726d703a06
--- /dev/null
+++ b/ratpack/src/test/groovy/com/baeldung/RatpackGroovySpec.groovy
@@ -0,0 +1,46 @@
+package com.baeldung;
+
+import ratpack.groovy.Groovy
+import ratpack.groovy.test.GroovyRatpackMainApplicationUnderTest;
+import ratpack.test.http.TestHttpClient;
+import ratpack.test.ServerBackedApplicationUnderTest;
+import org.junit.Test;
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import ratpack.test.MainClassApplicationUnderTest
+
+class RatpackGroovySpec {
+
+ ServerBackedApplicationUnderTest ratpackGroovyApp = new MainClassApplicationUnderTest(RatpackGroovyApp.class)
+ @Delegate TestHttpClient client = TestHttpClient.testHttpClient(ratpackGroovyApp)
+
+ @Test
+ void "test if app is started"() {
+ when:
+ get("")
+
+ then:
+ assert response.statusCode == 200
+ assert response.body.text == "Hello World from Ratpack with Groovy!!"
+ }
+
+ @Test
+ void "test greet with name"() {
+ when:
+ get("greet/Lewis")
+
+ then:
+ assert response.statusCode == 200
+ assert response.body.text == "Hello Lewis!!!"
+ }
+
+ @Test
+ void "test fetchUsers"() {
+ when:
+ get("fetchUsers")
+
+ then:
+ assert response.statusCode == 200
+ assert response.body.text == '[{"ID":1,"TITLE":"Mr","NAME":"Norman Potter","COUNTRY":"USA"},{"ID":2,"TITLE":"Miss","NAME":"Ketty Smith","COUNTRY":"FRANCE"}]'
+ }
+}
diff --git a/spring-5-data-reactive/pom.xml b/spring-5-data-reactive/pom.xml
index aa73cf11ae..8c16851de0 100644
--- a/spring-5-data-reactive/pom.xml
+++ b/spring-5-data-reactive/pom.xml
@@ -63,6 +63,11 @@
spring-boot-starter-test
test
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ test
+
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java
new file mode 100644
index 0000000000..8b2511a8f3
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/LogsCounterApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.tailablecursor;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class LogsCounterApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(LogsCounterApplication.class, args);
+ }
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java
new file mode 100644
index 0000000000..717a367751
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/Log.java
@@ -0,0 +1,21 @@
+package com.baeldung.tailablecursor.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Data
+@Document
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Log {
+ @Id
+ private String id;
+ private String service;
+ private LogLevel level;
+ private String message;
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java
new file mode 100644
index 0000000000..6826fbffd3
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/domain/LogLevel.java
@@ -0,0 +1,5 @@
+package com.baeldung.tailablecursor.domain;
+
+public enum LogLevel {
+ ERROR, WARN, INFO
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java
new file mode 100644
index 0000000000..dce11c548c
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/repository/LogsRepository.java
@@ -0,0 +1,12 @@
+package com.baeldung.tailablecursor.repository;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import org.springframework.data.mongodb.repository.Tailable;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+import reactor.core.publisher.Flux;
+
+public interface LogsRepository extends ReactiveCrudRepository {
+ @Tailable
+ Flux findByLevel(LogLevel level);
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java
new file mode 100644
index 0000000000..c243e64f97
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/ErrorLogsCounter.java
@@ -0,0 +1,62 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainer;
+import org.springframework.data.mongodb.core.messaging.MessageListener;
+import org.springframework.data.mongodb.core.messaging.MessageListenerContainer;
+import org.springframework.data.mongodb.core.messaging.TailableCursorRequest;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
+
+@Slf4j
+public class ErrorLogsCounter implements LogsCounter {
+
+ private static final String LEVEL_FIELD_NAME = "level";
+
+ private final String collectionName;
+ private final MessageListenerContainer container;
+
+ private final AtomicInteger counter = new AtomicInteger();
+
+ public ErrorLogsCounter(MongoTemplate mongoTemplate,
+ String collectionName) {
+ this.collectionName = collectionName;
+ this.container = new DefaultMessageListenerContainer(mongoTemplate);
+
+ container.start();
+ TailableCursorRequest request = getTailableCursorRequest();
+ container.register(request, Log.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private TailableCursorRequest getTailableCursorRequest() {
+ MessageListener listener = message -> {
+ log.info("ERROR log received: {}", message.getBody());
+ counter.incrementAndGet();
+ };
+
+ return TailableCursorRequest.builder()
+ .collection(collectionName)
+ .filter(query(where(LEVEL_FIELD_NAME).is(LogLevel.ERROR)))
+ .publishTo(listener)
+ .build();
+ }
+
+ @Override
+ public int count() {
+ return counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ container.stop();
+ }
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java
new file mode 100644
index 0000000000..b30eba0b25
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/InfoLogsCounter.java
@@ -0,0 +1,36 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Slf4j
+public class InfoLogsCounter implements LogsCounter {
+
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Disposable subscription;
+
+ public InfoLogsCounter(LogsRepository repository) {
+ Flux stream = repository.findByLevel(LogLevel.INFO);
+ this.subscription = stream.subscribe(l -> {
+ log.info("INFO log received: " + l);
+ counter.incrementAndGet();
+ });
+ }
+
+ @Override
+ public int count() {
+ return this.counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ this.subscription.dispose();
+ }
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java
new file mode 100644
index 0000000000..e14a3eadd7
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/LogsCounter.java
@@ -0,0 +1,5 @@
+package com.baeldung.tailablecursor.service;
+
+public interface LogsCounter {
+ int count();
+}
diff --git a/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java
new file mode 100644
index 0000000000..b21f61fa88
--- /dev/null
+++ b/spring-5-data-reactive/src/main/java/com/baeldung/tailablecursor/service/WarnLogsCounter.java
@@ -0,0 +1,41 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import javax.annotation.PreDestroy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
+
+@Slf4j
+public class WarnLogsCounter implements LogsCounter {
+
+ private static final String LEVEL_FIELD_NAME = "level";
+
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Disposable subscription;
+
+ public WarnLogsCounter(ReactiveMongoTemplate template) {
+ Flux stream = template.tail(query(where(LEVEL_FIELD_NAME).is(LogLevel.WARN)), Log.class);
+ subscription = stream.subscribe(l -> {
+ log.warn("WARN log received: " + l);
+ counter.incrementAndGet();
+ });
+ }
+
+ @Override
+ public int count() {
+ return counter.get();
+ }
+
+ @PreDestroy
+ public void close() {
+ subscription.dispose();
+ }
+}
diff --git a/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java
new file mode 100644
index 0000000000..5e20d3ec79
--- /dev/null
+++ b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/ErrorLogsCounterManualTest.java
@@ -0,0 +1,112 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.mongodb.MongoClient;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.CreateCollectionOptions;
+import de.flapdoodle.embed.mongo.MongodExecutable;
+import de.flapdoodle.embed.mongo.MongodProcess;
+import de.flapdoodle.embed.mongo.MongodStarter;
+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
+import de.flapdoodle.embed.mongo.config.Net;
+import de.flapdoodle.embed.mongo.distribution.Version;
+import de.flapdoodle.embed.process.runtime.Network;
+import org.bson.Document;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.util.SocketUtils;
+
+import java.io.IOException;
+import java.util.stream.IntStream;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ErrorLogsCounterManualTest {
+
+ private static final String SERVER = "localhost";
+ private static final int PORT = SocketUtils.findAvailableTcpPort(10000);
+ private static final String DB_NAME = "test";
+ private static final String COLLECTION_NAME = Log.class.getName().toLowerCase();
+
+ private static final MongodStarter starter = MongodStarter.getDefaultInstance();
+ private static final int MAX_DOCUMENTS_IN_COLLECTION = 3;
+
+ private ErrorLogsCounter errorLogsCounter;
+ private MongodExecutable mongodExecutable;
+ private MongodProcess mongoDaemon;
+ private MongoDatabase db;
+
+ @Before
+ public void setup() throws Exception {
+ MongoTemplate mongoTemplate = initMongoTemplate();
+
+ MongoCollection collection = createCappedCollection();
+
+ persistDocument(collection, -1, LogLevel.ERROR, "my-service", "Initial log");
+
+ errorLogsCounter = new ErrorLogsCounter(mongoTemplate, COLLECTION_NAME);
+ Thread.sleep(1000L); // wait for initialization
+ }
+
+ private MongoTemplate initMongoTemplate() throws IOException {
+ mongodExecutable = starter.prepare(new MongodConfigBuilder()
+ .version(Version.Main.PRODUCTION)
+ .net(new Net(SERVER, PORT, Network.localhostIsIPv6()))
+ .build());
+ mongoDaemon = mongodExecutable.start();
+
+ MongoClient mongoClient = new MongoClient(SERVER, PORT);
+ db = mongoClient.getDatabase(DB_NAME);
+
+ return new MongoTemplate(mongoClient, DB_NAME);
+ }
+
+ private MongoCollection createCappedCollection() {
+ db.createCollection(COLLECTION_NAME, new CreateCollectionOptions()
+ .capped(true)
+ .sizeInBytes(100000)
+ .maxDocuments(MAX_DOCUMENTS_IN_COLLECTION));
+ return db.getCollection(COLLECTION_NAME);
+ }
+
+ private void persistDocument(MongoCollection collection,
+ int i, LogLevel level, String service, String message) {
+ Document logMessage = new Document();
+ logMessage.append("_id", i);
+ logMessage.append("level", level.toString());
+ logMessage.append("service", service);
+ logMessage.append("message", message);
+ collection.insertOne(logMessage);
+ }
+
+ @After
+ public void tearDown() {
+ errorLogsCounter.close();
+ mongoDaemon.stop();
+ mongodExecutable.stop();
+ }
+
+ @Test
+ public void whenErrorLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ MongoCollection collection = db.getCollection(COLLECTION_NAME);
+
+ IntStream.range(1, 10)
+ .forEach(i -> persistDocument(collection,
+ i,
+ i > 5 ? LogLevel.ERROR : LogLevel.INFO,
+ "service" + i,
+ "Message from service " + i)
+ );
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(collection.countDocuments(), is((long) MAX_DOCUMENTS_IN_COLLECTION));
+ assertThat(errorLogsCounter.count(), is(5));
+ }
+
+}
diff --git a/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java
new file mode 100644
index 0000000000..cd8bd68257
--- /dev/null
+++ b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/InfoLogsCounterManualTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.LogsCounterApplication;
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.CollectionOptions;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LogsCounterApplication.class)
+@Slf4j
+public class InfoLogsCounterManualTest {
+ @Autowired
+ private LogsRepository repository;
+
+ @Autowired
+ private ReactiveMongoTemplate template;
+
+ @Before
+ public void setUp() {
+ createCappedCollectionUsingReactiveMongoTemplate(template);
+
+ persistDocument(Log.builder()
+ .level(LogLevel.INFO)
+ .service("Service 2")
+ .message("Initial INFO message")
+ .build());
+ }
+
+ private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) {
+ reactiveMongoTemplate.dropCollection(Log.class).block();
+ reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty()
+ .maxDocuments(5)
+ .size(1024 * 1024L)
+ .capped()).block();
+ }
+
+ private void persistDocument(Log log) {
+ repository.save(log).block();
+ }
+
+ @Test
+ public void wheInfoLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ InfoLogsCounter infoLogsCounter = new InfoLogsCounter(repository);
+
+ Thread.sleep(1000L); // wait for initialization
+
+ Flux.range(0,10)
+ .map(i -> Log.builder()
+ .level(i > 5 ? LogLevel.WARN : LogLevel.INFO)
+ .service("some-service")
+ .message("some log message")
+ .build())
+ .map(entity -> repository.save(entity).subscribe())
+ .blockLast();
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(infoLogsCounter.count(), is(7));
+ infoLogsCounter.close();
+ }
+}
\ No newline at end of file
diff --git a/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java
new file mode 100644
index 0000000000..79d94b6784
--- /dev/null
+++ b/spring-5-data-reactive/src/test/java/com/baeldung/tailablecursor/service/WarnLogsCounterManualTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.tailablecursor.service;
+
+import com.baeldung.tailablecursor.LogsCounterApplication;
+import com.baeldung.tailablecursor.domain.Log;
+import com.baeldung.tailablecursor.domain.LogLevel;
+import com.baeldung.tailablecursor.repository.LogsRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.mongodb.core.CollectionOptions;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+import reactor.core.publisher.Flux;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LogsCounterApplication.class)
+@Slf4j
+public class WarnLogsCounterManualTest {
+ @Autowired
+ private LogsRepository repository;
+
+ @Autowired
+ private ReactiveMongoTemplate template;
+
+ @Before
+ public void setUp() {
+ createCappedCollectionUsingReactiveMongoTemplate(template);
+
+ persistDocument(Log.builder()
+ .level(LogLevel.WARN)
+ .service("Service 1")
+ .message("Initial Warn message")
+ .build());
+ }
+
+ private void createCappedCollectionUsingReactiveMongoTemplate(ReactiveMongoTemplate reactiveMongoTemplate) {
+ reactiveMongoTemplate.dropCollection(Log.class).block();
+ reactiveMongoTemplate.createCollection(Log.class, CollectionOptions.empty()
+ .maxDocuments(5)
+ .size(1024 * 1024L)
+ .capped()).block();
+ }
+
+ private void persistDocument(Log log) {
+ repository.save(log).block();
+ }
+
+ @Test
+ public void whenWarnLogsArePersisted_thenTheyAreReceivedByLogsCounter() throws Exception {
+ WarnLogsCounter warnLogsCounter = new WarnLogsCounter(template);
+
+ Thread.sleep(1000L); // wait for initialization
+
+ Flux.range(0,10)
+ .map(i -> Log.builder()
+ .level(i > 5 ? LogLevel.WARN : LogLevel.INFO)
+ .service("some-service")
+ .message("some log message")
+ .build())
+ .map(entity -> repository.save(entity).subscribe())
+ .blockLast();
+
+ Thread.sleep(1000L); // wait to receive all messages from the reactive mongodb driver
+
+ assertThat(warnLogsCounter.count(), is(5));
+ warnLogsCounter.close();
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-performance/pom.xml b/spring-boot-performance/pom.xml
new file mode 100644
index 0000000000..bbee7493cc
--- /dev/null
+++ b/spring-boot-performance/pom.xml
@@ -0,0 +1,45 @@
+
+ 4.0.0
+ spring-boot-performance
+ spring-boot-performance
+ war
+ This is a simple Spring Boot application taking advantage of the latest Spring Boot improvements/features. Current version: 2.2
+
+
+ parent-boot-performance
+ com.baeldung
+ 0.0.1-SNAPSHOT
+ ../parent-boot-performance
+
+
+
+
+ com.baeldung.lazyinitialization.Application
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ spring-boot-performance
+
+
+ src/main/resources
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java b/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java
new file mode 100644
index 0000000000..195b260399
--- /dev/null
+++ b/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/Application.java
@@ -0,0 +1,32 @@
+package com.baeldung.lazyinitialization;
+
+import com.baeldung.lazyinitialization.services.Writer;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class Application {
+
+ @Bean("writer1")
+ public Writer getWriter1() {
+ return new Writer("Writer 1");
+ }
+
+ @Bean("writer2")
+ public Writer getWriter2() {
+ return new Writer("Writer 2");
+ }
+
+ public static void main(String[] args) {
+ ApplicationContext ctx = SpringApplication.run(Application.class, args);
+ System.out.println("Application context initialized!!!");
+
+ Writer writer1 = ctx.getBean("writer1", Writer.class);
+ writer1.write("First message");
+
+ Writer writer2 = ctx.getBean("writer2", Writer.class);
+ writer2.write("Second message");
+ }
+}
diff --git a/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/services/Writer.java b/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/services/Writer.java
new file mode 100644
index 0000000000..7c67fb7ea4
--- /dev/null
+++ b/spring-boot-performance/src/main/java/com/baeldung/lazyinitialization/services/Writer.java
@@ -0,0 +1,16 @@
+package com.baeldung.lazyinitialization.services;
+
+public class Writer {
+
+ private final String writerId;
+
+ public Writer(String writerId) {
+ this.writerId = writerId;
+ System.out.println(writerId + " initialized!!!");
+ }
+
+ public void write(String message) {
+ System.out.println(writerId + ": " + message);
+ }
+
+}
diff --git a/spring-boot-performance/src/main/resources/application.yml b/spring-boot-performance/src/main/resources/application.yml
new file mode 100644
index 0000000000..25f03eed56
--- /dev/null
+++ b/spring-boot-performance/src/main/resources/application.yml
@@ -0,0 +1,3 @@
+spring:
+ main:
+ lazy-initialization: true
\ No newline at end of file
diff --git a/spring-static-resources/pom.xml b/spring-static-resources/pom.xml
index f01e807919..84519a37c1 100644
--- a/spring-static-resources/pom.xml
+++ b/spring-static-resources/pom.xml
@@ -140,6 +140,13 @@
handlebars
${handlebars.version}
+
+
+
+ commons-io
+ commons-io
+ ${commons.io.version}
+
org.springframework
@@ -208,6 +215,9 @@
1.5.1
+
+
+ 2.5
\ No newline at end of file
diff --git a/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/LoadResourceConfig.java b/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/LoadResourceConfig.java
new file mode 100644
index 0000000000..32b9ba84d0
--- /dev/null
+++ b/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/LoadResourceConfig.java
@@ -0,0 +1,15 @@
+package com.baeldung.loadresourceasstring;
+
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class LoadResourceConfig {
+
+ @Bean
+ public String resourceString() {
+ return ResourceReader.readFileToString("resource.txt");
+ }
+
+}
diff --git a/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/ResourceReader.java b/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/ResourceReader.java
new file mode 100644
index 0000000000..7bc1babe91
--- /dev/null
+++ b/spring-static-resources/src/main/java/com/baeldung/loadresourceasstring/ResourceReader.java
@@ -0,0 +1,30 @@
+package com.baeldung.loadresourceasstring;
+
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.util.FileCopyUtils;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+
+public class ResourceReader {
+
+ public static String readFileToString(String path) {
+ ResourceLoader resourceLoader = new DefaultResourceLoader();
+ Resource resource = resourceLoader.getResource(path);
+ return asString(resource);
+ }
+
+ public static String asString(Resource resource) {
+ try (Reader reader = new InputStreamReader(resource.getInputStream(), UTF_8)) {
+ return FileCopyUtils.copyToString(reader);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/spring-static-resources/src/main/resources/resource.txt b/spring-static-resources/src/main/resources/resource.txt
new file mode 100644
index 0000000000..e78cfa90c2
--- /dev/null
+++ b/spring-static-resources/src/main/resources/resource.txt
@@ -0,0 +1 @@
+This is a resource text file. This file will be loaded as a resource and use its contents as a string.
\ No newline at end of file
diff --git a/spring-static-resources/src/test/java/com/baeldung/loadresourceasstring/LoadResourceAsStringIntegrationTest.java b/spring-static-resources/src/test/java/com/baeldung/loadresourceasstring/LoadResourceAsStringIntegrationTest.java
new file mode 100644
index 0000000000..c16c1a9720
--- /dev/null
+++ b/spring-static-resources/src/test/java/com/baeldung/loadresourceasstring/LoadResourceAsStringIntegrationTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.loadresourceasstring;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+import org.springframework.util.FileCopyUtils;
+
+import java.io.InputStreamReader;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = LoadResourceConfig.class)
+public class LoadResourceAsStringIntegrationTest {
+
+ private static final String EXPECTED_RESOURCE_VALUE = "This is a resource text file. This file will be loaded as a " + "resource and use its contents as a string.";
+
+ @Value("#{T(com.baeldung.loadresourceasstring.ResourceReader).readFileToString('classpath:resource.txt')}")
+ private String resourceStringUsingSpel;
+
+ @Autowired
+ @Qualifier("resourceString")
+ private String resourceString;
+
+ @Autowired
+ private ResourceLoader resourceLoader;
+
+ @Test
+ public void givenUsingResourceLoadAndFileCopyUtils_whenConvertingAResourceToAString_thenCorrect() {
+ Resource resource = resourceLoader.getResource("classpath:resource.txt");
+ assertEquals(EXPECTED_RESOURCE_VALUE, ResourceReader.asString(resource));
+ }
+
+ @Test
+ public void givenUsingResourceStringBean_whenConvertingAResourceToAString_thenCorrect() {
+ assertEquals(EXPECTED_RESOURCE_VALUE, resourceString);
+ }
+
+ @Test
+ public void givenUsingSpel_whenConvertingAResourceToAString_thenCorrect() {
+ assertEquals(EXPECTED_RESOURCE_VALUE, resourceStringUsingSpel);
+ }
+
+
+
+
+}