diff --git a/README.md b/README.md index 1ae225b1f3..aecd561645 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,13 @@ Building a single module To build a specific module, run the command: `mvn clean install` in the module directory. +Building modules from the root of the repository +==================== +To build specific modules from the root of the repository, run the command: `mvn clean install --pl asm,atomikos -Pdefault-first` in the root directory. + +Here `asm` and `atomikos` are the modules that we want to build and `default-first` is the maven profile in which these modules are present. + + Running a Spring Boot module ==================== To run a Spring Boot module, run the command: `mvn spring-boot:run` in the module directory. diff --git a/apache-httpclient-2/src/test/java/com/baeldung/httpclient/cookies/HttpClientGettingCookieValueUnitTest.java b/apache-httpclient-2/src/test/java/com/baeldung/httpclient/cookies/HttpClientGettingCookieValueUnitTest.java index 404acb3098..265fa39816 100644 --- a/apache-httpclient-2/src/test/java/com/baeldung/httpclient/cookies/HttpClientGettingCookieValueUnitTest.java +++ b/apache-httpclient-2/src/test/java/com/baeldung/httpclient/cookies/HttpClientGettingCookieValueUnitTest.java @@ -1,46 +1,50 @@ package com.baeldung.httpclient.cookies; -import org.apache.http.client.CookieStore; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.cookie.ClientCookie; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.cookie.BasicClientCookie; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.cookie.BasicCookieStore; +import org.apache.hc.client5.http.cookie.Cookie; +import org.apache.hc.client5.http.cookie.CookieStore; + +import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; + +import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; +import org.apache.hc.client5.http.protocol.HttpClientContext; + import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import static org.junit.Assert.assertEquals; - - -public class HttpClientGettingCookieValueUnitTest { +class HttpClientGettingCookieValueUnitTest { private static Logger log = LoggerFactory.getLogger(HttpClientGettingCookieValueUnitTest.class); private static final String SAMPLE_URL = "http://www.baeldung.com/"; @Test - public final void whenSettingCustomCookieOnTheRequest_thenGettingTheSameCookieFromTheResponse() throws IOException { + void whenSettingCustomCookieOnTheRequest_thenGettingTheSameCookieFromTheResponse() throws IOException { + HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.COOKIE_STORE, createCustomCookieStore()); - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - try (CloseableHttpResponse response = httpClient.execute(new HttpGet(SAMPLE_URL), context)) { - CookieStore cookieStore = context.getCookieStore(); - Cookie customCookie = cookieStore.getCookies() - .stream() - .peek(cookie -> log.info("cookie name:{}", cookie.getName())) - .filter(cookie -> "custom_cookie".equals(cookie.getName())) - .findFirst() - .orElseThrow(IllegalStateException::new); + final HttpGet request = new HttpGet(SAMPLE_URL); - assertEquals("test_value", customCookie.getValue()); - } + try (CloseableHttpClient client = HttpClientBuilder.create() + .build()) { + client.execute(request, context, new BasicHttpClientResponseHandler()); + CookieStore cookieStore = context.getCookieStore(); + Cookie customCookie = cookieStore.getCookies() + .stream() + .peek(cookie -> log.info("cookie name:{}", cookie.getName())) + .filter(cookie -> "custom_cookie".equals(cookie.getName())) + .findFirst() + .orElseThrow(IllegalStateException::new); + + assertEquals("test_value", customCookie.getValue()); } } @@ -48,7 +52,7 @@ public class HttpClientGettingCookieValueUnitTest { BasicCookieStore cookieStore = new BasicCookieStore(); BasicClientCookie cookie = new BasicClientCookie("custom_cookie", "test_value"); cookie.setDomain("baeldung.com"); - cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "true"); + cookie.setAttribute("domain", "true"); cookie.setPath("/"); cookieStore.addCookie(cookie); return cookieStore; diff --git a/core-java-modules/core-java-8/README.md b/core-java-modules/core-java-8/README.md index ff4ceaf6db..6061f3318d 100644 --- a/core-java-modules/core-java-8/README.md +++ b/core-java-modules/core-java-8/README.md @@ -11,4 +11,5 @@ This module contains articles about Java 8 core features - [Finding Min/Max in an Array with Java](https://www.baeldung.com/java-array-min-max) - [Internationalization and Localization in Java 8](https://www.baeldung.com/java-8-localization) - [Generalized Target-Type Inference in Java](https://www.baeldung.com/java-generalized-target-type-inference) +- [Monads in Java](https://www.baeldung.com/java-monads) - [[More -->]](/core-java-modules/core-java-8-2) diff --git a/core-java-modules/core-java-8/src/main/java/com/baeldung/monad/MonadSamples.java b/core-java-modules/core-java-8/src/main/java/com/baeldung/monad/MonadSamples.java index 3ccbac2a3c..87b7fe697e 100644 --- a/core-java-modules/core-java-8/src/main/java/com/baeldung/monad/MonadSamples.java +++ b/core-java-modules/core-java-8/src/main/java/com/baeldung/monad/MonadSamples.java @@ -28,30 +28,16 @@ class MonadSample1 extends MonadBaseExample { public double apply(double n) { return subtract1(add3(divideBy2(multiplyBy2(multiplyBy2(2))))); } - - public static void main(String[] args) { - final MonadSample1 test = new MonadSample1(); - System.out.println(test.apply(2)); - //6.0 - } - } class MonadSample2 extends MonadBaseExample { public double apply(double n) { - final double n1 = multiplyBy2(n); - final double n2 = multiplyBy2(n1); - final double n3 = divideBy2(n2); - final double n4 = add3(n3); + double n1 = multiplyBy2(n); + double n2 = multiplyBy2(n1); + double n3 = divideBy2(n2); + double n4 = add3(n3); return subtract1(n4); } - - public static void main(String[] args) { - final MonadSample2 test = new MonadSample2(); - System.out.println(test.apply(2)); - //6.0 - } - } class MonadSample3 extends MonadBaseExample { @@ -66,11 +52,6 @@ class MonadSample3 extends MonadBaseExample { .get(); } - public static void main(String[] args) { - final MonadSample3 test = new MonadSample3(); - System.out.println(test.apply(2)); - //6.0 - } } class MonadSample4 extends MonadBaseExample { @@ -90,13 +71,6 @@ class MonadSample3 extends MonadBaseExample { return leftSide.equals(rightSide); } - public static void main(String[] args) { - final MonadSample4 test = new MonadSample4(); - - System.out.println(test.leftIdentity()); //true - System.out.println(test.rightIdentity()); //true - System.out.println(test.associativity()); //true - } } class MonadSample5 extends MonadBaseExample { @@ -104,10 +78,5 @@ class MonadSample5 extends MonadBaseExample { Function> mapping = value -> Optional.of(value == null ? -1 : value + 1); return Optional.ofNullable((Integer) null).flatMap(mapping).equals(mapping.apply(null)); } - - public static void main(String[] args) { - final MonadSample5 test = new MonadSample5(); - System.out.println(test.fail()); - } } diff --git a/core-java-modules/core-java-collections-array-list/README.md b/core-java-modules/core-java-collections-array-list/README.md index d24f7492bb..53568ca98a 100644 --- a/core-java-modules/core-java-collections-array-list/README.md +++ b/core-java-modules/core-java-collections-array-list/README.md @@ -9,3 +9,6 @@ This module contains articles about the Java ArrayList collection - [Multi Dimensional ArrayList in Java](https://www.baeldung.com/java-multi-dimensional-arraylist) - [Removing an Element From an ArrayList](https://www.baeldung.com/java-arraylist-remove-element) - [The Capacity of an ArrayList vs the Size of an Array in Java](https://www.baeldung.com/java-list-capacity-array-size) +- [Case-Insensitive Searching in ArrayList](https://www.baeldung.com/java-arraylist-case-insensitive-search) +- [Storing Data Triple in a List in Java](https://www.baeldung.com/java-list-storing-triple) +- [Convert an ArrayList of Object to an ArrayList of String Elements](https://www.baeldung.com/java-object-list-to-strings) diff --git a/core-java-modules/core-java-collections-array-list/pom.xml b/core-java-modules/core-java-collections-array-list/pom.xml index 6b040739e8..e3a115854c 100644 --- a/core-java-modules/core-java-collections-array-list/pom.xml +++ b/core-java-modules/core-java-collections-array-list/pom.xml @@ -5,6 +5,33 @@ 4.0.0 core-java-collections-array-list 0.1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven-compiler-plugin.source} + ${maven-compiler-plugin.target} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.plugin.version} + + + --add-opens java.base/java.util=ALL-UNNAMED + + + + + + + 16 + 16 + 3.0.0-M3 + core-java-collections-array-list jar @@ -20,6 +47,12 @@ commons-collections4 ${commons-collections4.version} + + com.google.guava + guava + 31.1-jre + test + \ No newline at end of file diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseSearchUtil.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseSearchUtil.java new file mode 100644 index 0000000000..38532ef031 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseSearchUtil.java @@ -0,0 +1,14 @@ +package com.baeldung.list.ignorecase; + +import java.util.List; + +public class IgnoreCaseSearchUtil { + public static boolean ignoreCaseContains(List theList, String searchStr) { + for (String s : theList) { + if (searchStr.equalsIgnoreCase(s)) { + return true; + } + } + return false; + } +} diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseStringList.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseStringList.java new file mode 100644 index 0000000000..a27c2650ed --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/list/ignorecase/IgnoreCaseStringList.java @@ -0,0 +1,29 @@ +package com.baeldung.list.ignorecase; + +import java.util.ArrayList; +import java.util.Collection; + +public class IgnoreCaseStringList extends ArrayList { + + public IgnoreCaseStringList() { + + } + + public IgnoreCaseStringList(Collection c) { + super(c); + } + + @Override + public boolean contains(Object o) { + String searchStr = (String) o; + // Using Stream API: + // return this.stream().anyMatch(searchStr::equalsIgnoreCase); + for (String s : this) { + if (searchStr.equalsIgnoreCase(s)) { + return true; + } + } + return false; + } + +} diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/Node.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/Node.java new file mode 100644 index 0000000000..3e2c5693de --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/Node.java @@ -0,0 +1,17 @@ +package com.baeldung.listofobjectstolistofstring; + +public class Node { + + private final int x; + private final int y; + + public Node(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public String toString() { + return "Node (" + "x=" + x + ", y=" + y + ')'; + } +} diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/User.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/User.java new file mode 100644 index 0000000000..eb9298bce0 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/listofobjectstolistofstring/User.java @@ -0,0 +1,14 @@ +package com.baeldung.listofobjectstolistofstring; + +public class User { + private final String fullName; + + public User(String fullName) { + this.fullName = fullName; + } + + @Override + public String toString() { + return "User (" + "full name='" + fullName + ')'; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/triple/Triple.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/triple/Triple.java new file mode 100644 index 0000000000..b19266ad21 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/triple/Triple.java @@ -0,0 +1,26 @@ +package com.baeldung.triple; + +public class Triple { + + private final L left; + private final M middle; + private final R right; + + public Triple(L left, M middle, R right) { + this.left = left; + this.middle = middle; + this.right = right; + } + + public L getLeft() { + return left; + } + + public M getMiddle() { + return middle; + } + + public R getRight() { + return right; + } +} diff --git a/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/list/ignorecase/IgnoreCaseContainsUnitTest.java b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/list/ignorecase/IgnoreCaseContainsUnitTest.java new file mode 100644 index 0000000000..5ca9bb1028 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/list/ignorecase/IgnoreCaseContainsUnitTest.java @@ -0,0 +1,45 @@ +package com.baeldung.list.ignorecase; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class IgnoreCaseContainsUnitTest { + private static final List LANGUAGES = Arrays.asList("Java", "Python", "Kotlin", "Ruby", "Javascript", "Go"); + + @Test + void givenStringList_whenCallTheStandardContains_shouldReturnFalse() { + String searchStr = "jAvA"; + boolean result = LANGUAGES.contains(searchStr); + assertFalse(result); + } + + @Test + void givenStringList_whenSearchIgnoreCaseUsingStreamAPI_shouldReturnTrue() { + String searchStr = "koTliN"; + boolean result = LANGUAGES.stream().anyMatch(searchStr::equalsIgnoreCase); + assertTrue(result); + } + + @Test + void givenStringList_whenUsingUtilClass_shouldReturnTrue() { + String searchStr = "ruBY"; + boolean result = IgnoreCaseSearchUtil.ignoreCaseContains(LANGUAGES, searchStr); + assertTrue(result); + } + + @Test + void givenStringList_whenUsingIgnoreCaseStringList_shouldReturnTrue() { + String searchStr = "pYtHoN"; + List ignoreCaseList = new IgnoreCaseStringList(LANGUAGES); + boolean result = ignoreCaseList.contains(searchStr); + assertTrue(result); + + boolean resultContainAll = ignoreCaseList.containsAll(Arrays.asList("pYtHon", "jAvA", "koTliN", "ruBY")); + assertTrue(resultContainAll); + } +} diff --git a/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/listofobjectstolistofstring/ConvertObjectListToStringListUnitTest.java b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/listofobjectstolistofstring/ConvertObjectListToStringListUnitTest.java new file mode 100644 index 0000000000..1d393a2945 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/listofobjectstolistofstring/ConvertObjectListToStringListUnitTest.java @@ -0,0 +1,92 @@ +package com.baeldung.listofobjectstolistofstring; + +import com.google.common.collect.Lists; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ConvertObjectListToStringListUnitTest { + + @Test + public void givenObjectList_whenForEachUsedToConvert_thenReturnSuccess() { + List outputList = new ArrayList<>(objectListWithNull().size()); + for (Object obj : objectListWithNull()) { + outputList.add(Objects.toString(obj, null)); + } + Assert.assertEquals(expectedStringListWithNull(), outputList); + } + + @Test + public void givenObjectList_whenUsingStreamsToConvert_thenReturnSuccess() { + List outputList; + outputList = objectListWithNull().stream() + .map((obj) -> Objects.toString(obj, null)) + .collect(Collectors.toList()); + Assert.assertEquals(expectedStringListWithNull(), outputList); + + } + + @Test + public void givenObjectList_whenUsingStreamsUnmodifiableListToConvert_thenReturnSuccess() { + List outputList; + outputList = objectListWithNull().stream() + .filter(Objects::nonNull) + .map((obj) -> Objects.toString(obj, null)) + .collect(Collectors.toUnmodifiableList()); + Assert.assertEquals(expectedStringListWithoutNull(), outputList); + + } + + @Test + public void givenObjectList_whenUsingGuavaTransform_thenReturnSuccess() { + List outputList; + outputList = Lists.transform(objectListWithNull(), obj -> Objects.toString(obj, null)); + Assert.assertEquals(expectedStringListWithNull(), outputList); + } + + @Test + public void givenObjectListWithNoNull_whenUsingToList_thenReturnSuccess() { + List outputList; + outputList = objectListWithoutNull().stream() + .map((obj) -> Objects.toString(obj, null)) + .toList(); + Assert.assertEquals(expectedStringListWithoutNull(), outputList); + } + + private List expectedStringListWithNull() { + List listOfStrings = new ArrayList<>(); + listOfStrings.add("1"); + listOfStrings.add("true"); + listOfStrings.add("hello"); + listOfStrings.add(Double.toString(273773.98)); + listOfStrings.add(null); + listOfStrings.add(new Node(2, 4).toString()); + listOfStrings.add(new User("John Doe").toString()); + return listOfStrings; + } + + private List objectListWithNull() { + List listOfStrings = new ArrayList<>(); + listOfStrings.add(1); + listOfStrings.add(true); + listOfStrings.add("hello"); + listOfStrings.add(Double.valueOf(273773.98)); + listOfStrings.add(null); + listOfStrings.add(new Node(2, 4)); + listOfStrings.add(new User("John Doe")); + return listOfStrings; + } + + private List expectedStringListWithoutNull() { + return List.of("1", "true", "hello", Double.toString(273773.98), new Node(2, 4).toString(), new User("John Doe").toString()); + } + + private List objectListWithoutNull() { + return List.of(1, true, "hello", Double.valueOf(273773.98), new Node(2, 4), new User("John Doe")); + } +} diff --git a/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/triple/TripleInListUnitTest.java b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/triple/TripleInListUnitTest.java new file mode 100644 index 0000000000..2196ae687f --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/triple/TripleInListUnitTest.java @@ -0,0 +1,105 @@ +package com.baeldung.triple; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + +public class TripleInListUnitTest { + + enum OP { + PLUS("+"), MINUS("-"), MULTIPLY("x"); + final String opSign; + + OP(String x) { + this.opSign = x; + } + } + + private String createQuestion(Long num1, OP operator, Long num2) { + long result; + switch (operator) { + case PLUS: + result = num1 + num2; + break; + case MINUS: + result = num1 - num2; + break; + case MULTIPLY: + result = num1 * num2; + break; + default: + throw new IllegalArgumentException("Unknown operator"); + } + return String.format("%d %s %d = ? ( answer: %d )", num1, operator.opSign, num2, result); + } + + private static final List EXPECTED_QUESTIONS = Arrays.asList( + "100 - 42 = ? ( answer: 58 )", + "100 + 42 = ? ( answer: 142 )", + "100 x 42 = ? ( answer: 4200 )"); + + @Test + void givenTripleValues_whenStoreAsList_thenTypeIsNotSafe() { + + List myTriple1 = new ArrayList(3); + myTriple1.add(100L); + myTriple1.add(OP.MINUS); + myTriple1.add(42L); + + List myTriple2 = new ArrayList(3); + myTriple2.add(100L); + myTriple2.add(OP.PLUS); + myTriple2.add(42L); + + List myTriple3 = new ArrayList(3); + myTriple3.add(100L); + myTriple3.add(OP.MULTIPLY); + myTriple3.add(42L); + + List listOfTriples = new ArrayList<>(Arrays.asList(myTriple1, myTriple2, myTriple3)); + + List oopsTriple = new ArrayList(3); + oopsTriple.add("Oops"); + oopsTriple.add(911L); + oopsTriple.add("The type is wrong"); + + listOfTriples.add(oopsTriple); + assertEquals(4, listOfTriples.size()); + + List questions = listOfTriples.stream() + .filter( + triple -> triple.size() == 3 + && triple.get(0) instanceof Long + && triple.get(1) instanceof OP + && triple.get(2) instanceof Long + ).map(triple -> { + Long left = (Long) triple.get(0); + OP op = (OP) triple.get(1); + Long right = (Long) triple.get(2); + return createQuestion(left, op, right); + }).collect(Collectors.toList()); + + assertEquals(EXPECTED_QUESTIONS, questions); + } + + @Test + void givenTripleValues_whenUsingTheTripleClass_thenTypeIsSafeAndNeat() { + Triple triple1 = new Triple<>(100L, OP.MINUS, 42L); + Triple triple2 = new Triple<>(100L, OP.PLUS, 42L); + Triple triple3 = new Triple<>(100L, OP.MULTIPLY, 42L); + Triple tripleOops = new Triple<>("Oops", 911L, "The type is wrong"); + + List> listOfTriples = new ArrayList<>(Arrays.asList(triple1, triple2, triple3)); + // listOfTriples.add(tripleOops); // Compiler error: "java: incompatible types ... " + + List questions = listOfTriples.stream() + .map(triple -> createQuestion(triple.getLeft(), triple.getMiddle(), triple.getRight())) + .collect(Collectors.toList()); + + assertEquals(EXPECTED_QUESTIONS, questions); + } +} diff --git a/core-java-modules/core-java-jvm-3/README.md b/core-java-modules/core-java-jvm-3/README.md index 5c694edaa0..0c8a325cf2 100644 --- a/core-java-modules/core-java-jvm-3/README.md +++ b/core-java-modules/core-java-jvm-3/README.md @@ -5,4 +5,5 @@ This module contains articles about working with the Java Virtual Machine (JVM). ### Relevant Articles: - [Difference Between Class.getResource() and ClassLoader.getResource()](https://www.baeldung.com/java-class-vs-classloader-getresource) +- [Compiling and Executing Code From a String in Java](https://www.baeldung.com/java-string-compile-execute-code) - More articles: [[<-- prev]](/core-java-modules/core-java-jvm-2) diff --git a/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClass.java b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClass.java new file mode 100644 index 0000000000..c61f28bb79 --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClass.java @@ -0,0 +1,6 @@ +package com.baeldung.inmemorycompilation; + +public interface InMemoryClass { + + void runCode(); +} diff --git a/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClassLoader.java b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClassLoader.java new file mode 100644 index 0000000000..b4951e9d91 --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryClassLoader.java @@ -0,0 +1,30 @@ +package com.baeldung.inmemorycompilation; + +import static java.util.Objects.requireNonNull; + +import java.util.Map; + +public class InMemoryClassLoader extends ClassLoader { + + private final InMemoryFileManager manager; + + public InMemoryClassLoader(ClassLoader parent, InMemoryFileManager manager) { + super(parent); + this.manager = requireNonNull(manager, "manager must not be null"); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + + Map compiledClasses = manager + .getBytesMap(); + + if (compiledClasses.containsKey(name)) { + byte[] bytes = compiledClasses.get(name) + .getBytes(); + return defineClass(name, bytes, 0, bytes.length); + } else { + throw new ClassNotFoundException(); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryFileManager.java b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryFileManager.java new file mode 100644 index 0000000000..34ad78856a --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/InMemoryFileManager.java @@ -0,0 +1,52 @@ +package com.baeldung.inmemorycompilation; + +import java.util.Hashtable; +import java.util.Map; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.StandardJavaFileManager; + +public class InMemoryFileManager extends ForwardingJavaFileManager { + + private final Map compiledClasses; + private final ClassLoader loader; + + public InMemoryFileManager(StandardJavaFileManager standardManager) { + super(standardManager); + this.compiledClasses = new Hashtable<>(); + this.loader = new InMemoryClassLoader(this.getClass() + .getClassLoader(), + this + ); + } + + /** + * Used to get the class loader for our compiled class. It creates an anonymous class extending + * the SecureClassLoader which uses the byte code created by the compiler and stored in the + * JavaClassObject, and returns the Class for it + * + * @param location where to place or search for file objects. + */ + @Override + public ClassLoader getClassLoader(Location location) { + return loader; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, + FileObject sibling) { + + JavaClassAsBytes classAsBytes = new JavaClassAsBytes( + className, kind); + compiledClasses.put(className, classAsBytes); + + return classAsBytes; + } + + public Map getBytesMap() { + return compiledClasses; + } +} diff --git a/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaClassAsBytes.java b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaClassAsBytes.java new file mode 100644 index 0000000000..b7af9a76ba --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaClassAsBytes.java @@ -0,0 +1,27 @@ +package com.baeldung.inmemorycompilation; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.URI; +import javax.tools.SimpleJavaFileObject; + +/** + * Represents a Java class file (compiled byte-code) + */ +public class JavaClassAsBytes extends SimpleJavaFileObject { + + protected final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + public JavaClassAsBytes(String name, Kind kind) { + super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind); + } + + public byte[] getBytes() { + return bos.toByteArray(); + } + + @Override + public OutputStream openOutputStream() { + return bos; + } +} diff --git a/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaSourceFromString.java b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaSourceFromString.java new file mode 100644 index 0000000000..eaa6c21b54 --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/main/java/com/baeldung/inmemorycompilation/JavaSourceFromString.java @@ -0,0 +1,26 @@ +package com.baeldung.inmemorycompilation; + +import static java.util.Objects.requireNonNull; + +import java.net.URI; +import javax.tools.SimpleJavaFileObject; + +/** + * Represents a Java source code file + */ +public class JavaSourceFromString extends SimpleJavaFileObject { + + private final String sourceCode; + + public JavaSourceFromString(String name, String sourceCode) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), + Kind.SOURCE + ); + this.sourceCode = requireNonNull(sourceCode, "sourceCode must not be null"); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return sourceCode; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/inmemorycompilation/InMemoryCompilationUnitTest.java b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/inmemorycompilation/InMemoryCompilationUnitTest.java new file mode 100644 index 0000000000..410bdca866 --- /dev/null +++ b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/inmemorycompilation/InMemoryCompilationUnitTest.java @@ -0,0 +1,57 @@ +package com.baeldung.inmemorycompilation; + +import java.util.Collections; +import java.util.List; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InMemoryCompilationUnitTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryCompilationUnitTest.class); + + final static String QUALIFIED_CLASS_NAME = "com.baeldung.inmemorycompilation.TestClass"; + + final static String SOURCE_CODE = + "package com.baeldung.inmemorycompilation;\n" + + "public class TestClass implements InMemoryClass {\n" + + "@Override\n" + + " public void runCode() {\n" + + " System.out.println(\"code is running...\");\n" + + " }\n" + + "}\n"; + + @Test + public void whenStringIsCompiled_ThenCodeShouldExecute() throws ClassNotFoundException, InstantiationException, IllegalAccessException { + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + InMemoryFileManager manager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null)); + + List sourceFiles = Collections.singletonList(new JavaSourceFromString(QUALIFIED_CLASS_NAME, SOURCE_CODE)); + + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, sourceFiles); + + boolean result = task.call(); + + if (result) { + diagnostics.getDiagnostics() + .forEach(d -> LOGGER.error(String.valueOf(d))); + } else { + ClassLoader classLoader = manager.getClassLoader(null); + Class clazz = classLoader.loadClass(QUALIFIED_CLASS_NAME); + InMemoryClass instanceOfClass = (InMemoryClass) clazz.newInstance(); + + Assertions.assertInstanceOf(InMemoryClass.class, instanceOfClass); + + instanceOfClass.runCode(); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java similarity index 93% rename from core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java rename to core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java index fb0c88f4bb..2fe512384a 100644 --- a/core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java +++ b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassGetResourceUnitTest.java @@ -1,10 +1,12 @@ package com.baeldung.resource; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.net.URL; +@Disabled class ClassGetResourceUnitTest { @Test diff --git a/core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java similarity index 93% rename from core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java rename to core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java index 54025c5404..d2d0600165 100644 --- a/core-java-modules/core-java-jvm-3/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java +++ b/core-java-modules/core-java-jvm-3/src/test/java/com/baeldung/resource/ClassLoaderGetResourceUnitTest.java @@ -1,10 +1,12 @@ package com.baeldung.resource; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.net.URL; +@Disabled class ClassLoaderGetResourceUnitTest { @Test diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/convertnumberbases/ConvertNumberBases.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/convertnumberbases/ConvertNumberBases.java index 405504a965..09b880cd19 100644 --- a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/convertnumberbases/ConvertNumberBases.java +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/convertnumberbases/ConvertNumberBases.java @@ -2,68 +2,76 @@ package com.baeldung.convertnumberbases; public class ConvertNumberBases { - public static String convertNumberToNewBase(String number, int base, int newBase){ + public static String convertNumberToNewBase(String number, int base, int newBase) { return Integer.toString(Integer.parseInt(number, base), newBase); } + public static String convertNumberToNewBaseCustom(String num, int base, int newBase) { int decimalNumber = convertFromAnyBaseToDecimal(num, base); - return convertFromDecimalToBaseX(decimalNumber, newBase); + String targetBase = ""; + try { + targetBase = convertFromDecimalToBaseX(decimalNumber, newBase); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + return targetBase; } - public static String convertFromDecimalToBaseX(int num, int newBase) { + public static String convertFromDecimalToBaseX(int num, int newBase) throws IllegalArgumentException { + if ((newBase < 2 || newBase > 10) && newBase != 16) { + throw new IllegalArgumentException("New base must be from 2 - 10 or 16"); + } String result = ""; int remainder; while (num > 0) { remainder = num % newBase; if (newBase == 16) { - if (remainder == 10) + if (remainder == 10) { result += 'A'; - else if (remainder == 11) + } else if (remainder == 11) { result += 'B'; - else if (remainder == 12) + } else if (remainder == 12) { result += 'C'; - else if (remainder == 13) + } else if (remainder == 13) { result += 'D'; - else if (remainder == 14) + } else if (remainder == 14) { result += 'E'; - else if (remainder == 15) + } else if (remainder == 15) { result += 'F'; - else + } else { result += remainder; - } else + } + } else { result += remainder; - + } num /= newBase; } return new StringBuffer(result).reverse().toString(); } public static int convertFromAnyBaseToDecimal(String num, int base) { - - if (base < 2 || (base > 10 && base != 16)) + if (base < 2 || (base > 10 && base != 16)) { return -1; - + } int val = 0; int power = 1; - for (int i = num.length() - 1; i >= 0; i--) { int digit = charToDecimal(num.charAt(i)); - - if (digit < 0 || digit >= base) + if (digit < 0 || digit >= base) { return -1; - + } val += digit * power; power = power * base; } - return val; } public static int charToDecimal(char c) { - if (c >= '0' && c <= '9') + if (c >= '0' && c <= '9') { return (int) c - '0'; - else + } else { return (int) c - 'A' + 10; + } } } diff --git a/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/convertnumberbases/ConvertNumberBasesUnitTest.java b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/convertnumberbases/ConvertNumberBasesUnitTest.java index 109e1da1b2..a666346b1a 100644 --- a/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/convertnumberbases/ConvertNumberBasesUnitTest.java +++ b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/convertnumberbases/ConvertNumberBasesUnitTest.java @@ -1,5 +1,6 @@ package com.baeldung.convertnumberbases; +import static com.baeldung.convertnumberbases.ConvertNumberBases.convertFromDecimalToBaseX; import static com.baeldung.convertnumberbases.ConvertNumberBases.convertNumberToNewBase; import static com.baeldung.convertnumberbases.ConvertNumberBases.convertNumberToNewBaseCustom; import static org.junit.jupiter.api.Assertions.*; @@ -17,4 +18,15 @@ class ConvertNumberBasesUnitTest { assertEquals(convertNumberToNewBaseCustom("11001000", 2, 8), "310"); } + @Test + void whenInputIsOutOfRange_thenIllegalArgumentExceptionIsThrown() { + Exception exception = assertThrows(IllegalArgumentException.class, ()-> { + convertFromDecimalToBaseX(100, 12); + }); + String expectedMessage = "New base must be from 2 - 10 or 16"; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + } + } \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-others/README.md b/core-java-modules/core-java-lang-oop-others/README.md index d3909c0014..b482669508 100644 --- a/core-java-modules/core-java-lang-oop-others/README.md +++ b/core-java-modules/core-java-lang-oop-others/README.md @@ -6,3 +6,4 @@ This module contains articles about Object Oriented Programming (OOP) in Java - [Object-Oriented-Programming Concepts in Java](https://www.baeldung.com/java-oop) - [Static and Dynamic Binding in Java](https://www.baeldung.com/java-static-dynamic-binding) - [Pass-By-Value as a Parameter Passing Mechanism in Java](https://www.baeldung.com/java-pass-by-value-or-pass-by-reference) +- [Check If All the Variables of an Object Are Null](https://www.baeldung.com/java-check-all-variables-object-null) diff --git a/core-java-modules/core-java-streams-4/README.md b/core-java-modules/core-java-streams-4/README.md index 187117d5d3..b210c2775a 100644 --- a/core-java-modules/core-java-streams-4/README.md +++ b/core-java-modules/core-java-streams-4/README.md @@ -5,3 +5,4 @@ - [Filter Java Stream to 1 and Only 1 Element](https://www.baeldung.com/java-filter-stream-unique-element) - [Java 8 Streams: Multiple Filters vs. Complex Condition](https://www.baeldung.com/java-streams-multiple-filters-vs-condition) - [Finding Max Date in List Using Streams](https://www.baeldung.com/java-max-date-list-streams) +- [Batch Processing of Stream Data in Java](https://www.baeldung.com/java-stream-batch-processing) diff --git a/core-java-modules/core-java-streams-4/pom.xml b/core-java-modules/core-java-streams-4/pom.xml index ed4603796d..46c0f3f7e1 100644 --- a/core-java-modules/core-java-streams-4/pom.xml +++ b/core-java-modules/core-java-streams-4/pom.xml @@ -59,6 +59,36 @@ 3.12.0 test + + io.reactivex.rxjava3 + rxjava + ${rx.java3.version} + + + io.vavr + vavr + ${io.varv.version} + + + io.projectreactor + reactor-core + ${io.reactor3.version} + + + org.apache.commons + commons-collections4 + ${apache.commons.collection4.version} + + + com.google.guava + guava + ${google.guava.version} + + + com.oath.cyclops + cyclops + ${cyclops.version} + @@ -90,6 +120,12 @@ 12 1.2.5 2.2.2 + 3.1.5 + 1.0.0-alpha-4 + 3.5.1 + 4.4 + 31.1-jre + 10.4.1 \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/processing/CustomBatchIterator.java b/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/processing/CustomBatchIterator.java new file mode 100644 index 0000000000..b5407b7283 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/processing/CustomBatchIterator.java @@ -0,0 +1,47 @@ +package com.baeldung.streams.processing; + +import static java.util.Spliterator.ORDERED; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class CustomBatchIterator implements Iterator> { + private final int batchSize; + private List currentBatch; + private final Iterator iterator; + + public CustomBatchIterator(Iterator sourceIterator, int batchSize) { + this.batchSize = batchSize; + this.iterator = sourceIterator; + } + + @Override + public List next() { + return currentBatch; + } + + @Override + public boolean hasNext() { + prepareNextBatch(); + return currentBatch != null && !currentBatch.isEmpty(); + } + + public static Stream> batchStreamOf(Stream stream, int batchSize) { + return stream(new CustomBatchIterator<>(stream.iterator(), batchSize)); + } + + private static Stream stream(Iterator iterator) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, ORDERED), false); + } + + private void prepareNextBatch() { + currentBatch = new ArrayList<>(batchSize); + while (iterator.hasNext() && currentBatch.size() < batchSize) { + currentBatch.add(iterator.next()); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/StreamProcessingUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/StreamProcessingUnitTest.java new file mode 100644 index 0000000000..f8f88387d5 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/StreamProcessingUnitTest.java @@ -0,0 +1,141 @@ +package com.baeldung.streams.processing; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.apache.commons.collections4.ListUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.Iterators; + +import cyclops.data.LazySeq; +import cyclops.reactive.ReactiveSeq; +import io.reactivex.rxjava3.core.Observable; +import reactor.core.publisher.Flux; + +public class StreamProcessingUnitTest { + public final int BATCH_SIZE = 10; + + private final List firstBatch = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + private final List secondBatch = List.of(10, 11, 12, 13, 14, 15, 16, 17, 18, 19); + private final List thirdBatch = List.of(20, 21, 22, 23, 24, 25, 26, 27, 28, 29); + private final List fourthBatch = List.of(30, 31, 32, 33); + + public Stream data; + + @BeforeEach + public void setUp() { + data = IntStream.range(0, 34) + .boxed(); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingSpliterator_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(); + CustomBatchIterator.batchStreamOf(data, BATCH_SIZE) + .forEach(result::add); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingCollectionAPI_thenFourBatchesAreObtained() { + Collection> result = data.collect(Collectors.groupingBy(it -> it / BATCH_SIZE)) + .values(); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchParallelUsingCollectionAPI_thenFourBatchesAreObtained() { + Collection> result = data.parallel() + .collect(Collectors.groupingBy(it -> it / BATCH_SIZE)) + .values(); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingRxJavaV3_thenFourBatchesAreObtained() { + // RxJava v3 + Collection> result = new ArrayList<>(); + Observable.fromStream(data) + .buffer(BATCH_SIZE) + .subscribe(result::add); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingReactor_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(); + Flux.fromStream(data) + .buffer(BATCH_SIZE) + .subscribe(result::add); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingApacheCommon_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(ListUtils.partition(data.collect(Collectors.toList()), BATCH_SIZE)); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingGuava_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(); + Iterators.partition(data.iterator(), BATCH_SIZE) + .forEachRemaining(result::add); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingCyclops_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(); + ReactiveSeq.fromStream(data) + .grouped(BATCH_SIZE) + .toList() + .forEach(value -> result.add(value.collect(Collectors.toList()))); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingCyclopsLazy_thenFourBatchesAreObtained() { + Collection> result = new ArrayList<>(); + LazySeq.fromStream(data) + .grouped(BATCH_SIZE) + .toList() + .forEach(value -> result.add(value.collect(Collectors.toList()))); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/vavr/StreamProcessingWithVavrUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/vavr/StreamProcessingWithVavrUnitTest.java new file mode 100644 index 0000000000..859b059889 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/processing/vavr/StreamProcessingWithVavrUnitTest.java @@ -0,0 +1,30 @@ +package com.baeldung.streams.processing.vavr; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import com.baeldung.streams.processing.StreamProcessingUnitTest; + +import io.vavr.collection.List; +import io.vavr.collection.Stream; + +public class StreamProcessingWithVavrUnitTest extends StreamProcessingUnitTest { + + private final List firstBatch = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + private final List secondBatch = List.of(10, 11, 12, 13, 14, 15, 16, 17, 18, 19); + private final List thirdBatch = List.of(20, 21, 22, 23, 24, 25, 26, 27, 28, 29); + private final List fourthBatch = List.of(30, 31, 32, 33); + + @Test + public void givenAStreamOfData_whenIsProcessingInBatchUsingVavr_thenFourBatchesAreObtained() { + List> result = Stream.ofAll(data) + .toList() + .grouped(BATCH_SIZE) + .toList(); + assertTrue(result.contains(firstBatch)); + assertTrue(result.contains(secondBatch)); + assertTrue(result.contains(thirdBatch)); + assertTrue(result.contains(fourthBatch)); + } +} diff --git a/core-java-modules/core-java-uuid/src/main/java/com/baeldung/uuid/UUIDGenerator.java b/core-java-modules/core-java-uuid/src/main/java/com/baeldung/uuid/UUIDGenerator.java index 2170a72644..68ff9ce1d4 100644 --- a/core-java-modules/core-java-uuid/src/main/java/com/baeldung/uuid/UUIDGenerator.java +++ b/core-java-modules/core-java-uuid/src/main/java/com/baeldung/uuid/UUIDGenerator.java @@ -27,17 +27,17 @@ public final class UUIDGenerator { private static long get64LeastSignificantBitsForVersion1() { final long random63BitLong = new Random().nextLong() & 0x3FFFFFFFFFFFFFFFL; - final long variant3BitFlag = 0x8000000000000000L; - return random63BitLong + variant3BitFlag; + long variant3BitFlag = 0x8000000000000000L; + return random63BitLong | variant3BitFlag; } private static long get64MostSignificantBitsForVersion1() { - final long timeForUuidIn100Nanos = System.currentTimeMillis(); - final long time_low = (timeForUuidIn100Nanos & 0x0000_0000_FFFF_FFFFL) << 32; - final long time_mid = ((timeForUuidIn100Nanos >> 32) & 0xFFFF) << 16; + final long currentTimeMillis = System.currentTimeMillis(); + final long time_low = (currentTimeMillis & 0x0000_0000_FFFF_FFFFL) << 32; + final long time_mid = ((currentTimeMillis >> 32) & 0xFFFF) << 16; final long version = 1 << 12; - final long time_hi = ((timeForUuidIn100Nanos >> 48) & 0x0FFF); - return time_low + time_mid + version + time_hi; + final long time_high = ((currentTimeMillis >> 48) & 0x0FFF); + return time_low | time_mid | version | time_high; } /** diff --git a/core-java-modules/core-java/README.md b/core-java-modules/core-java/README.md index 087c5d356e..0000962164 100644 --- a/core-java-modules/core-java/README.md +++ b/core-java-modules/core-java/README.md @@ -9,3 +9,4 @@ - [A Guide to the ResourceBundle](http://www.baeldung.com/java-resourcebundle) - [Merging java.util.Properties Objects](https://www.baeldung.com/java-merging-properties) - [Illegal Character Compilation Error](https://www.baeldung.com/java-illegal-character-error) +- [Lambda Expression vs. Anonymous Inner Class](https://www.baeldung.com/java-lambdas-vs-anonymous-class) diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index cc137d08b6..bbbca6adf5 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -31,7 +31,6 @@ core-java-collections-2 core-java-collections-3 core-java-collections-4 - core-java-collections-array-list core-java-collections-conversions core-java-collections-conversions-2 core-java-collections-set-2 diff --git a/httpclient-simple/pom.xml b/httpclient-simple/pom.xml index 45da1e7a39..c39983564f 100644 --- a/httpclient-simple/pom.xml +++ b/httpclient-simple/pom.xml @@ -112,6 +112,18 @@ + + + org.apache.httpcomponents.client5 + httpclient5-fluent + ${httpclient5-fluent.version} + + + commons-logging + commons-logging + + + org.apache.commons @@ -308,6 +320,7 @@ 5.2 5.2 + 5.2 1.6.1 diff --git a/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientPostingLiveTest.java b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientPostingLiveTest.java index f5dff8d757..b30921c990 100644 --- a/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientPostingLiveTest.java +++ b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientPostingLiveTest.java @@ -1,74 +1,88 @@ package com.baeldung.httpclient; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.auth.AuthenticationException; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.fluent.Form; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.message.BasicNameValuePair; -import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; +import org.apache.hc.client5.http.fluent.Form; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.client5.http.fluent.Request; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicNameValuePair; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; +import com.baeldung.handler.CustomHttpClientResponseHandler; /* * NOTE : Need module spring-rest to be running */ -public class HttpClientPostingLiveTest { +class HttpClientPostingLiveTest { private static final String SAMPLE_URL = "http://www.example.com"; private static final String URL_SECURED_BY_BASIC_AUTHENTICATION = "http://browserspy.dk/password-ok.php"; private static final String DEFAULT_USER = "test"; private static final String DEFAULT_PASS = "test"; @Test - public void whenSendPostRequestUsingHttpClient_thenCorrect() throws IOException { - final CloseableHttpClient client = HttpClients.createDefault(); + void whenSendPostRequestUsingHttpClient_thenCorrect() throws IOException { final HttpPost httpPost = new HttpPost(SAMPLE_URL); - final List params = new ArrayList(); params.add(new BasicNameValuePair("username", DEFAULT_USER)); params.add(new BasicNameValuePair("password", DEFAULT_PASS)); httpPost.setEntity(new UrlEncodedFormEntity(params)); - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public void whenSendPostRequestWithAuthorizationUsingHttpClient_thenCorrect() throws IOException, AuthenticationException { - final CloseableHttpClient client = HttpClients.createDefault(); + void whenSendPostRequestWithAuthorizationUsingHttpClient_thenCorrect() throws IOException { final HttpPost httpPost = new HttpPost(URL_SECURED_BY_BASIC_AUTHENTICATION); - httpPost.setEntity(new StringEntity("test post")); - final UsernamePasswordCredentials creds = new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS); - httpPost.addHeader(new BasicScheme().authenticate(creds, httpPost, null)); - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); + final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + final UsernamePasswordCredentials credentials = + new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS.toCharArray()); + + credsProvider.setCredentials(new AuthScope(URL_SECURED_BY_BASIC_AUTHENTICATION, 80) ,credentials); + + try (CloseableHttpClient client = HttpClients.custom() + .setDefaultCredentialsProvider(credsProvider) + .build(); + + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public void whenPostJsonUsingHttpClient_thenCorrect() throws IOException { - final CloseableHttpClient client = HttpClients.createDefault(); + void whenPostJsonUsingHttpClient_thenCorrect() throws IOException { final HttpPost httpPost = new HttpPost(SAMPLE_URL); final String json = "{\"id\":1,\"name\":\"John\"}"; @@ -77,68 +91,91 @@ public class HttpClientPostingLiveTest { httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-type", "application/json"); - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public void whenPostFormUsingHttpClientFluentAPI_thenCorrect() throws IOException { - final HttpResponse response = Request.Post(SAMPLE_URL).bodyForm(Form.form().add("username", DEFAULT_USER).add("password", DEFAULT_PASS).build()).execute().returnResponse(); + void whenPostFormUsingHttpClientFluentAPI_thenCorrect() throws IOException { + Request request = Request.post(SAMPLE_URL) + .bodyForm(Form.form() + .add("username", DEFAULT_USER) + .add("password", DEFAULT_PASS) + .build()); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + HttpResponse response = request.execute() + .returnResponse(); + assertThat(response.getCode(), equalTo(HttpStatus.SC_OK)); } @Test - public void whenSendMultipartRequestUsingHttpClient_thenCorrect() throws IOException { - final CloseableHttpClient client = HttpClients.createDefault(); + void whenSendMultipartRequestUsingHttpClient_thenCorrect() throws IOException { final HttpPost httpPost = new HttpPost(SAMPLE_URL); final MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addTextBody("username", DEFAULT_USER); builder.addTextBody("password", DEFAULT_PASS); - builder.addBinaryBody("file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); + builder.addBinaryBody( + "file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); + + final HttpEntity multipart = builder.build(); + httpPost.setEntity(multipart); + + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } + } + + @Test + void whenUploadFileUsingHttpClient_thenCorrect() throws IOException { + final HttpPost httpPost = new HttpPost(SAMPLE_URL); + + final MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.addBinaryBody( + "file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); final HttpEntity multipart = builder.build(); httpPost.setEntity(multipart); - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public void whenUploadFileUsingHttpClient_thenCorrect() throws IOException { - final CloseableHttpClient client = HttpClients.createDefault(); + void whenGetUploadFileProgressUsingHttpClient_thenCorrect() throws IOException { final HttpPost httpPost = new HttpPost(SAMPLE_URL); final MultipartEntityBuilder builder = MultipartEntityBuilder.create(); - builder.addBinaryBody("file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); + builder.addBinaryBody( + "file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); final HttpEntity multipart = builder.build(); - httpPost.setEntity(multipart); - - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); - } - - @Test - public void whenGetUploadFileProgressUsingHttpClient_thenCorrect() throws IOException { - final CloseableHttpClient client = HttpClients.createDefault(); - final HttpPost httpPost = new HttpPost(SAMPLE_URL); - - final MultipartEntityBuilder builder = MultipartEntityBuilder.create(); - builder.addBinaryBody("file", new File("src/test/resources/test.in"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); - final HttpEntity multipart = builder.build(); - - final ProgressEntityWrapper.ProgressListener pListener = percentage -> assertFalse(Float.compare(percentage, 100) > 0); + final ProgressEntityWrapper.ProgressListener pListener = + percentage -> assertFalse(Float.compare(percentage, 100) > 0); httpPost.setEntity(new ProgressEntityWrapper(multipart, pListener)); - final CloseableHttpResponse response = client.execute(httpPost); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - client.close(); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(httpPost, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } } \ No newline at end of file diff --git a/httpclient-simple/src/test/java/com/baeldung/httpclient/ProgressEntityWrapper.java b/httpclient-simple/src/test/java/com/baeldung/httpclient/ProgressEntityWrapper.java index c7adf51b3e..0fbc6e5dcd 100644 --- a/httpclient-simple/src/test/java/com/baeldung/httpclient/ProgressEntityWrapper.java +++ b/httpclient-simple/src/test/java/com/baeldung/httpclient/ProgressEntityWrapper.java @@ -4,8 +4,8 @@ import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; -import org.apache.http.HttpEntity; -import org.apache.http.entity.HttpEntityWrapper; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; public class ProgressEntityWrapper extends HttpEntityWrapper { private final ProgressListener listener; diff --git a/httpclient-simple/src/test/java/com/baeldung/httpclient/sec/HttpClientAuthLiveTest.java b/httpclient-simple/src/test/java/com/baeldung/httpclient/sec/HttpClientAuthLiveTest.java index 0f7018a9ac..35af84679b 100644 --- a/httpclient-simple/src/test/java/com/baeldung/httpclient/sec/HttpClientAuthLiveTest.java +++ b/httpclient-simple/src/test/java/com/baeldung/httpclient/sec/HttpClientAuthLiveTest.java @@ -1,118 +1,119 @@ package com.baeldung.httpclient.sec; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + import org.apache.commons.codec.binary.Base64; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpHost; -import org.apache.http.HttpStatus; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicAuthCache; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.protocol.HttpContext; -import com.baeldung.httpclient.ResponseUtil; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; + +import com.baeldung.handler.CustomHttpClientResponseHandler; + +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.AuthCache; +import org.apache.hc.client5.http.auth.CredentialsProvider; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.auth.BasicAuthCache; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.auth.BasicScheme; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.protocol.HttpClientContext; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.protocol.HttpContext; + +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; - /* * NOTE : Need module httpclient-simple to be running */ - -public class HttpClientAuthLiveTest { +class HttpClientAuthLiveTest { private static final String URL_SECURED_BY_BASIC_AUTHENTICATION = "http://localhost:8082/httpclient-simple/api/foos/1"; private static final String DEFAULT_USER = "user1"; private static final String DEFAULT_PASS = "user1Pass"; - - private CloseableHttpClient client; - - private CloseableHttpResponse response; - - @Before - public final void before() { - client = HttpClientBuilder.create().build(); - } - - @After - public final void after() throws IllegalStateException, IOException { - ResponseUtil.closeResponse(response); - } - - // tests + private final char[] DEFAULT_PASS_ARRAY = DEFAULT_PASS.toCharArray() ; @Test - public final void whenExecutingBasicGetRequestWithBasicAuthenticationEnabled_thenSuccess() throws IOException { - client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider()).build(); + final void whenExecutingBasicGetRequestWithBasicAuthenticationEnabled_thenSuccess() throws IOException { + final HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION); + try (CloseableHttpClient client = HttpClientBuilder.create() + .setDefaultCredentialsProvider(provider()) + .build(); - response = client.execute(new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION)); - - final int statusCode = response.getStatusLine().getStatusCode(); - assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, new CustomHttpClientResponseHandler())) { + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public final void givenAuthenticationIsPreemptive_whenExecutingBasicGetRequestWithBasicAuthenticationEnabled_thenSuccess() throws IOException { - client = HttpClientBuilder.create().build(); - response = client.execute(new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION), context()); + final void givenAuthenticationIsPreemptive_whenExecutingBasicGetRequestWithBasicAuthenticationEnabled_thenSuccess() throws IOException { + final HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION); + try (CloseableHttpClient client = HttpClientBuilder.create() + .build(); - final int statusCode = response.getStatusLine().getStatusCode(); - assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, context(), new CustomHttpClientResponseHandler())) { + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(200)); + } } @Test - public final void givenAuthorizationHeaderIsSetManually_whenExecutingGetRequest_thenSuccess() throws IOException { - client = HttpClientBuilder.create().build(); - + final void givenAuthorizationHeaderIsSetManually_whenExecutingGetRequest_thenSuccess() throws IOException { final HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION); request.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader(DEFAULT_USER, DEFAULT_PASS)); - response = client.execute(request); + try (CloseableHttpClient client = HttpClientBuilder.create() + .build(); - final int statusCode = response.getStatusLine().getStatusCode(); - assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, context(), new CustomHttpClientResponseHandler())) { + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public final void givenAuthorizationHeaderIsSetManually_whenExecutingGetRequest_thenSuccess2() throws IOException { + final void givenAuthorizationHeaderIsSetManually_whenExecutingGetRequest_thenSuccess2() throws IOException { final HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION); final String auth = DEFAULT_USER + ":" + DEFAULT_PASS; final byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1)); final String authHeader = "Basic " + new String(encodedAuth); request.setHeader(HttpHeaders.AUTHORIZATION, authHeader); - client = HttpClientBuilder.create().build(); - response = client.execute(request); + try (CloseableHttpClient client = HttpClientBuilder.create() + .build(); - final int statusCode = response.getStatusLine().getStatusCode(); - assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, new CustomHttpClientResponseHandler())) { + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } // UTILS private CredentialsProvider provider() { - final CredentialsProvider provider = new BasicCredentialsProvider(); - final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS); - provider.setCredentials(AuthScope.ANY, credentials); + final HttpHost targetHost = new HttpHost("http", "localhost", 8082); + final BasicCredentialsProvider provider = new BasicCredentialsProvider(); + AuthScope authScope = new AuthScope(targetHost); + provider.setCredentials(authScope, new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS_ARRAY)); return provider; } private HttpContext context() { - final HttpHost targetHost = new HttpHost("localhost", 8082, "http"); - final CredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS)); + final HttpHost targetHost = new HttpHost("http", "localhost", 8082); + final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + AuthScope authScope = new AuthScope(targetHost); + credsProvider.setCredentials(authScope, new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS_ARRAY)); // Create AuthCache instance final AuthCache authCache = new BasicAuthCache(); diff --git a/jackson-modules/jackson-annotations/README.md b/jackson-modules/jackson-annotations/README.md index 3b6cd6f20b..8b405ec778 100644 --- a/jackson-modules/jackson-annotations/README.md +++ b/jackson-modules/jackson-annotations/README.md @@ -8,3 +8,4 @@ This module contains articles about Jackson annotations. - [Jackson – Bidirectional Relationships](https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion) - [Jackson JSON Views](https://www.baeldung.com/jackson-json-view-annotation) - [Deduction-Based Polymorphism in Jackson 2.12](https://www.baeldung.com/jackson-deduction-based-polymorphism) +- [@JsonIgnore vs @Transient](https://www.baeldung.com/java-jsonignore-vs-transient) diff --git a/jenkins-modules/jenkins-jobs/README.md b/jenkins-modules/jenkins-jobs/README.md index 7b33dc6d99..d29a25244f 100644 --- a/jenkins-modules/jenkins-jobs/README.md +++ b/jenkins-modules/jenkins-jobs/README.md @@ -2,3 +2,4 @@ - [Trigger Another Job from a Jenkins Pipeline](https://www.baeldung.com/ops/jenkins-pipeline-trigger-new-job) - [Fixing the “No Such DSL method” Error in Jenkins Pipeline](https://www.baeldung.com/ops/jenkins-pipeline-no-such-dsl-method-error) - [Jenkins Pipeline – Change to Another Folder](https://www.baeldung.com/ops/jenkins-pipeline-change-to-another-folder) +- [How to Stop a Zombie Job on Jenkins Without Restarting the Server?](https://www.baeldung.com/ops/stop-zombie-job-on-jenkins-without-restarting-the-server) diff --git a/jenkins-modules/jenkins-jobs/prevent-build-failure-job/pipeline-prevent-build-failure-job b/jenkins-modules/jenkins-jobs/prevent-build-failure-job/pipeline-prevent-build-failure-job new file mode 100644 index 0000000000..b143f31dc7 --- /dev/null +++ b/jenkins-modules/jenkins-jobs/prevent-build-failure-job/pipeline-prevent-build-failure-job @@ -0,0 +1,16 @@ +pipeline { + agent any + stages { + stage('tryCatch') { + steps { + script { + try { + sh 'test_script.sh' + } catch (e) { + echo "An error occurred: ${e}" + } + } + } + } + } +} diff --git a/json-modules/json-2/src/main/java/com/baeldung/jsonvalidation/JacksonValidator.java b/json-modules/json-2/src/main/java/com/baeldung/jsonvalidation/JacksonValidator.java index 8c339f46c8..4385b39bb3 100644 --- a/json-modules/json-2/src/main/java/com/baeldung/jsonvalidation/JacksonValidator.java +++ b/json-modules/json-2/src/main/java/com/baeldung/jsonvalidation/JacksonValidator.java @@ -1,11 +1,15 @@ package com.baeldung.jsonvalidation; import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; public class JacksonValidator { - final ObjectMapper mapper = new ObjectMapper(); + final ObjectMapper mapper = JsonMapper.builder() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); public boolean isValid(String json) { try { diff --git a/logging-modules/tinylog2/README.md b/logging-modules/tinylog2/README.md new file mode 100644 index 0000000000..880ff8e65f --- /dev/null +++ b/logging-modules/tinylog2/README.md @@ -0,0 +1,2 @@ +## Relevant Articles +- [Lightweight Logging With tinylog 2](https://www.baeldung.com/java-logging-tinylog-guide) diff --git a/microservices-modules/rest-express/README.md b/microservices-modules/rest-express/README.md index 0fdff99fb7..a3340b238d 100644 --- a/microservices-modules/rest-express/README.md +++ b/microservices-modules/rest-express/README.md @@ -2,4 +2,5 @@ This module contains articles about RestExpress. -### Relevant articles \ No newline at end of file +### Relevant articles +- [RESTful Microservices With RestExpress](https://www.baeldung.com/java-restexpress-guide) diff --git a/persistence-modules/flyway/README.md b/persistence-modules/flyway/README.md index bd5f9bbe03..5fbbc7df9c 100644 --- a/persistence-modules/flyway/README.md +++ b/persistence-modules/flyway/README.md @@ -2,3 +2,4 @@ - [Database Migrations with Flyway](http://www.baeldung.com/database-migrations-with-flyway) - [A Guide to Flyway Callbacks](http://www.baeldung.com/flyway-callbacks) - [Rolling Back Migrations with Flyway](https://www.baeldung.com/flyway-roll-back) +- [Flyway Out of Order Migrations](https://www.baeldung.com/flyway-migrations) diff --git a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java index 51bbe73e9e..7f6653d7a8 100644 --- a/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java +++ b/persistence-modules/spring-data-elasticsearch/src/main/java/com/baeldung/spring/data/es/config/Config.java @@ -6,17 +6,17 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; -import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; @Configuration @EnableElasticsearchRepositories(basePackages = "com.baeldung.spring.data.es.repository") @ComponentScan(basePackages = { "com.baeldung.spring.data.es.service" }) -public class Config { +public class Config extends AbstractElasticsearchConfiguration { @Bean - RestHighLevelClient client() { + @Override + public RestHighLevelClient elasticsearchClient() { ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") .build(); @@ -24,9 +24,4 @@ public class Config { return RestClients.create(clientConfiguration) .rest(); } - - @Bean - public ElasticsearchOperations elasticsearchTemplate() { - return new ElasticsearchRestTemplate(client()); - } } diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java index 412cd04e09..cc38acc41c 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchManualTest.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; @@ -41,7 +41,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; public class ElasticSearchManualTest { @Autowired - private ElasticsearchRestTemplate elasticsearchTemplate; + private ElasticsearchOperations elasticsearchOperations; @Autowired private ArticleRepository articleRepository; @@ -117,7 +117,7 @@ public class ElasticSearchManualTest { final Query searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); } @@ -126,7 +126,7 @@ public class ElasticSearchManualTest { public void givenSavedDoc_whenTitleUpdated_thenCouldFindByUpdatedTitle() { final Query searchQuery = new NativeSearchQueryBuilder().withQuery(fuzzyQuery("title", "serch")) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); @@ -147,7 +147,7 @@ public class ElasticSearchManualTest { final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); final long count = articleRepository.count(); @@ -162,7 +162,7 @@ public class ElasticSearchManualTest { public void givenSavedDoc_whenOneTermMatches_thenFindByTitle() { final Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); } } diff --git a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java index aaf0c80097..03c8da80c9 100644 --- a/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java +++ b/persistence-modules/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryManualTest.java @@ -39,7 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; @@ -58,7 +58,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; public class ElasticSearchQueryManualTest { @Autowired - private ElasticsearchRestTemplate elasticsearchTemplate; + private ElasticsearchOperations elasticsearchOperations; @Autowired private ArticleRepository articleRepository; @@ -101,7 +101,7 @@ public class ElasticSearchQueryManualTest { public void givenFullTitle_whenRunMatchQuery_thenDocIsFound() { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(Operator.AND)) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); } @@ -110,7 +110,7 @@ public class ElasticSearchQueryManualTest { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Engines Solutions")) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); assertEquals("Search engines", articles.getSearchHit(0) @@ -123,7 +123,7 @@ public class ElasticSearchQueryManualTest { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "elasticsearch data")) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(3, articles.getTotalHits()); } @@ -133,14 +133,14 @@ public class ElasticSearchQueryManualTest { NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")) .build(); - SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")) .build(); - articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(0, articles.getTotalHits()); } @@ -150,7 +150,7 @@ public class ElasticSearchQueryManualTest { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(2, articles.getTotalHits()); } @@ -205,7 +205,7 @@ public class ElasticSearchQueryManualTest { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); } @@ -217,7 +217,7 @@ public class ElasticSearchQueryManualTest { .prefixLength(3)) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); } @@ -229,7 +229,7 @@ public class ElasticSearchQueryManualTest { .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(2, articles.getTotalHits()); } @@ -241,7 +241,7 @@ public class ElasticSearchQueryManualTest { final NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) .build(); - final SearchHits
articles = elasticsearchTemplate.search(searchQuery, Article.class, IndexCoordinates.of("blog")); + final SearchHits
articles = elasticsearchOperations.search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(2, articles.getTotalHits()); } diff --git a/persistence-modules/spring-data-jpa-repo-2/README.md b/persistence-modules/spring-data-jpa-repo-2/README.md index f51e7135ae..6f19577606 100644 --- a/persistence-modules/spring-data-jpa-repo-2/README.md +++ b/persistence-modules/spring-data-jpa-repo-2/README.md @@ -7,4 +7,5 @@ - [LIKE Queries in Spring JPA Repositories](https://www.baeldung.com/spring-jpa-like-queries) - [How to Access EntityManager with Spring Data](https://www.baeldung.com/spring-data-entitymanager) - [Difference Between JPA and Spring Data JPA](https://www.baeldung.com/spring-data-jpa-vs-jpa) +- [Differences Between Spring Data JPA findFirst() and findTop()](https://www.baeldung.com/spring-data-jpa-findfirst-vs-findtop) - More articles: [[<-- prev]](../spring-data-jpa-repo) diff --git a/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/Student.java b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/Student.java new file mode 100644 index 0000000000..1d6eaa3b33 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/Student.java @@ -0,0 +1,75 @@ +package com.baeldung.spring.data.persistence.search; + +import java.util.Objects; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Student { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + + private String name; + private int score; + + public Student() { + } + + public Student(String name, int score) { + + this.name = name; + this.score = score; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } + + @Override + public String toString() { + return "Student [id=" + id + ", name=" + name + ", score=" + score + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, score); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Student other = (Student) obj; + return id == other.id && Objects.equals(name, other.name) && score == other.score; + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentApplication.java b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentApplication.java new file mode 100644 index 0000000000..6aa895d067 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentApplication.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.data.persistence.search; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StudentApplication { + + private static final Logger log = LoggerFactory.getLogger(StudentApplication.class); + + public static void main(String[] args) { + SpringApplication.run(StudentApplication.class, args); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentRepository.java b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentRepository.java new file mode 100644 index 0000000000..29aade5886 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-2/src/main/java/com/baeldung/spring/data/persistence/search/StudentRepository.java @@ -0,0 +1,31 @@ +package com.baeldung.spring.data.persistence.search; + +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface StudentRepository extends JpaRepository { + + Student findFirstByOrderByScoreDesc(); + + Student findFirstBy(Sort sort); + + Student findFirstByNameLike(String name, Sort sort); + + List findFirst3ByOrderByScoreDesc(); + + List findFirst2ByScoreBetween(int startScore, int endScore, Sort sort); + + Student findTopByOrderByScoreDesc(); + + Student findTopBy(Sort sort); + + Student findTopByNameLike(String name, Sort sort); + + List findTop3ByOrderByScoreDesc(); + + List findTop2ByScoreBetween(int startScore, int endScore, Sort sort); +} diff --git a/persistence-modules/spring-data-jpa-repo-2/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-repo-2/src/main/resources/application.properties index 30cc5abbcc..3ca0cc1242 100644 --- a/persistence-modules/spring-data-jpa-repo-2/src/main/resources/application.properties +++ b/persistence-modules/spring-data-jpa-repo-2/src/main/resources/application.properties @@ -3,3 +3,4 @@ spring.datasource.username=sa spring.datasource.password=sa spring.jpa.properties.hibernate.globally_quoted_identifiers=true +logging.level.com.baeldung.spring.data.persistence.search=debug \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-2/src/test/java/com/baeldung/spring/data/persistence/search/StudentApplicationUnitTest.java b/persistence-modules/spring-data-jpa-repo-2/src/test/java/com/baeldung/spring/data/persistence/search/StudentApplicationUnitTest.java new file mode 100644 index 0000000000..ea16b3597d --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-2/src/test/java/com/baeldung/spring/data/persistence/search/StudentApplicationUnitTest.java @@ -0,0 +1,100 @@ +package com.baeldung.spring.data.persistence.search; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Sort; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class StudentApplicationUnitTest { + + @Autowired + private StudentRepository studentRepo; + private List students; + + @Before + public void fillData() { + students = new ArrayList<>(); + int count = 10; + Random r = new Random(); + List scores = r.ints(0, 101) + .distinct() + .limit(count) + .boxed() + .collect(Collectors.toList()); + + for (int i = 0; i < count; i++) { + Integer score = scores.get(i); + Student s = new Student("Student-" + i, score); + students.add(s); + } + + studentRepo.saveAll(students); + Comparator c = Comparator.comparing(a -> a.getScore()); + c = c.reversed(); + students.sort(c); + } + + @After + public void clearData() { + studentRepo.deleteAll(); + } + + @Test + public void givenStudentScores_whenMoreThanOne_thenFindFirst() { + + Student student = studentRepo.findFirstByOrderByScoreDesc(); + Student s = students.get(0); + assertEquals(student, s); + } + + @Test + public void givenStudentScores_whenMoreThan3_thenFindFirstThree() { + + List firstThree = studentRepo.findFirst3ByOrderByScoreDesc(); + List sList = students.subList(0, 3); + assertArrayEquals(firstThree.toArray(), sList.toArray()); + } + + @Test + public void givenStudentScores_whenNameMatches_thenFindFirstStudent() { + + String matchString = "3"; + Student student = studentRepo.findFirstByNameLike("%" + matchString + "%", Sort.by("score") + .descending()); + Student s = students.stream() + .filter(a -> a.getName() + .contains(matchString)) + .findFirst() + .orElse(null); + assertEquals(student, s); + } + + @Test + public void givenStudentScores_whenBetweenRange_thenFindFirstTwoStudents() { + + List topTwoBetweenRange = studentRepo.findFirst2ByScoreBetween(50, 60, Sort.by("score") + .descending()); + List _students = students.stream() + .filter(a -> a.getScore() >= 50 && a.getScore() <= 60) + .limit(2) + .collect(Collectors.toList()); + assertArrayEquals(_students.toArray(), topTwoBetweenRange.toArray()); + } +} diff --git a/pom.xml b/pom.xml index c4e5c25d9d..0b19c6eee6 100644 --- a/pom.xml +++ b/pom.xml @@ -1133,6 +1133,7 @@ core-java-modules/core-java-collections-set core-java-modules/core-java-collections-list-4 + core-java-modules/core-java-collections-array-list core-java-modules/core-java-collections-maps-4 core-java-modules/core-java-collections-maps-5 core-java-modules/core-java-concurrency-simple diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/README.md b/spring-boot-modules/spring-boot-keycloak-adapters/README.md new file mode 100644 index 0000000000..d24d315f50 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/README.md @@ -0,0 +1,7 @@ +## Spring Boot Keycloak + +This module contains articles about Keycloak in Spring Boot projects. + +## Relevant articles: +- [Custom User Attributes with Keycloak](https://www.baeldung.com/keycloak-custom-user-attributes) +- [Get Keycloak User ID in Spring](https://www.baeldung.com/spring-keycloak-get-user-id) diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/pom.xml b/spring-boot-modules/spring-boot-keycloak-adapters/pom.xml new file mode 100644 index 0000000000..0da8d920d1 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + com.baeldung.keycloak + spring-boot-keycloak-adapters + 0.0.1 + spring-boot-keycloak-adapters + jar + This is a simple application demonstrating integration between Keycloak and Spring Boot. + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + + org.keycloak.bom + keycloak-adapter-bom + ${keycloak-adapter-bom.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + org.keycloak + keycloak-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.hsqldb + hsqldb + runtime + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + 15.0.2 + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java similarity index 100% rename from spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java rename to spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomUserAttrController.java diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/Customer.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/Customer.java new file mode 100644 index 0000000000..3293446b1d --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/Customer.java @@ -0,0 +1,49 @@ +package com.baeldung.keycloak; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Customer { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + private String name; + private String serviceRendered; + private String address; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getServiceRendered() { + return serviceRendered; + } + + public void setServiceRendered(String serviceRendered) { + this.serviceRendered = serviceRendered; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + +} diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomerDAO.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomerDAO.java new file mode 100644 index 0000000000..20d992d335 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/CustomerDAO.java @@ -0,0 +1,7 @@ +package com.baeldung.keycloak; + +import org.springframework.data.repository.CrudRepository; + +public interface CustomerDAO extends CrudRepository { + +} diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakConfig.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakConfig.java similarity index 100% rename from spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/KeycloakConfig.java rename to spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakConfig.java diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java new file mode 100644 index 0000000000..06c41e9b1d --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/KeycloakLogoutHandler.java @@ -0,0 +1,45 @@ +package com.baeldung.keycloak; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class KeycloakLogoutHandler implements LogoutHandler { + + private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class); + private final RestTemplate restTemplate; + + public KeycloakLogoutHandler(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) { + logoutFromKeycloak((OidcUser) auth.getPrincipal()); + } + + private void logoutFromKeycloak(OidcUser user) { + String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout"; + UriComponentsBuilder builder = UriComponentsBuilder + .fromUriString(endSessionEndpoint) + .queryParam("id_token_hint", user.getIdToken().getTokenValue()); + + ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class); + if (logoutResponse.getStatusCode().is2xxSuccessful()) { + logger.info("Successfulley logged out from Keycloak"); + } else { + logger.error("Could not propagate logout to Keycloak"); + } + } + +} diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SecurityConfig.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SecurityConfig.java new file mode 100644 index 0000000000..c39e37cfaa --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SecurityConfig.java @@ -0,0 +1,41 @@ +package com.baeldung.keycloak; + +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.core.session.SessionRegistryImpl; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; + +@Configuration +@EnableWebSecurity +class SecurityConfig { + + private final KeycloakLogoutHandler keycloakLogoutHandler; + + SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) { + this.keycloakLogoutHandler = keycloakLogoutHandler; + } + + @Bean + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/customers*", "/users*") + .hasRole("USER") + .anyRequest() + .permitAll(); + http.oauth2Login() + .and() + .logout() + .addLogoutHandler(keycloakLogoutHandler) + .logoutSuccessUrl("/"); + return http.build(); + } +} diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SpringBoot.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SpringBoot.java new file mode 100644 index 0000000000..90d7e774a4 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/SpringBoot.java @@ -0,0 +1,20 @@ +package com.baeldung.keycloak; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication + +public class SpringBoot { + + public static void main(String[] args) { + SpringApplication.run(SpringBoot.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/WebController.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/WebController.java new file mode 100644 index 0000000000..bbd96c8135 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/java/com/baeldung/keycloak/WebController.java @@ -0,0 +1,60 @@ +package com.baeldung.keycloak; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import java.security.Principal; + +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; + +@Controller +public class WebController { + + @Autowired + private CustomerDAO customerDAO; + + @GetMapping(path = "/") + public String index() { + return "external"; + } + + @GetMapping("/logout") + public String logout(HttpServletRequest request) throws Exception { + request.logout(); + return "redirect:/"; + } + + @GetMapping(path = "/customers") + public String customers(Principal principal, Model model) { + addCustomers(); + Iterable customers = customerDAO.findAll(); + model.addAttribute("customers", customers); + model.addAttribute("username", principal.getName()); + return "customers"; + } + + // add customers for demonstration + public void addCustomers() { + + Customer customer1 = new Customer(); + customer1.setAddress("1111 foo blvd"); + customer1.setName("Foo Industries"); + customer1.setServiceRendered("Important services"); + customerDAO.save(customer1); + + Customer customer2 = new Customer(); + customer2.setAddress("2222 bar street"); + customer2.setName("Bar LLP"); + customer2.setServiceRendered("Important services"); + customerDAO.save(customer2); + + Customer customer3 = new Customer(); + customer3.setAddress("33 main street"); + customer3.setName("Big LLC"); + customer3.setServiceRendered("Important services"); + customerDAO.save(customer3); + } +} diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-embedded.properties b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/application-embedded.properties similarity index 100% rename from spring-boot-modules/spring-boot-keycloak/src/main/resources/application-embedded.properties rename to spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/application-embedded.properties diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/application.properties b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/application.properties new file mode 100644 index 0000000000..323617e2ef --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/application.properties @@ -0,0 +1,15 @@ +### server port +server.port=8081 + +#Keycloak Configuration +keycloak.auth-server-url=http://localhost:8180/auth +keycloak.realm=SpringBootKeycloak +keycloak.resource=login-app +keycloak.public-client=true +keycloak.principal-attribute=preferred_username + +spring.security.oauth2.client.registration.keycloak.client-id=login-app +spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.keycloak.scope=openid +spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/auth/realms/SpringBootKeycloak +spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/logback.xml b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/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-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/customers.html b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/customers.html new file mode 100644 index 0000000000..de2df93ef1 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/customers.html @@ -0,0 +1,34 @@ + + + + + +
+

+ Hello, --name--. +

+ + + + + + + + + + + + + + + + + +
IDNameAddressService Rendered
Text ...Text ...Text ...Text...
+ + Logout +
+ + + diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/external.html b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/external.html new file mode 100644 index 0000000000..2f9cc76961 --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/external.html @@ -0,0 +1,31 @@ + + + + + +
+
+

Customer Portal

+
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam + erat lectus, vehicula feugiat ultricies at, tempus sed ante. Cras + arcu erat, lobortis vitae quam et, mollis pharetra odio. Nullam sit + amet congue ipsum. Nunc dapibus odio ut ligula venenatis porta non + id dui. Duis nec tempor tellus. Suspendisse id blandit ligula, sit + amet varius mauris. Nulla eu eros pharetra, tristique dui quis, + vehicula libero. Aenean a neque sit amet tellus porttitor rutrum nec + at leo.

+ +

Existing Customers

+
+ Enter the intranet: customers +
+
+ +
+ + + + diff --git a/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/layout.html b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/layout.html new file mode 100644 index 0000000000..bab0c2982b --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/layout.html @@ -0,0 +1,18 @@ + + + +Customer Portal + + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html b/spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/userInfo.html similarity index 100% rename from spring-boot-modules/spring-boot-keycloak/src/main/resources/templates/userInfo.html rename to spring-boot-modules/spring-boot-keycloak-adapters/src/main/resources/templates/userInfo.html diff --git a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloak/KeycloakConfigurationLiveTest.java b/spring-boot-modules/spring-boot-keycloak-adapters/src/test/java/com/baeldung/keycloak/KeycloakConfigurationLiveTest.java similarity index 100% rename from spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloak/KeycloakConfigurationLiveTest.java rename to spring-boot-modules/spring-boot-keycloak-adapters/src/test/java/com/baeldung/keycloak/KeycloakConfigurationLiveTest.java diff --git a/spring-boot-modules/spring-boot-keycloak/README.md b/spring-boot-modules/spring-boot-keycloak/README.md index e95ada0e05..b4545e121c 100644 --- a/spring-boot-modules/spring-boot-keycloak/README.md +++ b/spring-boot-modules/spring-boot-keycloak/README.md @@ -4,9 +4,8 @@ This module contains articles about Keycloak in Spring Boot projects. ## Relevant articles: - [A Quick Guide to Using Keycloak With Spring Boot](https://www.baeldung.com/spring-boot-keycloak) -- [Custom User Attributes with Keycloak](https://www.baeldung.com/keycloak-custom-user-attributes) - [Customizing the Login Page for Keycloak](https://www.baeldung.com/keycloak-custom-login-page) - [Keycloak User Self-Registration](https://www.baeldung.com/keycloak-user-registration) - [Customizing Themes for Keycloak](https://www.baeldung.com/spring-keycloak-custom-themes) - [Securing SOAP Web Services With Keycloak](https://www.baeldung.com/soap-keycloak) -- [Get Keycloak User ID in Spring](https://www.baeldung.com/spring-keycloak-get-user-id) + diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml index 4f30d32bec..d13ef22345 100644 --- a/spring-boot-modules/spring-boot-keycloak/pom.xml +++ b/spring-boot-modules/spring-boot-keycloak/pom.xml @@ -17,26 +17,14 @@ ../../parent-boot-2 - - - - org.keycloak.bom - keycloak-adapter-bom - ${keycloak-adapter-bom.version} - pom - import - - - - org.springframework.boot spring-boot-starter - org.keycloak - keycloak-spring-boot-starter + org.springframework.boot + spring-boot-starter-oauth2-resource-server org.springframework.boot @@ -113,8 +101,4 @@ - - 15.0.2 - - \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java index c39e37cfaa..c85438952a 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java +++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloak/SecurityConfig.java @@ -2,8 +2,11 @@ package com.baeldung.keycloak; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; @@ -36,6 +39,13 @@ class SecurityConfig { .logout() .addLogoutHandler(keycloakLogoutHandler) .logoutSuccessUrl("/"); + http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); return http.build(); } + + @Bean + public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class) + .build(); + } } diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloaksoap/KeycloakSecurityConfig.java b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloaksoap/KeycloakSecurityConfig.java index 66a17f4967..e55d307e33 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloaksoap/KeycloakSecurityConfig.java +++ b/spring-boot-modules/spring-boot-keycloak/src/main/java/com/baeldung/keycloaksoap/KeycloakSecurityConfig.java @@ -1,54 +1,27 @@ package com.baeldung.keycloaksoap; -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; +import org.springframework.security.web.SecurityFilterChain; -@KeycloakConfiguration +@Configuration +@EnableWebSecurity @ConditionalOnProperty(name = "keycloak.enabled", havingValue = "true") @EnableGlobalMethodSecurity(jsr250Enabled = true) -public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - //@formatter:off - http - .csrf() - .disable() - .authorizeRequests() - .anyRequest() - .permitAll(); - //@formatter:on - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); - } +public class KeycloakSecurityConfig { @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf() + .disable() + .authorizeHttpRequests(auth -> auth.anyRequest() + .authenticated()) + .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); + return http.build(); } - - @Bean - public KeycloakConfigResolver keycloakSpringBootConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } - } diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-keycloak.properties b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-keycloak.properties index 0a28b7ac48..474e671ce3 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-keycloak.properties +++ b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application-keycloak.properties @@ -1,14 +1,8 @@ server.port=18080 keycloak.enabled=true -keycloak.realm=baeldung-soap-services -keycloak.auth-server-url=http://localhost:8080/auth -keycloak.bearer-only=true -keycloak.credentials.secret=14da6f9e-261f-489a-9bf0-1441e4a9ddc4 -keycloak.ssl-required=external -keycloak.resource=baeldung-soap-services -keycloak.use-resource-role-mappings=true +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/baeldung-soap-services # Custom properties begin here ws.api.path=/ws/api/v1/* diff --git a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties index 323617e2ef..df2fadabae 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties +++ b/spring-boot-modules/spring-boot-keycloak/src/main/resources/application.properties @@ -1,15 +1,10 @@ ### server port server.port=8081 -#Keycloak Configuration -keycloak.auth-server-url=http://localhost:8180/auth -keycloak.realm=SpringBootKeycloak -keycloak.resource=login-app -keycloak.public-client=true -keycloak.principal-attribute=preferred_username - spring.security.oauth2.client.registration.keycloak.client-id=login-app spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.keycloak.scope=openid -spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/auth/realms/SpringBootKeycloak -spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username \ No newline at end of file +spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak +spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username + +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloak/KeycloakContextIntegrationTest.java b/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloak/KeycloakContextIntegrationTest.java new file mode 100644 index 0000000000..336c8364aa --- /dev/null +++ b/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloak/KeycloakContextIntegrationTest.java @@ -0,0 +1,18 @@ +package com.baeldung.keycloak; + +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import com.baeldung.keycloak.SpringBoot; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = { SpringBoot.class }) +public class KeycloakContextIntegrationTest { + + @Test + public void whenLoadApplication_thenSuccess() { + + } + +} diff --git a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloaksoap/KeycloakSoapLiveTest.java b/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloaksoap/KeycloakSoapLiveTest.java index 0327915399..171c7bf330 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloaksoap/KeycloakSoapLiveTest.java +++ b/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloaksoap/KeycloakSoapLiveTest.java @@ -105,7 +105,7 @@ class KeycloakSoapLiveTest { void givenAccessToken_whenDeleteProduct_thenReturnSuccess() { HttpHeaders headers = new HttpHeaders(); headers.set("content-type", "text/xml"); - headers.set("Authorization", "Bearer " + generateToken("jhondoe", "password")); + headers.set("Authorization", "Bearer " + generateToken("johndoe", "password")); HttpEntity request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class); diff --git a/spring-boot-modules/spring-boot-keycloak/src/test/resources/application-test.properties b/spring-boot-modules/spring-boot-keycloak/src/test/resources/application-test.properties index a818b5be7a..609d59b4bf 100644 --- a/spring-boot-modules/spring-boot-keycloak/src/test/resources/application-test.properties +++ b/spring-boot-modules/spring-boot-keycloak/src/test/resources/application-test.properties @@ -1,4 +1,7 @@ grant.type=password client.id=baeldung-soap-services client.secret=d2ba7af8-f7d2-4c97-b4a5-3c88b59920ae -url=http://localhost:8080/auth/realms/baeldung-soap-services/protocol/openid-connect/token +url=http://localhost:8080/realms/baeldung-soap-services/protocol/openid-connect/token + +keycloak.enabled=true +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/baeldung-soap-services diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/BuzzController.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/BuzzController.java new file mode 100644 index 0000000000..09bf16f008 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/BuzzController.java @@ -0,0 +1,21 @@ +package com.baeldung.requestheader; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.requestheader.interceptor.OperatorHolder; + +@RestController +public class BuzzController { + private final OperatorHolder operatorHolder; + + public BuzzController(OperatorHolder operatorHolder) { + this.operatorHolder = operatorHolder; + } + + @GetMapping("buzz") + public String buzz() { + return "hello, " + operatorHolder.getOperator(); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/FooBarController.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/FooBarController.java new file mode 100644 index 0000000000..e0fd5f2f64 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/FooBarController.java @@ -0,0 +1,22 @@ +package com.baeldung.requestheader; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FooBarController { + + @GetMapping("foo") + public String foo(HttpServletRequest request) { + String operator = request.getHeader("operator"); + return "hello, " + operator; + } + + @GetMapping("bar") + public String bar(@RequestHeader("operator") String operator) { + return "hello, " + operator; + } +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/HeaderInterceptorApplication.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/HeaderInterceptorApplication.java new file mode 100644 index 0000000000..f2e9aaca12 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/HeaderInterceptorApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.requestheader; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@SpringBootApplication +@EnableWebMvc +public class HeaderInterceptorApplication { + + public static void main(String[] args) { + SpringApplication.run(HeaderInterceptorApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/config/HeaderInterceptorConfig.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/config/HeaderInterceptorConfig.java new file mode 100644 index 0000000000..07fb5b5184 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/config/HeaderInterceptorConfig.java @@ -0,0 +1,33 @@ +package com.baeldung.requestheader.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.baeldung.requestheader.interceptor.OperatorHolder; +import com.baeldung.requestheader.interceptor.OperatorInterceptor; + +@Configuration +public class HeaderInterceptorConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(operatorInterceptor()); + } + + @Bean + public OperatorInterceptor operatorInterceptor() { + return new OperatorInterceptor(operatorHolder()); + } + + @Bean + @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) + public OperatorHolder operatorHolder() { + return new OperatorHolder(); + } + +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorHolder.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorHolder.java new file mode 100644 index 0000000000..31d36c0b59 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorHolder.java @@ -0,0 +1,13 @@ +package com.baeldung.requestheader.interceptor; + +public class OperatorHolder { + private String operator; + + public String getOperator() { + return operator; + } + + public void setOperator(String operator) { + this.operator = operator; + } +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorInterceptor.java b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorInterceptor.java new file mode 100644 index 0000000000..0d0a0c7405 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/main/java/com/baeldung/requestheader/interceptor/OperatorInterceptor.java @@ -0,0 +1,22 @@ +package com.baeldung.requestheader.interceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.servlet.HandlerInterceptor; + +public class OperatorInterceptor implements HandlerInterceptor { + + private final OperatorHolder operatorHolder; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String operator = request.getHeader("operator"); + operatorHolder.setOperator(operator); + return true; + } + + public OperatorInterceptor(OperatorHolder operatorHolder) { + this.operatorHolder = operatorHolder; + } +} diff --git a/spring-boot-modules/spring-boot-mvc-5/src/test/java/com/baeldung/requestheader/HeaderInterceptorIntegrationTest.java b/spring-boot-modules/spring-boot-mvc-5/src/test/java/com/baeldung/requestheader/HeaderInterceptorIntegrationTest.java new file mode 100644 index 0000000000..ee053a4c36 --- /dev/null +++ b/spring-boot-modules/spring-boot-mvc-5/src/test/java/com/baeldung/requestheader/HeaderInterceptorIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung.requestheader; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { HeaderInterceptorApplication.class }) +@WebAppConfiguration +public class HeaderInterceptorIntegrationTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext) + .build(); + } + + @Test + public void givenARequestWithOperatorHeader_whenWeCallFooEndpoint_thenOperatorIsExtracted() throws Exception { + MockHttpServletResponse response = this.mockMvc.perform(get("/foo").header("operator", "John.Doe")) + .andDo(print()) + .andReturn() + .getResponse(); + + assertThat(response.getContentAsString()).isEqualTo("hello, John.Doe"); + } + + @Test + public void givenARequestWithOperatorHeader_whenWeCallBarEndpoint_thenOperatorIsExtracted() throws Exception { + MockHttpServletResponse response = this.mockMvc.perform(get("/bar").header("operator", "John.Doe")) + .andDo(print()) + .andReturn() + .getResponse(); + + assertThat(response.getContentAsString()).isEqualTo("hello, John.Doe"); + } + + @Test + public void givenARequestWithOperatorHeader_whenWeCallBuzzEndpoint_thenOperatorIsIntercepted() throws Exception { + MockHttpServletResponse response = this.mockMvc.perform(get("/buzz").header("operator", "John.Doe")) + .andDo(print()) + .andReturn() + .getResponse(); + + assertThat(response.getContentAsString()).isEqualTo("hello, John.Doe"); + } + +} diff --git a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml index e4bcdb5888..5d3c8b3af5 100644 --- a/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml +++ b/spring-boot-modules/spring-boot-swagger-keycloak/src/main/resources/application.yml @@ -2,7 +2,6 @@ keycloak: auth-server-url: https://api.example.com/auth # Keycloak server url realm: todos-service-realm # Keycloak Realm resource: todos-service-clients # Keycloak Client - public-client: true principal-attribute: preferred_username ssl-required: external credentials: diff --git a/spring-kafka-2/README.md b/spring-kafka-2/README.md index 76c090342d..1f5e015f9e 100644 --- a/spring-kafka-2/README.md +++ b/spring-kafka-2/README.md @@ -4,4 +4,4 @@ This module contains articles about Spring with Kafka ### Relevant articles -- [Implementing Retry In Kafka Consumer] +- [Implementing Retry In Kafka Consumer](https://www.baeldung.com/spring-retry-kafka-consumer) diff --git a/spring-scheduling/src/main/java/com/baeldung/async/config/SpringAsyncConfig.java b/spring-scheduling/src/main/java/com/baeldung/async/config/SpringAsyncConfig.java index 56fa643017..872f59ebb6 100644 --- a/spring-scheduling/src/main/java/com/baeldung/async/config/SpringAsyncConfig.java +++ b/spring-scheduling/src/main/java/com/baeldung/async/config/SpringAsyncConfig.java @@ -24,7 +24,9 @@ public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { - return new SimpleAsyncTaskExecutor(); + ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + threadPoolTaskExecutor.initialize(); + return threadPoolTaskExecutor; } @Override diff --git a/spring-security-modules/spring-security-saml/src/main/resources/application.properties b/spring-security-modules/spring-security-saml/src/main/resources/application.properties index fd7798dda9..332278225c 100644 --- a/spring-security-modules/spring-security-saml/src/main/resources/application.properties +++ b/spring-security-modules/spring-security-saml/src/main/resources/application.properties @@ -1,9 +1,9 @@ -saml.keystore.location=classpath:/saml/saml-keystore +saml.keystore.location=classpath:/saml/saml-keystore.jks # Password for Java keystore and item therein -saml.keystore.password= -saml.keystore.alias= +saml.keystore.password=baeldungsamlokta +saml.keystore.alias=baeldungspringsaml # SAML Entity ID extracted from top of SAML metadata file -saml.idp= +saml.idp=http://www.okta.com/exk26fxqrz8LLk9dV4x7 saml.sp=http://localhost:8080/saml/metadata spring.main.allow-circular-references=true \ No newline at end of file diff --git a/spring-security-modules/spring-security-saml/src/main/resources/saml/saml-keystore.jks b/spring-security-modules/spring-security-saml/src/main/resources/saml/saml-keystore.jks new file mode 100644 index 0000000000..535c611180 Binary files /dev/null and b/spring-security-modules/spring-security-saml/src/main/resources/saml/saml-keystore.jks differ diff --git a/spring-security-modules/spring-security-saml/src/main/resources/saml/samlKeystore.jks b/spring-security-modules/spring-security-saml/src/main/resources/saml/samlKeystore.jks deleted file mode 100644 index 7f3a5850d9..0000000000 Binary files a/spring-security-modules/spring-security-saml/src/main/resources/saml/samlKeystore.jks and /dev/null differ diff --git a/spring-security-modules/spring-security-web-angular/spring-security-web-angular-server/src/main/java/com/baeldung/springbootsecurityrest/basicauth/config/BasicAuthConfiguration.java b/spring-security-modules/spring-security-web-angular/spring-security-web-angular-server/src/main/java/com/baeldung/springbootsecurityrest/basicauth/config/BasicAuthConfiguration.java index 504dbf63e3..5f4b82a191 100644 --- a/spring-security-modules/spring-security-web-angular/spring-security-web-angular-server/src/main/java/com/baeldung/springbootsecurityrest/basicauth/config/BasicAuthConfiguration.java +++ b/spring-security-modules/spring-security-web-angular/spring-security-web-angular-server/src/main/java/com/baeldung/springbootsecurityrest/basicauth/config/BasicAuthConfiguration.java @@ -1,5 +1,7 @@ package com.baeldung.springbootsecurityrest.basicauth.config; +import static org.springframework.security.config.Customizer.withDefaults; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -27,6 +29,7 @@ public class BasicAuthConfiguration { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf() .disable() + .cors(withDefaults()) .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**") .permitAll() diff --git a/spring-web-modules/spring-web-url/README.md b/spring-web-modules/spring-web-url/README.md index 41b479337b..79a89f4386 100644 --- a/spring-web-modules/spring-web-url/README.md +++ b/spring-web-modules/spring-web-url/README.md @@ -6,3 +6,4 @@ This module contains articles about Spring MVC - [Using a Slash Character in Spring URLs](https://www.baeldung.com/spring-slash-character-in-url) - [Excluding URLs for a Filter in a Spring Web Application](https://www.baeldung.com/spring-exclude-filter) - [Handling URL Encoded Form Data in Spring REST](https://www.baeldung.com/spring-url-encoded-form-data) +- [Spring MVC – Mapping the Root URL to a Page](https://www.baeldung.com/spring-mvc-map-root-url) diff --git a/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/RootMappingApplication.java b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/RootMappingApplication.java new file mode 100644 index 0000000000..f64753fa36 --- /dev/null +++ b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/RootMappingApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.rootmapping; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@SpringBootApplication +@EnableWebMvc +public class RootMappingApplication { + + public static void main(String[] args) { + SpringApplication.run(RootMappingApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/config/WebConfig.java b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/config/WebConfig.java new file mode 100644 index 0000000000..ba15bc992c --- /dev/null +++ b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/config/WebConfig.java @@ -0,0 +1,13 @@ +package com.baeldung.rootmapping.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("index"); + } +} diff --git a/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/controller/RootController.java b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/controller/RootController.java new file mode 100644 index 0000000000..7724e43e71 --- /dev/null +++ b/spring-web-modules/spring-web-url/src/main/java/com/baeldung/rootmapping/controller/RootController.java @@ -0,0 +1,12 @@ +package com.baeldung.rootmapping.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class RootController { + @GetMapping("/") + public String index() { + return "index"; + } +} \ No newline at end of file diff --git a/spring-web-modules/spring-web-url/src/main/resources/templates/index.html b/spring-web-modules/spring-web-url/src/main/resources/templates/index.html new file mode 100644 index 0000000000..acfb847868 --- /dev/null +++ b/spring-web-modules/spring-web-url/src/main/resources/templates/index.html @@ -0,0 +1,10 @@ + + + + + Index Page + + +

Hello World!

+ + \ No newline at end of file diff --git a/web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterManualTest.java b/web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterUnitTest.java similarity index 90% rename from web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterManualTest.java rename to web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterUnitTest.java index b77901c8e2..64812f6935 100644 --- a/web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterManualTest.java +++ b/web-modules/ninja/src/test/java/controllers/ApiControllerDocTesterUnitTest.java @@ -7,11 +7,11 @@ import org.doctester.testbrowser.Response; import org.junit.Test; import ninja.NinjaDocTester; -public class ApiControllerDocTesterManualTest extends NinjaDocTester { +public class ApiControllerDocTesterUnitTest extends NinjaDocTester { String URL_INDEX = "/"; String URL_HELLO = "/hello"; - + @Test public void testGetIndex() { Response response = makeRequest(Request.GET().url(testServerUrl().path(URL_INDEX))); @@ -23,5 +23,5 @@ public class ApiControllerDocTesterManualTest extends NinjaDocTester { Response response = makeRequest(Request.GET().url(testServerUrl().path(URL_HELLO))); assertThat(response.payload, containsString("Bonjour, bienvenue dans Ninja Framework!")); } - + } diff --git a/web-modules/ninja/src/test/java/controllers/ApiControllerMockManualTest.java b/web-modules/ninja/src/test/java/controllers/ApiControllerMockUnitTest.java similarity index 68% rename from web-modules/ninja/src/test/java/controllers/ApiControllerMockManualTest.java rename to web-modules/ninja/src/test/java/controllers/ApiControllerMockUnitTest.java index 2fa76bca4e..8534fa0b0f 100644 --- a/web-modules/ninja/src/test/java/controllers/ApiControllerMockManualTest.java +++ b/web-modules/ninja/src/test/java/controllers/ApiControllerMockUnitTest.java @@ -1,23 +1,23 @@ package controllers; import static org.junit.Assert.assertEquals; -import javax.inject.Inject; + import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import ninja.NinjaRunner; + +import ninja.NinjaTest; import ninja.Result; import services.UserService; -@RunWith(NinjaRunner.class) -public class ApiControllerMockManualTest { +public class ApiControllerMockUnitTest extends NinjaTest { - @Inject private UserService userService; + private UserService userService; - ApplicationController applicationController; + private ApplicationController applicationController; @Before public void setupTest() { + userService = this.ninjaTestServer.getInjector().getInstance(UserService.class); applicationController = new ApplicationController(); applicationController.userService = userService; } @@ -28,5 +28,5 @@ public class ApiControllerMockManualTest { System.out.println(result.getRenderable()); assertEquals(userService.getUserMap().toString(), result.getRenderable().toString()); } - + }