diff --git a/axon/README.md b/axon/README.md index 2dad554110..9aeef05dd6 100644 --- a/axon/README.md +++ b/axon/README.md @@ -20,4 +20,5 @@ Two scripts are included to easily start middleware using Docker matching the pr - [Multi-Entity Aggregates in Axon](https://www.baeldung.com/java-axon-multi-entity-aggregates) - [Snapshotting Aggregates in Axon](https://www.baeldung.com/axon-snapshotting-aggregates) - [Dispatching Queries in Axon Framework](https://www.baeldung.com/axon-query-dispatching) -- [Persisting the Query Model](https://www.baeldung.com/axon-persisting-query-model) +- [Persisting the Query Model](https://www.baeldung.com/persisting-the-query-model) +- [Using and testing Axon applications via REST](https://www.baeldung.com/using-and-testing-axon-applications-via-rest) diff --git a/axon/pom.xml b/axon/pom.xml index cfdd24ff3c..42e32758ea 100644 --- a/axon/pom.xml +++ b/axon/pom.xml @@ -81,13 +81,22 @@ org.awaitility awaitility - 4.2.0 + test + + + org.springframework + spring-webflux + test + + + io.projectreactor.netty + reactor-netty-http test - 4.6.2 + 4.6.3 3.4.8 diff --git a/axon/src/test/java/com/baeldung/axon/gui/OrderRestEndpointIntegrationTest.java b/axon/src/test/java/com/baeldung/axon/gui/OrderRestEndpointIntegrationTest.java new file mode 100644 index 0000000000..a30e42766b --- /dev/null +++ b/axon/src/test/java/com/baeldung/axon/gui/OrderRestEndpointIntegrationTest.java @@ -0,0 +1,187 @@ +package com.baeldung.axon.gui; + +import com.baeldung.axon.OrderApplication; +import com.baeldung.axon.querymodel.OrderResponse; +import com.baeldung.axon.querymodel.OrderStatusResponse; + +import org.junit.jupiter.api.*; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.MediaType; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.test.StepVerifier; + +import java.util.ArrayList; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(classes = OrderApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class OrderRestEndpointIntegrationTest { + + @LocalServerPort + private int port; + + @Test + @DirtiesContext + void givenCreateOrderCalled_whenCallingAllOrders_thenOneCreatedOrderIsReturned() { + WebClient client = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + createRandomNewOrder(client); + StepVerifier.create(retrieveListResponse(client.get() + .uri("http://localhost:" + port + "/all-orders"))) + .expectNextMatches(list -> 1 == list.size() && list.get(0) + .getOrderStatus() == OrderStatusResponse.CREATED) + .verifyComplete(); + } + + @Test + @DirtiesContext + void givenCreateOrderCalledThreeTimesAnd_whenCallingAllOrdersStreaming_thenTwoCreatedOrdersAreReturned() { + WebClient client = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + for (int i = 0; i < 3; i++) { + createRandomNewOrder(client); + } + StepVerifier.create(retrieveStreamingResponse(client.get() + .uri("http://localhost:" + port + "/all-orders-streaming"))) + .expectNextMatches(o -> o.getOrderStatus() == OrderStatusResponse.CREATED) + .expectNextMatches(o -> o.getOrderStatus() == OrderStatusResponse.CREATED) + .expectNextMatches(o -> o.getOrderStatus() == OrderStatusResponse.CREATED) + .verifyComplete(); + } + + @Test + @DirtiesContext + void givenRuleExistThatNeedConfirmationBeforeShipping_whenCallingShipUnconfirmed_thenErrorReturned() { + WebClient client = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + StepVerifier.create(retrieveResponse(client.post() + .uri("http://localhost:" + port + "/ship-unconfirmed-order"))) + .verifyError(WebClientResponseException.class); + } + + @Test + @DirtiesContext + void givenShipOrderCalled_whenCallingAllShippedChairs_then234PlusOneIsReturned() { + WebClient client = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + verifyVoidPost(client, "http://localhost:" + port + "/ship-order"); + StepVerifier.create(retrieveIntegerResponse(client.get() + .uri("http://localhost:" + port + "/total-shipped/Deluxe Chair"))) + .assertNext(r -> assertEquals(235, r)) + .verifyComplete(); + } + + @Test + @DirtiesContext + void givenOrdersAreUpdated_whenCallingOrderUpdates_thenUpdatesReturned() { + WebClient updaterClient = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + WebClient receiverClient = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + String orderId = UUID.randomUUID() + .toString(); + String productId = UUID.randomUUID() + .toString(); + StepVerifier.create(retrieveResponse(updaterClient.post() + .uri("http://localhost:" + port + "/order/" + orderId))) + .assertNext(Assertions::assertNotNull) + .verifyComplete(); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + executor.schedule(() -> addIncrementDecrementConfirmAndShipProduct(orderId, productId), 1L, TimeUnit.SECONDS); + try { + StepVerifier.create(retrieveStreamingResponse(receiverClient.get() + .uri("http://localhost:" + port + "/order-updates/" + orderId))) + .assertNext(p -> assertTrue(p.getProducts() + .isEmpty())) + .assertNext(p -> assertEquals(1, p.getProducts() + .get(productId))) + .assertNext(p -> assertEquals(2, p.getProducts() + .get(productId))) + .assertNext(p -> assertEquals(1, p.getProducts() + .get(productId))) + .assertNext(p -> assertEquals(OrderStatusResponse.CONFIRMED, p.getOrderStatus())) + .assertNext(p -> assertEquals(OrderStatusResponse.SHIPPED, p.getOrderStatus())) + .thenCancel() + .verify(); + } finally { + executor.shutdown(); + } + } + + private void addIncrementDecrementConfirmAndShipProduct(String orderId, String productId) { + WebClient client = WebClient.builder() + .clientConnector(httpConnector()) + .build(); + String base = "http://localhost:" + port + "/order/" + orderId; + verifyVoidPost(client, base + "/product/" + productId); + verifyVoidPost(client, base + "/product/" + productId + "/increment"); + verifyVoidPost(client, base + "/product/" + productId + "/decrement"); + verifyVoidPost(client, base + "/confirm"); + verifyVoidPost(client, base + "/ship"); + } + + private void createRandomNewOrder(WebClient client){ + StepVerifier.create(retrieveResponse(client.post() + .uri("http://localhost:" + port + "/order"))) + .assertNext(Assertions::assertNotNull) + .verifyComplete(); + } + + private void verifyVoidPost(WebClient client, String uri) { + StepVerifier.create(retrieveResponse(client.post() + .uri(uri))) + .verifyComplete(); + } + + private static ReactorClientHttpConnector httpConnector() { + HttpClient httpClient = HttpClient.create() + .wiretap(true); + return new ReactorClientHttpConnector(httpClient); + } + + private Mono retrieveResponse(WebClient.RequestBodySpec spec) { + return spec.retrieve() + .bodyToMono(String.class); + } + + private Mono retrieveListResponse(WebClient.RequestHeadersSpec spec) { + return spec.accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(ResponseList.class); + } + + private Mono retrieveIntegerResponse(WebClient.RequestHeadersSpec spec) { + return spec.retrieve() + .bodyToMono(Integer.class); + } + + private Flux retrieveStreamingResponse(WebClient.RequestHeadersSpec spec) { + return spec.retrieve() + .bodyToFlux(OrderResponse.class); + } + + private static class ResponseList extends ArrayList { + + private ResponseList() { + super(); + } + } +} diff --git a/core-java-modules/core-java-11-3/pom.xml b/core-java-modules/core-java-11-3/pom.xml index b4a4591b28..ccccae5ba1 100644 --- a/core-java-modules/core-java-11-3/pom.xml +++ b/core-java-modules/core-java-11-3/pom.xml @@ -14,6 +14,20 @@ 1.0.0-SNAPSHOT ../../pom.xml + + + + com.google.code.gson + gson + ${gson.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + @@ -32,6 +46,8 @@ 11 11 + 2.14.1 + 2.10 \ No newline at end of file diff --git a/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/Todo.java b/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/Todo.java new file mode 100644 index 0000000000..01a1ee5c52 --- /dev/null +++ b/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/Todo.java @@ -0,0 +1,73 @@ +package com.baeldung.httppojo; + +import java.util.Objects; + +public class Todo { + + int userId; + int id; + String title; + boolean completed; + + public Todo() { + } + + public Todo(int userId, int id, String title, boolean completed) { + this.userId = userId; + this.id = id; + this.title = title; + this.completed = completed; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Todo todo = (Todo) o; + return userId == todo.userId && id == todo.id && completed == todo.completed && Objects.equals(title, todo.title); + } + + @Override + public int hashCode() { + return Objects.hash(userId, id, title, completed); + } + + @Override + public String toString() { + return "{" + "userId=" + userId + ", id=" + id + ", title='" + title + '\'' + ", completed=" + completed + '}'; + } +} diff --git a/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/TodoAppClient.java b/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/TodoAppClient.java new file mode 100644 index 0000000000..c2295d97ce --- /dev/null +++ b/core-java-modules/core-java-11-3/src/main/java/com/baeldung/httppojo/TodoAppClient.java @@ -0,0 +1,107 @@ +package com.baeldung.httppojo; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.List; +import java.util.concurrent.CompletionException; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +public class TodoAppClient { + + ObjectMapper objectMapper = new ObjectMapper(); + + Gson gson = new GsonBuilder().create(); + + public String sampleApiRequest() throws Exception { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://jsonplaceholder.typicode.com/todos")) + .build(); + + HttpResponse response = client.send(request, BodyHandlers.ofString()); + + return response.body(); + + } + + public Todo syncGson() throws Exception { + String response = sampleApiRequest(); + + List todo = gson.fromJson(response, new TypeToken>() { + }.getType()); + + return todo.get(1); + + } + + public Todo syncJackson() throws Exception { + String response = sampleApiRequest(); + + Todo[] todo = objectMapper.readValue(response, Todo[].class); + + return todo[1]; + + } + + public Todo asyncJackson() throws Exception { + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://jsonplaceholder.typicode.com/todos")) + .build(); + + TodoAppClient todoAppClient = new TodoAppClient(); + + List todo = HttpClient.newHttpClient() + .sendAsync(request, BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenApply(todoAppClient::readValueJackson) + .get(); + + return todo.get(1); + + } + + public Todo asyncGson() throws Exception { + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://jsonplaceholder.typicode.com/todos")) + .build(); + TodoAppClient todoAppClient = new TodoAppClient(); + + List todo = HttpClient.newHttpClient() + .sendAsync(request, BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenApply(todoAppClient::readValueGson) + .get(); + + return todo.get(1); + + } + + List readValueJackson(String content) { + + try { + return objectMapper.readValue(content, new TypeReference>() { + }); + } catch (IOException ioe) { + throw new CompletionException(ioe); + } + } + + List readValueGson(String content) { + + return gson.fromJson(content, new TypeToken>() { + }.getType()); + + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-11-3/src/test/java/com/baeldung/httppojo/HttpClientPojoClassUnitTest.java b/core-java-modules/core-java-11-3/src/test/java/com/baeldung/httppojo/HttpClientPojoClassUnitTest.java new file mode 100644 index 0000000000..a364a8d27e --- /dev/null +++ b/core-java-modules/core-java-11-3/src/test/java/com/baeldung/httppojo/HttpClientPojoClassUnitTest.java @@ -0,0 +1,45 @@ +package com.baeldung.httppojo; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class HttpClientPojoClassUnitTest { + + Todo expectedTodo = new Todo(1, 2, "quis ut nam facilis et officia qui", false); + + @Test + public void givenSampleApiCall_whenResponseIsMappedByGson_thenCompareResponseMappedByGson() throws Exception { + TodoAppClient sampleGson = new TodoAppClient(); + + assertEquals(expectedTodo, sampleGson.syncGson()); + + } + + @Test + public void givenSampleApiCall_whenResponseIsMappedByJackson_thenCompareResponseMappedByJackson() throws Exception { + TodoAppClient sampleJackson = new TodoAppClient(); + + assertEquals(expectedTodo, sampleJackson.syncJackson()); + } + + @Test + public void givenSampleRestApi_whenApiIsConsumedByHttpClient_thenCompareJsonString() throws Exception { + TodoAppClient sampleTest = new TodoAppClient(); + assertNotNull(sampleTest.sampleApiRequest()); + + } + + @Test + public void givenSampleApiAsyncCall_whenResponseIsMappedByJackson_thenCompareResponseMappedByJackson() throws Exception { + TodoAppClient sampleAsynJackson = new TodoAppClient(); + assertEquals(expectedTodo, sampleAsynJackson.asyncJackson()); + } + + @Test + public void givenSampleApiAsyncCall_whenResponseIsMappedByGson_thenCompareResponseMappedByGson() throws Exception { + TodoAppClient sampleAsynGson = new TodoAppClient(); + assertEquals(expectedTodo, sampleAsynGson.asyncGson()); + } + +} diff --git a/core-java-modules/core-java-19/src/test/java/com/baeldung/features/JEP428StructuredConcurrencyUnitTest.java b/core-java-modules/core-java-19/src/test/java/com/baeldung/features/JEP428StructuredConcurrencyUnitTest.java new file mode 100644 index 0000000000..64cd84ba01 --- /dev/null +++ b/core-java-modules/core-java-19/src/test/java/com/baeldung/features/JEP428StructuredConcurrencyUnitTest.java @@ -0,0 +1,122 @@ +package com.baeldung.features; + +import jdk.incubator.concurrent.StructuredTaskScope; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; +import java.util.concurrent.*; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class JEP428StructuredConcurrencyUnitTest { + + private static final String ERROR_MESSAGE = "Failed to get the result"; + + @Test + public void givenStructuredConcurrency_whenThrowingException_thenCorrect() { + assertThatThrownBy(() -> { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + Future shelter = scope.fork(this::getShelter); + Future> dogs = scope.fork(this::getDogsWithException); + scope.throwIfFailed(e -> new RuntimeException(ERROR_MESSAGE)); + scope.join(); + Response response = new Response(shelter.resultNow(), dogs.resultNow()); + + assertThat(response).isNotNull(); + assertThat(response.shelter()).isNotNull(); + assertThat(response.dogs()).isNotNull(); + assertThat(response.dogs().size()).isEqualTo(2); + } + }).isInstanceOf(RuntimeException.class) + .hasMessage(ERROR_MESSAGE); + } + + @Test + public void givenStructuredConcurrency_whenSlowTasksReachesDeadline_thenCorrect() { + assertThatThrownBy(() -> { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + Future shelter = scope.fork(this::getShelter); + Future> dogs = scope.fork(this::getDogsSlowly); + scope.throwIfFailed(e -> new RuntimeException(ERROR_MESSAGE)); + scope.join(); + scope.joinUntil(Instant.now().plusMillis(50)); + Response response = new Response(shelter.resultNow(), dogs.resultNow()); + + assertThat(response).isNotNull(); + assertThat(response.shelter()).isNotNull(); + assertThat(response.dogs()).isNotNull(); + assertThat(response.dogs().size()).isEqualTo(2); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).isInstanceOf(IllegalStateException.class); + } + + @Test + public void givenStructuredConcurrency_whenResultNow_thenCorrect() { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + Future shelter = scope.fork(this::getShelter); + Future> dogs = scope.fork(this::getDogs); + scope.join(); + + Response response = new Response(shelter.resultNow(), dogs.resultNow()); + + assertThat(response).isNotNull(); + assertThat(response.shelter()).isNotNull(); + assertThat(response.dogs()).isNotNull(); + assertThat(response.dogs().size()).isEqualTo(2); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Test + public void givenUnstructuredConcurrency_whenGet_thenCorrect() { + Future shelter; + Future> dogs; + try (ExecutorService executorService = Executors.newFixedThreadPool(3)) { + shelter = executorService.submit(this::getShelter); + dogs = executorService.submit(this::getDogs); + Shelter theShelter = shelter.get(); // Join the shelter + List theDogs = dogs.get(); // Join the dogs + Response response = new Response(theShelter, theDogs); + + assertThat(response).isNotNull(); + assertThat(response.shelter()).isNotNull(); + assertThat(response.dogs()).isNotNull(); + assertThat(response.dogs().size()).isEqualTo(2); + + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + private Shelter getShelter() { + return new Shelter("Shelter"); + } + + private List getDogs() { + return List.of(new Dog("Buddy"), new Dog("Simba")); + } + + private List getDogsWithException() { + throw new RuntimeException(ERROR_MESSAGE); + } + + private List getDogsSlowly() throws InterruptedException { + Thread.sleep(1500); + throw new RuntimeException(ERROR_MESSAGE); + } + + record Shelter(String name) { + } + + record Dog(String name) { + } + + record Response(Shelter shelter, List dogs) { + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZeros.java b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZeros.java new file mode 100644 index 0000000000..66862791aa --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/main/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZeros.java @@ -0,0 +1,15 @@ +package com.baeldung.initializearraylistwithnullorzeros; + +import java.util.ArrayList; + +public class InitializeArrayListWithNullOrZeros { + + public static void main(String[] args) { + + ArrayList arrayList = new ArrayList<>(); + for (int i = 0; i< 10; i++) { + arrayList.add(null); + } + } +} + diff --git a/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZerosUnitTest.java b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZerosUnitTest.java new file mode 100644 index 0000000000..00987fa198 --- /dev/null +++ b/core-java-modules/core-java-collections-array-list/src/test/java/com/baeldung/initializearraylistwithnullorzeros/InitializeArrayListWithNullOrZerosUnitTest.java @@ -0,0 +1,74 @@ +package com.baeldung.initializearraylistwithnullorzeros; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class InitializeArrayListWithNullOrZerosUnitTest { + + @Test + public void whenInitializingListWithNCopies_thenListIsCorrectlyPopulated() { + // when + ArrayList list = IntStream.of(new int[10]) + .boxed() + .collect(Collectors.toCollection(ArrayList::new)); + + // then + Assertions.assertEquals(10, list.size()); + Assertions.assertTrue(list.stream().allMatch(elem -> elem == 0)); + } + + @Test + public void whenInitializingListWithStream_thenListIsCorrectlyPopulated() { + + // when + ArrayList listWithZeros = Stream.generate(() -> 0) + .limit(10).collect(Collectors.toCollection(ArrayList::new)); + + ArrayList listWithNulls = Stream.generate(() -> null) + .limit(10).collect(Collectors.toCollection(ArrayList::new)); + + // then + Assertions.assertEquals(10, listWithZeros.size()); + Assertions.assertTrue(listWithZeros.stream().allMatch(elem -> elem == 0)); + + Assertions.assertEquals(10, listWithNulls.size()); + Assertions.assertTrue(listWithNulls.stream().allMatch(Objects::isNull)); + } + + @Test public void whenInitializingListWithIntStream_thenListIsCorrectlyPopulated() { + // when + ArrayList list = IntStream.of(new int[10]) + .boxed() + .collect(Collectors.toCollection(ArrayList::new)); + + // then + Assertions.assertEquals(10, list.size()); + Assertions.assertTrue(list.stream().allMatch(elem -> elem == 0)); } + + @Test + public void whenInitializingListWithAsList_thenListIsCorrectlyPopulated() { + // when + Integer[] integers = new Integer[10]; + Arrays.fill(integers, 0); + List integerList = new ArrayList<>(Arrays.asList(integers)); + + // then + Assertions.assertEquals(10, integerList.size()); + Assertions.assertTrue(integerList.stream().allMatch(elem -> elem == 0)); + } + + @Test + public void whenInitializingListWithVector_thenListIsCorrectlyPopulated() { + // when + List integerList = new Vector<>() {{setSize(10);}}; + + // then + Assertions.assertEquals(10, integerList.size()); + Assertions.assertTrue(integerList.stream().allMatch(Objects::isNull)); + } +} diff --git a/core-java-modules/core-java-collections-list-5/README.md b/core-java-modules/core-java-collections-list-5/README.md index 4b7fbf8669..78959e7d5d 100644 --- a/core-java-modules/core-java-collections-list-5/README.md +++ b/core-java-modules/core-java-collections-list-5/README.md @@ -2,4 +2,5 @@ This module contains articles about the Java List collection -### Relevant Articles: \ No newline at end of file +### Relevant Articles: +- [Java List Interface](https://www.baeldung.com/java-list-interface) diff --git a/core-java-modules/core-java-exceptions-4/README.md b/core-java-modules/core-java-exceptions-4/README.md index 9bd5d4b9bb..dc76b90fc7 100644 --- a/core-java-modules/core-java-exceptions-4/README.md +++ b/core-java-modules/core-java-exceptions-4/README.md @@ -9,4 +9,5 @@ This module contains articles about core java exceptions - [“Sneaky Throws” in Java](https://www.baeldung.com/java-sneaky-throws) - [Get the Current Stack Trace in Java](https://www.baeldung.com/java-get-current-stack-trace) - [Errors and Exceptions in Java](https://www.baeldung.com/java-errors-vs-exceptions) +- [Fix the IllegalArgumentException: No enum const class](https://www.baeldung.com/java-fix-no-enum-const-class) - [[<-- Prev]](../core-java-exceptions-3) diff --git a/core-java-modules/core-java-functional/pom.xml b/core-java-modules/core-java-functional/pom.xml index 52d74035a5..9ad47f8133 100644 --- a/core-java-modules/core-java-functional/pom.xml +++ b/core-java-modules/core-java-functional/pom.xml @@ -14,4 +14,30 @@ 0.0.1-SNAPSHOT + + + org.mockito + mockito-inline + ${mockito-inline.version} + test + + + + + core-java-functional + + + src/main/resources + true + + + + + + 3.8.0 + 3.22.0 + 3.12.0 + 0.10.4 + + \ No newline at end of file diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/ConsumerCallback.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/ConsumerCallback.java new file mode 100644 index 0000000000..f3086b8cef --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/ConsumerCallback.java @@ -0,0 +1,17 @@ +package com.baeldung.callbackfunctions; + +import java.util.function.Consumer; + +public class ConsumerCallback { + public void getAge(int initialAge, Consumer callback) { + callback.accept(initialAge); + } + + public void increaseAge(int initialAge, int ageDifference, Consumer callback) { + System.out.println("===== Increase age ===="); + + int newAge = initialAge + ageDifference; + callback.accept(newAge); + + } +} diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/EventListener.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/EventListener.java new file mode 100644 index 0000000000..dceba7a931 --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/EventListener.java @@ -0,0 +1,12 @@ +package com.baeldung.callbackfunctions; + +public interface EventListener { + + String onTrigger(); + + void respondToTrigger(); +} + + + + diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventConsumer.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventConsumer.java new file mode 100644 index 0000000000..d2f3732679 --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventConsumer.java @@ -0,0 +1,21 @@ +package com.baeldung.callbackfunctions.asynchronous; + +import com.baeldung.callbackfunctions.EventListener; + +public class AsynchronousEventConsumer{ + + private EventListener listener; + + public AsynchronousEventConsumer(EventListener listener) { + this.listener = listener; + } + + public void doAsynchronousOperation() + { + System.out.println("Performing operation in Asynchronous Task"); + + new Thread(() -> listener.onTrigger()).start(); + } + + +} diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventListenerImpl.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventListenerImpl.java new file mode 100644 index 0000000000..a6e43797f5 --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/asynchronous/AsynchronousEventListenerImpl.java @@ -0,0 +1,19 @@ +package com.baeldung.callbackfunctions.asynchronous; + +import com.baeldung.callbackfunctions.EventListener; + +public class AsynchronousEventListenerImpl implements EventListener { + + @Override + public String onTrigger() + { + respondToTrigger(); + return "Asynchronously running callback function"; + } + + @Override + public void respondToTrigger(){ + System.out.println("This is a side effect of the asynchronous trigger."); + } + +} diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventConsumer.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventConsumer.java new file mode 100644 index 0000000000..9b06151854 --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventConsumer.java @@ -0,0 +1,22 @@ +package com.baeldung.callbackfunctions.synchronous; + +import com.baeldung.callbackfunctions.EventListener; + +public class SynchronousEventConsumer { + + private final EventListener eventListener; + + public SynchronousEventConsumer(EventListener listener) + { + this.eventListener = listener; + } + + public String doSynchronousOperation() + { + System.out.println("Performing callback before synchronous Task"); + + return eventListener.onTrigger(); + } + + +} diff --git a/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventListenerImpl.java b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventListenerImpl.java new file mode 100644 index 0000000000..156f7857c3 --- /dev/null +++ b/core-java-modules/core-java-functional/src/main/java/com/baeldung/callbackfunctions/synchronous/SynchronousEventListenerImpl.java @@ -0,0 +1,18 @@ +package com.baeldung.callbackfunctions.synchronous; + +import com.baeldung.callbackfunctions.EventListener; + +public class SynchronousEventListenerImpl implements EventListener { + + @Override + public String onTrigger() + { + return "Synchronously running callback function"; + } + + @Override + public void respondToTrigger(){ + System.out.println("Response to trigger"); + } + +} diff --git a/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/AsynchronousCallbackUnitTest.java b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/AsynchronousCallbackUnitTest.java new file mode 100644 index 0000000000..494d7365d3 --- /dev/null +++ b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/AsynchronousCallbackUnitTest.java @@ -0,0 +1,22 @@ +package com.baeldung.callbackfunctions; + +import org.junit.Test; +import org.mockito.Mockito; +import com.baeldung.callbackfunctions.EventListener; +import com.baeldung.callbackfunctions.asynchronous.AsynchronousEventConsumer; +import com.baeldung.callbackfunctions.asynchronous.AsynchronousEventListenerImpl; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class AsynchronousCallbackUnitTest { + + @Test + public void whenCallbackIsInvokedAsynchronously_shouldRunAsynchronousOperation(){ + EventListener listener = Mockito.mock(AsynchronousEventListenerImpl.class); + AsynchronousEventConsumer asynchronousEventListenerConsumer = new AsynchronousEventConsumer(listener); + asynchronousEventListenerConsumer.doAsynchronousOperation(); + + verify(listener, times(1)).onTrigger(); + } +} diff --git a/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/ConsumerCallbackUnitTest.java b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/ConsumerCallbackUnitTest.java new file mode 100644 index 0000000000..ba09a7e50c --- /dev/null +++ b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/ConsumerCallbackUnitTest.java @@ -0,0 +1,20 @@ +package com.baeldung.callbackfunctions; + +import org.junit.jupiter.api.Test; +import com.baeldung.callbackfunctions.ConsumerCallback; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ConsumerCallbackUnitTest { + + @Test + public void whenIncreasingInitialAgeByGivenValueThroughCallback_shouldIncreaseAge(){ + ConsumerCallback consumerCallback = new ConsumerCallback(); + consumerCallback.getAge(20, (initialAge) -> { + int ageDifference = 10; + consumerCallback.increaseAge(initialAge, ageDifference, (newAge) -> { + assertEquals(initialAge + ageDifference, newAge); + }); + }); + } +} diff --git a/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/SynchronousCallbackUnitTest.java b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/SynchronousCallbackUnitTest.java new file mode 100644 index 0000000000..0c270524b7 --- /dev/null +++ b/core-java-modules/core-java-functional/src/test/java/com/baeldung/callbackfunctions/SynchronousCallbackUnitTest.java @@ -0,0 +1,22 @@ +package callbackFunctions; + +import org.junit.jupiter.api.Test; +import com.baeldung.callbackfunctions.EventListener; +import com.baeldung.callbackfunctions.synchronous.SynchronousEventConsumer; +import com.baeldung.callbackfunctions.synchronous.SynchronousEventListenerImpl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class SynchronousCallbackUnitTest { + + @Test + public void whenCallbackIsInvokedSynchronously_shouldRunSynchronousOperation(){ + EventListener listener = new SynchronousEventListenerImpl(); + SynchronousEventConsumer synchronousEventConsumer = new SynchronousEventConsumer(listener); + String result = synchronousEventConsumer.doSynchronousOperation(); + + assertNotNull(result); + assertEquals("Synchronously running callback function", result); + } +} 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 index 410bdca866..f1470879eb 100644 --- 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 @@ -41,7 +41,7 @@ public class InMemoryCompilationUnitTest { boolean result = task.call(); - if (result) { + if (!result) { diagnostics.getDiagnostics() .forEach(d -> LOGGER.error(String.valueOf(d))); } else { diff --git a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/transientkw/Book.java b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/transientkw/Book.java index 5822d83841..790747ec69 100644 --- a/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/transientkw/Book.java +++ b/core-java-modules/core-java-lang-3/src/main/java/com/baeldung/transientkw/Book.java @@ -11,6 +11,8 @@ public class Book implements Serializable { private transient int copies; private final transient String bookCategory = "Fiction"; + private final transient String bookCategoryNewOperator = new String("Fiction with new Operator"); + public String getBookName() { return bookName; } @@ -39,4 +41,7 @@ public class Book implements Serializable { return bookCategory; } + public String getBookCategoryNewOperator() { + return bookCategoryNewOperator; + } } diff --git a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java index a3ea77ec86..6c68896651 100644 --- a/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java +++ b/core-java-modules/core-java-lang-3/src/test/java/com/baeldung/transientkw/TransientUnitTest.java @@ -34,7 +34,17 @@ class TransientUnitTest { assertEquals("Fiction", book2.getBookCategory()); } - + + @Test + void givenFinalTransientWithNewOperator_whenSerDe_thenValuePersisted() throws Exception { + Book book = new Book(); + + BookSerDe.serialize(book); + Book book2 = BookSerDe.deserialize(); + + assertNull(book2.getBookCategoryNewOperator()); + } + @AfterAll public static void cleanup() { File file = new File(BookSerDe.fileName); diff --git a/core-java-modules/core-java-lang-5/README.md b/core-java-modules/core-java-lang-5/README.md index 5ae3955bc8..b8deff199e 100644 --- a/core-java-modules/core-java-lang-5/README.md +++ b/core-java-modules/core-java-lang-5/README.md @@ -12,4 +12,5 @@ This module contains articles about core features in the Java language - [Infinity in Java](https://www.baeldung.com/java-infinity) - [Type Parameter vs Wildcard in Java Generics](https://www.baeldung.com/java-generics-type-parameter-vs-wildcard) - [Convert Between int and char in Java](https://www.baeldung.com/java-convert-int-char) -- +- [Converting a Number from One Base to Another in Java](https://www.baeldung.com/java-converting-a-number-from-one-base-to-another) +- [Check if Command-Line Arguments Are Null in Java](https://www.baeldung.com/java-check-command-line-args) diff --git a/core-java-modules/core-java-lang-5/pom.xml b/core-java-modules/core-java-lang-5/pom.xml index 6d3771bd31..767ebb6e35 100644 --- a/core-java-modules/core-java-lang-5/pom.xml +++ b/core-java-modules/core-java-lang-5/pom.xml @@ -13,6 +13,12 @@ core-java-modules 0.0.1-SNAPSHOT + + + 3.12.0 + 0.10.2 + + core-java-lang-5 @@ -24,4 +30,18 @@ + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + org.reflections + reflections + ${reflections.version} + + + \ No newline at end of file diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass1.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass1.java new file mode 100644 index 0000000000..fb91223a97 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass1.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public class ChildClass1 implements ChildInterface1 { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass2.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass2.java new file mode 100644 index 0000000000..2d5958784d --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildClass2.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public class ChildClass2 implements ChildInterface2 { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface1.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface1.java new file mode 100644 index 0000000000..8a2ad5f345 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface1.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public interface ChildInterface1 extends MasterInterface { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface2.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface2.java new file mode 100644 index 0000000000..cff662edf0 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/ChildInterface2.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public interface ChildInterface2 extends MasterInterface { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterClass.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterClass.java new file mode 100644 index 0000000000..29f3ed5fe8 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterClass.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public class MasterClass implements MasterInterface { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterInterface.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterInterface.java new file mode 100644 index 0000000000..6aefb8b6e6 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/checkinterface/MasterInterface.java @@ -0,0 +1,5 @@ +package com.baeldung.checkinterface; + +public interface MasterInterface { + +} diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithErrorHandling.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithErrorHandling.java new file mode 100644 index 0000000000..e80a2b46d9 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithErrorHandling.java @@ -0,0 +1,12 @@ +package com.baeldung.commandline; + +public class CommandLineWithErrorHandling { + + public static void main(String[] args) { + if (args.length > 0) { + System.out.println(args[0]); + } else { + System.out.println("No command line arguments were provided."); + } + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithoutErrorHandling.java b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithoutErrorHandling.java new file mode 100644 index 0000000000..d761969bfd --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/main/java/com/baeldung/commandline/CommandLineWithoutErrorHandling.java @@ -0,0 +1,9 @@ +package com.baeldung.commandline; + +public class CommandLineWithoutErrorHandling { + + public static void main(String[] args) { + + System.out.println(args[0]); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/checkinterface/CheckInterfaceUnitTest.java b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/checkinterface/CheckInterfaceUnitTest.java new file mode 100644 index 0000000000..dcbbb7eb05 --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/checkinterface/CheckInterfaceUnitTest.java @@ -0,0 +1,154 @@ +package com.baeldung.checkinterface; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.ClassUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.reflections.ReflectionUtils; +import org.reflections.Reflections; + +public class CheckInterfaceUnitTest { + + protected static Reflections reflections; + + @BeforeAll + public static void initializeReflectionsLibrary() { + + reflections = new Reflections("com.baeldung.checkinterface"); + } + + @Test + public void whenUsingReflectionGetInterfaces_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + List> interfaces = Arrays.asList(childClass2.getClass().getInterfaces()); + + assertEquals(1, interfaces.size()); + assertTrue(interfaces.contains(ChildInterface2.class)); + } + + @Test + public void whenUsingReflectionGetInterfaces_thenParentInterfaceIsNotFound() { + + ChildClass2 childClass2 = new ChildClass2(); + List> interfaces = Arrays.asList(childClass2.getClass().getInterfaces()); + + assertFalse(interfaces.contains(MasterInterface.class)); + } + + @Test + public void whenUsingReflectionGetInterfacesRecursively_thenParentInterfaceIsFound() { + + Set> interfaces = getAllExtendedOrImplementedInterfacesRecursively(ChildClass2.class); + + assertTrue(interfaces.contains(ChildInterface2.class)); + assertTrue(interfaces.contains(MasterInterface.class)); + } + + @Test + public void whenUsingReflectionIsAssignableFrom_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(ChildInterface2.class.isAssignableFrom(childClass2.getClass())); + } + + @Test + public void whenUsingReflectionIsAssignableFrom_thenParentInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(MasterInterface.class.isAssignableFrom(childClass2.getClass())); + } + + @Test + public void whenUsingReflectionIsInstance_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(ChildInterface2.class.isInstance(childClass2)); + } + + @Test + public void whenUsingReflectionIsInstance_thenParentInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(MasterInterface.class.isInstance(childClass2)); + } + + @Test + public void whenUsingReflectionInstanceOf_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(childClass2 instanceof ChildInterface2); + } + + @Test + public void whenUsingReflectionInstanceOf_thenParentInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + + assertTrue(childClass2 instanceof MasterInterface); + } + + @Test + public void whenUsingCommons_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + List> interfaces = ClassUtils.getAllInterfaces(childClass2.getClass()); + + assertTrue(interfaces.contains(ChildInterface2.class)); + } + + @Test + public void whenUsingCommons_thenParentInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + List> interfaces = ClassUtils.getAllInterfaces(childClass2.getClass()); + + assertTrue(interfaces.contains(MasterInterface.class)); + } + + @Test + public void whenUsingReflections_thenDirectlyImplementedInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + Set> interfaces = reflections.get(ReflectionUtils.Interfaces.of(childClass2.getClass())); + + assertTrue(interfaces.contains(ChildInterface2.class)); + } + + @Test + public void whenUsingReflections_thenParentInterfaceIsFound() { + + ChildClass2 childClass2 = new ChildClass2(); + Set> interfaces = reflections.get(ReflectionUtils.Interfaces.of(childClass2.getClass())); + + assertTrue(interfaces.contains(MasterInterface.class)); + } + + static Set> getAllExtendedOrImplementedInterfacesRecursively(Class clazz) { + + Set> res = new HashSet>(); + Class[] interfaces = clazz.getInterfaces(); + + if (interfaces.length > 0) { + res.addAll(Arrays.asList(interfaces)); + for (Class interfaze : interfaces) { + res.addAll(getAllExtendedOrImplementedInterfacesRecursively(interfaze)); + } + } + + return res; + } +} diff --git a/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/commandline/CommandLineWithoutErrorHandlingUnitTest.java b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/commandline/CommandLineWithoutErrorHandlingUnitTest.java new file mode 100644 index 0000000000..197818631b --- /dev/null +++ b/core-java-modules/core-java-lang-5/src/test/java/com/baeldung/commandline/CommandLineWithoutErrorHandlingUnitTest.java @@ -0,0 +1,12 @@ +package com.baeldung.commandline; + +import org.junit.Test; +import static org.junit.Assert.fail; + +public class CommandLineWithoutErrorHandlingUnitTest { + + @Test(expected = NullPointerException.class) + public void givenNullCommandLineArgument_whenPassedToMainFunction_thenExpectNullPointerException() { + CommandLineWithoutErrorHandling.main(null); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-lang-oop-types-2/src/test/java/com/baeldung/enums/comparestrenum/CompareStringAndEnumUnitTest.java b/core-java-modules/core-java-lang-oop-types-2/src/test/java/com/baeldung/enums/comparestrenum/CompareStringAndEnumUnitTest.java new file mode 100644 index 0000000000..6ce3bc7bdc --- /dev/null +++ b/core-java-modules/core-java-lang-oop-types-2/src/test/java/com/baeldung/enums/comparestrenum/CompareStringAndEnumUnitTest.java @@ -0,0 +1,78 @@ +package com.baeldung.enums.comparestrenum; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Optional; + +import static com.baeldung.enums.comparestrenum.Weekday.Fri; +import static com.baeldung.enums.comparestrenum.Weekday.Sat; +import static org.junit.jupiter.api.Assertions.*; + +enum Weekday { + Mon("Monday"), + Tue("Tuesday"), + Wed("Wednesday"), + Thu("Thursday"), + Fri("Friday"), + Sat("Saturday"); + + private String fullName; + + Weekday(String fullName) { + this.fullName = fullName; + } + + public String getFullName() { + return fullName; + } + + + static Optional byFullNameIgnoreCase(String givenFullName) { + return Arrays.stream(values()).filter(it -> it.fullName.equalsIgnoreCase(givenFullName)).findAny(); + } + + static Optional byNameIgnoreCase(String givenName) { + return Arrays.stream(values()).filter(it -> it.name().equalsIgnoreCase(givenName)).findAny(); + } +} + +public class CompareStringAndEnumUnitTest { + private static final String SAT = "sAt"; + private static final String SATURDAY = "sAtuRdAy"; + private static final String TYPO_FRI = "ffri"; + private static final String TYPO_FRIDAY = "ffriday"; + + @Test + void givenAString_whenCompareEnumWithName_thenGetExpectedResult() { + assertTrue(SAT.equalsIgnoreCase(Sat.name())); + assertFalse(TYPO_FRI.equalsIgnoreCase(Fri.name())); + } + + @Test + void givenAString_whenCompareEnumWithProperty_thenGetExpectedResult() { + assertTrue(SATURDAY.equalsIgnoreCase(Sat.getFullName())); + assertFalse(TYPO_FRI.equalsIgnoreCase(Fri.getFullName())); + } + + @Test + void givenAString_whenFindEnumByName_thenGetExpectedResult() { + Optional optResult = Weekday.byNameIgnoreCase(SAT); + assertTrue(optResult.isPresent()); + assertEquals(Sat, optResult.get()); + + Optional optResult2 = Weekday.byNameIgnoreCase(TYPO_FRI); + assertFalse(optResult2.isPresent()); + } + + @Test + void givenAString_whenFindEnumByProperty_thenGetExpectedResult() { + Optional optResult = Weekday.byFullNameIgnoreCase(SATURDAY); + assertTrue(optResult.isPresent()); + assertEquals(Sat, optResult.get()); + + Optional optResult2 = Weekday.byFullNameIgnoreCase(TYPO_FRIDAY); + assertFalse(optResult2.isPresent()); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-networking-2/pom.xml b/core-java-modules/core-java-networking-2/pom.xml index 77f23be97c..982f4fa346 100644 --- a/core-java-modules/core-java-networking-2/pom.xml +++ b/core-java-modules/core-java-networking-2/pom.xml @@ -25,15 +25,20 @@ ${commons-lang3.version} - javax.mail - mail - ${javax.mail.version} + org.eclipse.angus + angus-mail + ${angus.mail.version} org.asynchttpclient async-http-client ${async-http-client.version} + + jakarta.xml.bind + jakarta.xml.bind-api + ${jakarta.bind.version} + com.icegreen greenmail @@ -48,9 +53,10 @@ 4.5.9 - 1.5.0-b01 + 2.0.1 2.4.5 - 1.5.8 + 2.3.3 + 2.0.0-alpha-3 \ No newline at end of file diff --git a/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/EmailService.java b/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/EmailService.java index 3e40cf53f7..b2d8e6b710 100644 --- a/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/EmailService.java +++ b/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/EmailService.java @@ -1,19 +1,20 @@ package com.baeldung.mail; -import javax.mail.Authenticator; -import javax.mail.Message; -import javax.mail.Multipart; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; import java.io.File; import java.net.URI; import java.util.Properties; +import jakarta.mail.Authenticator; +import jakarta.mail.Message; +import jakarta.mail.Multipart; +import jakarta.mail.PasswordAuthentication; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; + public class EmailService { private String username; diff --git a/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentService.java b/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentService.java index fbe8a54bbe..66e1372ac1 100644 --- a/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentService.java +++ b/core-java-modules/core-java-networking-2/src/main/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentService.java @@ -1,21 +1,23 @@ package com.baeldung.mail.mailwithattachment; -import javax.mail.BodyPart; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.Properties; +import jakarta.mail.Authenticator; +import jakarta.mail.BodyPart; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.PasswordAuthentication; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; + public class MailWithAttachmentService { private final String username; @@ -37,7 +39,7 @@ public class MailWithAttachmentService { props.put("mail.smtp.host", this.host); props.put("mail.smtp.port", this.port); - return Session.getInstance(props, new javax.mail.Authenticator() { + return Session.getInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } diff --git a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java index 8fe50efd69..a8670bb01e 100644 --- a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java +++ b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/download/FileDownloadIntegrationTest.java @@ -1,10 +1,7 @@ package com.baeldung.download; -import org.junit.After; -import org.junit.BeforeClass; -import org.junit.Test; +import static org.junit.Assert.assertTrue; -import javax.xml.bind.DatatypeConverter; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; @@ -13,7 +10,11 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.concurrent.ExecutionException; -import static org.junit.Assert.assertTrue; +import javax.xml.bind.DatatypeConverter; + +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; public class FileDownloadIntegrationTest { diff --git a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/EmailServiceLiveTest.java b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/EmailServiceLiveTest.java index cec4cfcb55..184df8428a 100644 --- a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/EmailServiceLiveTest.java +++ b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/EmailServiceLiveTest.java @@ -1,17 +1,19 @@ package com.baeldung.mail; -import com.icegreen.greenmail.junit.GreenMailRule; -import com.icegreen.greenmail.util.ServerSetupTest; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import java.io.IOException; +import com.icegreen.greenmail.junit.GreenMailRule; +import com.icegreen.greenmail.util.ServerSetupTest; -import static org.junit.Assert.assertEquals; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; public class EmailServiceLiveTest { diff --git a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentServiceLiveTest.java b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentServiceLiveTest.java index 04ad47875f..c6850461ae 100644 --- a/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentServiceLiveTest.java +++ b/core-java-modules/core-java-networking-2/src/test/java/com/baeldung/mail/mailwithattachment/MailWithAttachmentServiceLiveTest.java @@ -1,20 +1,20 @@ package com.baeldung.mail.mailwithattachment; +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + import com.icegreen.greenmail.configuration.GreenMailConfiguration; import com.icegreen.greenmail.junit.GreenMailRule; import com.icegreen.greenmail.util.GreenMailUtil; import com.icegreen.greenmail.util.ServerSetupTest; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import javax.annotation.Resource; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; - -import static org.junit.Assert.assertEquals; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; public class MailWithAttachmentServiceLiveTest { @@ -29,7 +29,6 @@ public class MailWithAttachmentServiceLiveTest { .withUser(USERNAME, PASSWORD) ); - @Resource private MailWithAttachmentService emailService; @Before @@ -73,5 +72,4 @@ public class MailWithAttachmentServiceLiveTest { return GreenMailUtil.getBody(((MimeMultipart) receivedMessage.getContent()) .getBodyPart(2)); } - } diff --git a/core-java-modules/core-java-networking-3/pom.xml b/core-java-modules/core-java-networking-3/pom.xml index 4f373238ee..6d0374c598 100644 --- a/core-java-modules/core-java-networking-3/pom.xml +++ b/core-java-modules/core-java-networking-3/pom.xml @@ -30,9 +30,9 @@ ${tomcat.embeded.version} - com.sun.mail - javax.mail - ${javax.mail.version} + org.eclipse.angus + angus-mail + ${angus.mail.version} @@ -95,7 +95,7 @@ 5.3.3 1.32 0.17 - 1.6.2 + 2.0.1 1.7 diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/downloadattachments/DownloadEmailAttachments.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/downloadattachments/DownloadEmailAttachments.java index 4030f3b983..de726f6cbf 100644 --- a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/downloadattachments/DownloadEmailAttachments.java +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/downloadattachments/DownloadEmailAttachments.java @@ -6,16 +6,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; -import javax.mail.Address; -import javax.mail.Folder; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.NoSuchProviderException; -import javax.mail.Part; -import javax.mail.Session; -import javax.mail.Store; -import javax.mail.internet.MimeBodyPart; +import jakarta.mail.Address; +import jakarta.mail.Folder; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.NoSuchProviderException; +import jakarta.mail.Part; +import jakarta.mail.Session; +import jakarta.mail.Store; +import jakarta.mail.internet.MimeBodyPart; public class DownloadEmailAttachments { private String downloadDirectory; @@ -24,7 +24,7 @@ public class DownloadEmailAttachments { this.downloadDirectory = dir; } - public void downloadEmailAttachments(String host, String port, String userName, String password) throws NoSuchProviderException, MessagingException, IOException { + public void downloadEmailAttachments(String host, String port, String userName, String password) throws MessagingException, IOException { Properties properties = setMailServerProperties(host, port); Store store = setSessionStoreProperties(userName, password, properties); Folder inbox = store.getFolder("INBOX"); @@ -67,7 +67,7 @@ public class DownloadEmailAttachments { return downloadedAttachments; } - public Store setSessionStoreProperties(String userName, String password, Properties properties) throws NoSuchProviderException, MessagingException { + public Store setSessionStoreProperties(String userName, String password, Properties properties) throws MessagingException { Session session = Session.getDefaultInstance(properties); Store store = session.getStore("pop3"); diff --git a/core-java-modules/core-java-numbers-5/README.md b/core-java-modules/core-java-numbers-5/README.md index 714e808239..7a010e2fbf 100644 --- a/core-java-modules/core-java-numbers-5/README.md +++ b/core-java-modules/core-java-numbers-5/README.md @@ -6,3 +6,4 @@ - [List All Factors of a Number in Java](https://www.baeldung.com/java-list-factors-integer) - [Make Division of Two Integers Result in a Float](https://www.baeldung.com/java-integer-division-float-result) - [Creating Random Numbers With No Duplicates in Java](https://www.baeldung.com/java-unique-random-numbers) +- [Multiply a BigDecimal by an Integer in Java](https://www.baeldung.com/java-bigdecimal-multiply-integer) diff --git a/core-java-modules/core-java-numbers-5/src/main/java/com/baeldung/IntegerNullOrZero.java b/core-java-modules/core-java-numbers-5/src/main/java/com/baeldung/IntegerNullOrZero.java new file mode 100644 index 0000000000..5fbc428b90 --- /dev/null +++ b/core-java-modules/core-java-numbers-5/src/main/java/com/baeldung/IntegerNullOrZero.java @@ -0,0 +1,26 @@ +package com.baeldung; + +import java.util.Optional; +import org.apache.commons.lang3.ObjectUtils; + +public class IntegerNullOrZero { + private IntegerNullOrZero() { + throw new RuntimeException("This class cannot be instantiated."); + } + + public static boolean usingStandardWay(Integer num) { + return num == null || num == 0; + } + + public static boolean usingTernaryOperator(Integer num) { + return 0 == (num == null ? 0 : num); + } + + public static boolean usingOptional(Integer num) { + return Optional.ofNullable(num).orElse(0) == 0; + } + + public static boolean usingObjectUtils(Integer num) { + return ObjectUtils.defaultIfNull(num, 0) == 0; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/bigdecimaltimesint/BigDecimalxIntegerUnitTest.java b/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/bigdecimaltimesint/BigDecimalxIntegerUnitTest.java new file mode 100644 index 0000000000..afdf095f4e --- /dev/null +++ b/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/bigdecimaltimesint/BigDecimalxIntegerUnitTest.java @@ -0,0 +1,22 @@ +package com.baeldung.bigdecimaltimesint; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigDecimal; + +import org.junit.jupiter.api.Test; + +public class BigDecimalxIntegerUnitTest { + private static final BigDecimal BIG = new BigDecimal("42.42"); + private static final int INT = 10; + private static final BigDecimal EXPECTED = new BigDecimal("424.2"); + + @Test + void givenBigDecimalAndInt_whenTimes_thenGetExpectedResult() { + BigDecimal result = BIG.multiply(BigDecimal.valueOf(INT)); + + assertEquals(0, EXPECTED.compareTo(result)); + assertThat(result).isEqualByComparingTo(EXPECTED); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/intnullorzero/IntegerNullOrZeroUnitTest.java b/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/intnullorzero/IntegerNullOrZeroUnitTest.java new file mode 100644 index 0000000000..6926289536 --- /dev/null +++ b/core-java-modules/core-java-numbers-5/src/test/java/com/baeldung/intnullorzero/IntegerNullOrZeroUnitTest.java @@ -0,0 +1,69 @@ +package com.baeldung.intnullorzero; + +import static com.baeldung.IntegerNullOrZero.usingObjectUtils; +import static com.baeldung.IntegerNullOrZero.usingOptional; +import static com.baeldung.IntegerNullOrZero.usingStandardWay; +import static com.baeldung.IntegerNullOrZero.usingTernaryOperator; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class IntegerNullOrZeroUnitTest { + + @Test + void givenInts_whenUsingStandardWay_thenGetExpectedResult() { + int n0 = 0; + boolean result0 = usingStandardWay(n0); + assertTrue(result0); + + boolean resultNull = usingStandardWay(null); + assertTrue(resultNull); + + int n42 = 42; + boolean result42 = usingStandardWay(n42); + assertFalse(result42); + } + + @Test + void givenInts_whenUsingTernaryOperator_thenGetExpectedResult() { + int n0 = 0; + boolean result0 = usingTernaryOperator(n0); + assertTrue(result0); + + boolean resultNull = usingTernaryOperator(null); + assertTrue(resultNull); + + int n42 = 42; + boolean result42 = usingTernaryOperator(n42); + assertFalse(result42); + } + + @Test + void givenInts_whenUsingOptional_thenGetExpectedResult() { + int n0 = 0; + boolean result0 = usingOptional(n0); + assertTrue(result0); + + boolean resultNull = usingOptional(null); + assertTrue(resultNull); + + int n42 = 42; + boolean result42 = usingOptional(n42); + assertFalse(result42); + } + + @Test + void givenInts_whenUsingObjectUtils_thenGetExpectedResult() { + int n0 = 0; + boolean result0 = usingObjectUtils(n0); + assertTrue(result0); + + boolean resultNull = usingObjectUtils(null); + assertTrue(resultNull); + + int n42 = 42; + boolean result42 = usingObjectUtils(n42); + assertFalse(result42); + } +} \ 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 index b5407b7283..bfc7ffae3b 100644 --- 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 @@ -14,20 +14,20 @@ public class CustomBatchIterator implements Iterator> { private List currentBatch; private final Iterator iterator; - public CustomBatchIterator(Iterator sourceIterator, int batchSize) { + private CustomBatchIterator(Iterator sourceIterator, int batchSize) { this.batchSize = batchSize; this.iterator = sourceIterator; } @Override public List next() { + prepareNextBatch(); return currentBatch; } @Override public boolean hasNext() { - prepareNextBatch(); - return currentBatch != null && !currentBatch.isEmpty(); + return iterator.hasNext(); } public static Stream> batchStreamOf(Stream stream, int batchSize) { diff --git a/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/streamtoiterable/StreamToIterable.java b/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/streamtoiterable/StreamToIterable.java new file mode 100644 index 0000000000..cdba5ea91f --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/main/java/com/baeldung/streams/streamtoiterable/StreamToIterable.java @@ -0,0 +1,42 @@ +package com.baeldung.streams.streamtoiterable; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import joptsimple.internal.Strings; + +public class StreamToIterable { + public String streamToIterableLambda(List listOfStrings) { + Stream stringStream = listOfStrings.stream(); + StringBuilder sentence = new StringBuilder(); + for (String eachString : (Iterable) () -> stringStream.iterator()) { + doSomethingOnString(eachString, sentence); + } + return sentence.toString(); + } + + public String streamToIterableMethodReference(List listOfStrings) { + Stream stringStream = listOfStrings.stream(); + StringBuilder sentence = new StringBuilder(); + for (String eachString : (Iterable) stringStream::iterator) { + doSomethingOnString(eachString, sentence); + } + return sentence.toString(); + } + + public String streamToList(List listOfStrings) { + Stream stringStream = listOfStrings.stream(); + StringBuilder sentence = new StringBuilder(); + for (String eachString : stringStream.collect(Collectors.toList())) { + doSomethingOnString(eachString, sentence); + } + return sentence.toString(); + } + + private void doSomethingOnString(String s, StringBuilder sentence) { + if (!Strings.isNullOrEmpty(s)) { + sentence.append(s); + } + } +} diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/streamtoiterable/StreamToIterableUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/streamtoiterable/StreamToIterableUnitTest.java new file mode 100644 index 0000000000..6715477cc3 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/streamtoiterable/StreamToIterableUnitTest.java @@ -0,0 +1,48 @@ +package com.baeldung.streams.streamtoiterable; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class StreamToIterableUnitTest { + + @Test + public void givenList_whenLambdaIsUsed_ThenStreamAsIterable(){ + StreamToIterable streamToIterable = new StreamToIterable(); + String actualString = streamToIterable.streamToIterableLambda(getListOfStrings()); + String expectedString = "Thisisasentencewithnospaces"; + Assert.assertEquals(expectedString, actualString); + } + + @Test + public void givenList_whenMethodReferenceIsUsed_ThenStreamAsIterable(){ + StreamToIterable streamToIterable = new StreamToIterable(); + String actualString = streamToIterable.streamToIterableMethodReference(getListOfStrings()); + String expectedString = "Thisisasentencewithnospaces"; + Assert.assertEquals(expectedString, actualString); + } + + @Test + public void givenList_whenCollectedToList_ThenStreamAsIterable(){ + StreamToIterable streamToIterable = new StreamToIterable(); + String actualString = streamToIterable.streamToList(getListOfStrings()); + String expectedString = "Thisisasentencewithnospaces"; + Assert.assertEquals(expectedString, actualString); + } + + private List getListOfStrings(){ + List listOfStrings = new ArrayList<>(); + listOfStrings.add("This"); + listOfStrings.add("is"); + listOfStrings.add("a"); + listOfStrings.add(null); + listOfStrings.add("sentence"); + listOfStrings.add("with"); + listOfStrings.add("no"); + listOfStrings.add(null); + listOfStrings.add("spaces"); + return listOfStrings; + } +} diff --git a/docker-modules/docker-caching/multi-module-caching/pom.xml b/docker-modules/docker-caching/multi-module-caching/pom.xml index f85f00a28d..94a370453c 100644 --- a/docker-modules/docker-caching/multi-module-caching/pom.xml +++ b/docker-modules/docker-caching/multi-module-caching/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung multi-module-caching @@ -25,6 +25,7 @@ + UTF-8 1.8 31.1-jre diff --git a/docker-modules/docker-caching/single-module-caching/pom.xml b/docker-modules/docker-caching/single-module-caching/pom.xml index aa9720a273..4a4e53f1d3 100644 --- a/docker-modules/docker-caching/single-module-caching/pom.xml +++ b/docker-modules/docker-caching/single-module-caching/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung single-module-caching @@ -48,6 +48,7 @@ 8 8 + UTF-8 31.1-jre diff --git a/docker-modules/docker-compose-2/README.md b/docker-modules/docker-compose-2/README.md new file mode 100644 index 0000000000..729105e3fd --- /dev/null +++ b/docker-modules/docker-compose-2/README.md @@ -0,0 +1 @@ +## Relevant Articles: diff --git a/docker-modules/docker-compose-2/communication_same_machine/Dockerfile b/docker-modules/docker-compose-2/communication_same_machine/Dockerfile new file mode 100644 index 0000000000..a0c4ebe59a --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:latest +MAINTAINER baeldung.com +RUN apk update && apk add iputils && apk add bash && apk add curl \ No newline at end of file diff --git a/docker-modules/docker-compose-2/communication_same_machine/Dockerfile.node b/docker-modules/docker-compose-2/communication_same_machine/Dockerfile.node new file mode 100644 index 0000000000..6d8f0eff81 --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/Dockerfile.node @@ -0,0 +1,7 @@ +FROM node:8.16.1-alpine +WORKDIR /app +COPY host_docker_internal/package.json /app +COPY host_docker_internal/index.js /app +RUN npm install +CMD node index.js +EXPOSE 8080 \ No newline at end of file diff --git a/docker-modules/docker-compose-2/communication_same_machine/dns/docker-compose.yml b/docker-modules/docker-compose-2/communication_same_machine/dns/docker-compose.yml new file mode 100644 index 0000000000..0a06993241 --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/dns/docker-compose.yml @@ -0,0 +1,20 @@ +services: + alpine-app-1: + container_name: alpine-app-1 + image: alpine-app-1 + build: + context: .. + dockerfile: Dockerfile + tty: true + ports: + - 8081:8081 + + alpine-app-2: + container_name: alpine-app-2 + image: alpine-app-2 + build: + context: .. + dockerfile: Dockerfile + tty: true + ports: + - 8080:8080 diff --git a/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/docker-compose.yml b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/docker-compose.yml new file mode 100644 index 0000000000..8e7174a9a1 --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/docker-compose.yml @@ -0,0 +1,29 @@ +services: + alpine-app-1: + container_name: alpine-app-1 + extra_hosts: # for linux hosts since version 20.10 + - host.docker.internal:host-gateway + build: + context: .. + dockerfile: Dockerfile + image: alpine-app-1 + tty: true + networks: + - first-network + + node-app: + container_name: node-app + build: + context: .. + dockerfile: Dockerfile.node + image: node-app + ports: + - 8080:8080 + networks: + - second-network + +networks: + first-network: + driver: bridge + second-network: + driver: bridge \ No newline at end of file diff --git a/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/index.js b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/index.js new file mode 100644 index 0000000000..cefae028e5 --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/index.js @@ -0,0 +1,10 @@ +var express = require('express') +var app = express() + +app.get('/', function (req, res) { + res.send('Hello World!') +}) + +app.listen(8080, function () { + console.log('app listening on port 8080!') +}) \ No newline at end of file diff --git a/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/package.json b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/package.json new file mode 100644 index 0000000000..cde98b1cfd --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/host_docker_internal/package.json @@ -0,0 +1,14 @@ +{ + "name": "host_docker_internal", + "version": "1.0.0", + "description": "node js app", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Baeldung", + "license": "ISC", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/docker-modules/docker-compose-2/communication_same_machine/static_ip_bridge/docker-compose.yml b/docker-modules/docker-compose-2/communication_same_machine/static_ip_bridge/docker-compose.yml new file mode 100644 index 0000000000..0193bd72fb --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/static_ip_bridge/docker-compose.yml @@ -0,0 +1,34 @@ +services: + alpine-app-1: + container_name: alpine-app-1 + build: + context: .. + dockerfile: Dockerfile + image: alpine-app-1 + tty: true + ports: + - 8080:8080 + networks: + network-example: + ipv4_address: 10.5.0.2 + + alpine-app-2: + container_name: alpine-app-2 + build: + context: .. + dockerfile: Dockerfile + image: alpine-app-2 + tty: true + ports: + - 8081:8081 + networks: + network-example: + ipv4_address: 10.5.0.3 + +networks: + network-example: + driver: bridge + ipam: + config: + - subnet: 10.5.0.0/16 + gateway: 10.5.0.1 \ No newline at end of file diff --git a/docker-modules/docker-compose-2/communication_same_machine/static_ip_macvlan/docker-compose.yml b/docker-modules/docker-compose-2/communication_same_machine/static_ip_macvlan/docker-compose.yml new file mode 100644 index 0000000000..cef1a0b5cb --- /dev/null +++ b/docker-modules/docker-compose-2/communication_same_machine/static_ip_macvlan/docker-compose.yml @@ -0,0 +1,36 @@ +services: + alpine-app-1: + container_name: alpine-app-1 + build: + context: .. + dockerfile: Dockerfile + image: alpine-app-1 + tty: true + ports: + - 8080:8080 + networks: + network-example: + ipv4_address: 192.168.2.2 + + alpine-app-2: + container_name: alpine-app-2 + build: + context: .. + dockerfile: Dockerfile + image: alpine-app-2 + tty: true + ports: + - 8081:8081 + networks: + network-example: + ipv4_address: 192.168.2.3 + +networks: + network-example: + driver: macvlan + driver_opts: + parent: enp0s3 + ipam: + config: + - subnet: 192.168.2.0/24 + gateway: 192.168.2.1 \ No newline at end of file diff --git a/docker-modules/docker-compose-2/pom.xml b/docker-modules/docker-compose-2/pom.xml new file mode 100644 index 0000000000..851742309d --- /dev/null +++ b/docker-modules/docker-compose-2/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + docker-compose-2 + Demo project for Spring Boot and Docker - Module docker-compose-2 + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + \ No newline at end of file diff --git a/docker-modules/pom.xml b/docker-modules/pom.xml index e71307b7e0..1a87fa5d1c 100644 --- a/docker-modules/pom.xml +++ b/docker-modules/pom.xml @@ -18,6 +18,7 @@ docker-caching docker-compose + docker-compose-2 docker-containers docker-images docker-spring-boot diff --git a/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutLiveTest.java b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutLiveTest.java index 8bd7042dc6..ab80d5665f 100644 --- a/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutLiveTest.java +++ b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutLiveTest.java @@ -1,129 +1,171 @@ package com.baeldung.httpclient; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.TimeUnit; -import org.apache.http.HttpResponse; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.config.SocketConfig; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpParams; -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; +import org.apache.hc.client5.http.ConnectTimeoutException; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.config.RequestConfig; +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; -public class HttpClientTimeoutLiveTest { +import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; - private CloseableHttpResponse response; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.SocketConfig; +import org.apache.hc.core5.util.Timeout; - @After - public final void after() throws IllegalStateException, IOException { - ResponseUtil.closeResponse(response); - } - // tests - @Test - public final void givenUsingOldApi_whenSettingTimeoutViaParameter_thenCorrect() throws IOException { - - DefaultHttpClient httpClient = new DefaultHttpClient(); - int timeout = 5; // seconds - HttpParams httpParams = httpClient.getParams(); - httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000); - httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, timeout * 1000); - httpParams.setParameter(ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000)); - - final HttpGet request = new HttpGet("http://www.github.com"); - HttpResponse execute = httpClient.execute(request); - assertThat(execute.getStatusLine().getStatusCode(), equalTo(200)); - } +import com.baeldung.handler.CustomHttpClientResponseHandler; + +class HttpClientTimeoutLiveTest { @Test - public final void givenUsingNewApi_whenSettingTimeoutViaRequestConfig_thenCorrect() throws IOException { - final int timeout = 2; - final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); - final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); - final HttpGet request = new HttpGet("http://www.github.com"); - - response = client.execute(request); - - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); - } - - @Test - public final void givenUsingNewApi_whenSettingTimeoutViaSocketConfig_thenCorrect() throws IOException { + void givenUsingNewApi_whenSettingTimeoutViaHighLevelApi_thenCorrect() throws IOException { final int timeout = 2; - final SocketConfig config = SocketConfig.custom().setSoTimeout(timeout * 1000).build(); - final CloseableHttpClient client = HttpClientBuilder.create().setDefaultSocketConfig(config).build(); + ConnectionConfig connConfig = ConnectionConfig.custom() + .setConnectTimeout(timeout, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeout, TimeUnit.MILLISECONDS) + .build(); + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofMilliseconds(2000L)) + .build(); + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connConfig); + final HttpGet request = new HttpGet("http://www.github.com"); - response = client.execute(request); + try (CloseableHttpClient client = HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig) + .setConnectionManager(cm) + .build(); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } @Test - public final void givenUsingNewApi_whenSettingTimeoutViaHighLevelApi_thenCorrect() throws IOException { - final int timeout = 5; + void givenUsingNewApi_whenSettingTimeoutViaSocketConfig_thenCorrect() throws IOException { + final int timeout = 2000; + final SocketConfig config = SocketConfig.custom().setSoTimeout(timeout, TimeUnit.MILLISECONDS).build(); - final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); - final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setSocketConfig(config); final HttpGet request = new HttpGet("http://www.github.com"); - response = client.execute(request); + try (CloseableHttpClient client = HttpClientBuilder.create() + .setConnectionManager(cm) + .build(); - assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } } + /** * This simulates a timeout against a domain with multiple routes/IPs to it (not a single raw IP) */ - @Test(expected = ConnectTimeoutException.class) - @Ignore - public final void givenTimeoutIsConfigured_whenTimingOut_thenTimeoutException() throws IOException { + @Disabled + void givenTimeoutIsConfigured_whenTimingOut_thenTimeoutException() throws IOException { final int timeout = 3; - final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); - final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + ConnectionConfig connConfig = ConnectionConfig.custom() + .setConnectTimeout(timeout, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeout, TimeUnit.MILLISECONDS) + .build(); + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofMilliseconds(3000L)) + .build(); + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connConfig); final HttpGet request = new HttpGet("http://www.google.com:81"); - client.execute(request); + + assertThrows(ConnectTimeoutException.class, () -> { + try (CloseableHttpClient client = HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig) + .setConnectionManager(cm) + .build(); + + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(request, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } + }); } @Test - public void whenSecuredRestApiIsConsumed_then200OK() throws IOException { - CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + void whenSecuredRestApiIsConsumed_then200OK() throws IOException { + int timeout = 20000; // milliseconds + + ConnectionConfig connConfig = ConnectionConfig.custom() + .setConnectTimeout(timeout, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeout, TimeUnit.MILLISECONDS) + .build(); + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofMilliseconds(20000L)) + .build(); - int timeout = 20; // seconds - RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout * 1000) - .setConnectTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); HttpGet getMethod = new HttpGet("http://localhost:8082/httpclient-simple/api/bars/1"); getMethod.setConfig(requestConfig); - int hardTimeout = 5; // seconds + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connConfig); + + int hardTimeout = 5000; // milliseconds TimerTask task = new TimerTask() { @Override public void run() { getMethod.abort(); } }; - new Timer(true).schedule(task, hardTimeout * 1000); + new Timer(true).schedule(task, hardTimeout); + + try (CloseableHttpClient client = HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig) + .setConnectionManager(cm) + .build(); + + CloseableHttpResponse response = (CloseableHttpResponse) client + .execute(getMethod, new CustomHttpClientResponseHandler())) { + + final int statusCode = response.getCode(); + System.out.println("HTTP Status of response: " + statusCode); + assertThat(statusCode, equalTo(HttpStatus.SC_OK)); + } - HttpResponse response = httpClient.execute(getMethod); - System.out.println("HTTP Status of response: " + response.getStatusLine().getStatusCode()); } } diff --git a/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutV4LiveTest.java b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutV4LiveTest.java new file mode 100644 index 0000000000..9bf523590e --- /dev/null +++ b/httpclient-simple/src/test/java/com/baeldung/httpclient/HttpClientTimeoutV4LiveTest.java @@ -0,0 +1,116 @@ +package com.baeldung.httpclient; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +class HttpClientTimeoutV4LiveTest { + + private CloseableHttpResponse response; + + @AfterEach + public final void after() throws IllegalStateException, IOException { + ResponseUtil.closeResponse(response); + } + + + @Test + void givenUsingNewApi_whenSettingTimeoutViaRequestConfig_thenCorrect() throws IOException { + final int timeout = 2; + final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); + final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + final HttpGet request = new HttpGet("http://www.github.com"); + + response = client.execute(request); + + assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + } + + @Test + void givenUsingNewApi_whenSettingTimeoutViaSocketConfig_thenCorrect() throws IOException { + final int timeout = 2; + + final SocketConfig config = SocketConfig.custom().setSoTimeout(timeout * 1000).build(); + final CloseableHttpClient client = HttpClientBuilder.create().setDefaultSocketConfig(config).build(); + + final HttpGet request = new HttpGet("http://www.github.com"); + + response = client.execute(request); + + assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + } + + @Test + void givenUsingNewApi_whenSettingTimeoutViaHighLevelApi_thenCorrect() throws IOException { + final int timeout = 5; + + final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); + final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + + final HttpGet request = new HttpGet("http://www.github.com"); + + response = client.execute(request); + + assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + } + + /** + * This simulates a timeout against a domain with multiple routes/IPs to it (not a single raw IP) + */ + @Test + @Disabled + public final void givenTimeoutIsConfigured_whenTimingOut_thenTimeoutException() throws IOException { + final int timeout = 3; + + final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); + final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); + + final HttpGet request = new HttpGet("http://www.google.com:81"); + + assertThrows(ConnectTimeoutException.class, () -> { + client.execute(request); + }); + + } + + @Test + void whenSecuredRestApiIsConsumed_then200OK() throws IOException { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + + int timeout = 20; // seconds + RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout * 1000) + .setConnectTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build(); + HttpGet getMethod = new HttpGet("http://localhost:8082/httpclient-simple/api/bars/1"); + getMethod.setConfig(requestConfig); + + int hardTimeout = 5; // seconds + TimerTask task = new TimerTask() { + @Override + public void run() { + getMethod.abort(); + } + }; + new Timer(true).schedule(task, hardTimeout * 1000); + + HttpResponse response = httpClient.execute(getMethod); + System.out.println("HTTP Status of response: " + response.getStatusLine().getStatusCode()); + } + +} diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml index 937a88da09..bef4b25f1b 100644 --- a/logging-modules/logback/pom.xml +++ b/logging-modules/logback/pom.xml @@ -65,14 +65,14 @@ - com.sun.mail - javax.mail - ${javax.mail.version} + org.eclipse.angus + angus-mail + ${angus.mail.version} - javax.activation - javax.activation-api - ${javax.activation.version} + org.eclipse.angus + angus-activation + ${angus.activation.version} runtime @@ -116,8 +116,8 @@ 20180130 0.1.5 3.3.5 - 1.6.2 - 1.2.0 + 2.0.1 + 2.0.0 1.3.5 2.0.4 diff --git a/persistence-modules/scylladb/README.md b/persistence-modules/scylladb/README.md new file mode 100644 index 0000000000..b845051972 --- /dev/null +++ b/persistence-modules/scylladb/README.md @@ -0,0 +1,2 @@ +## Relevant Articles +- [Introduction to ScyllaDB with Java](https://www.baeldung.com/java-scylladb) diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index e9da6eeca4..3ed9eea431 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -51,6 +51,7 @@ spring-boot-libraries spring-boot-libraries-2 spring-boot-process-automation + spring-boot-logging-logback spring-boot-logging-log4j2 spring-boot-mvc spring-boot-mvc-2 diff --git a/spring-boot-modules/spring-boot-keycloak/pom.xml b/spring-boot-modules/spring-boot-keycloak/pom.xml index d13ef22345..2b259346c3 100644 --- a/spring-boot-modules/spring-boot-keycloak/pom.xml +++ b/spring-boot-modules/spring-boot-keycloak/pom.xml @@ -18,10 +18,6 @@ - - org.springframework.boot - spring-boot-starter - org.springframework.boot spring-boot-starter-oauth2-resource-server @@ -81,7 +77,7 @@ org.codehaus.mojo jaxb2-maven-plugin - 2.3.1 + 2.5.0 xjc @@ -100,5 +96,9 @@ + + + com.baeldung.keycloak.SpringBoot + \ 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 c85438952a..1ad22d9397 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 @@ -30,7 +30,7 @@ class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() - .antMatchers("/customers*", "/users*") + .antMatchers("/customers*") .hasRole("USER") .anyRequest() .permitAll(); diff --git a/spring-boot-modules/spring-boot-logging-log4j2/pom.xml b/spring-boot-modules/spring-boot-logging-log4j2/pom.xml index 7178aeca32..b2a6975964 100644 --- a/spring-boot-modules/spring-boot-logging-log4j2/pom.xml +++ b/spring-boot-modules/spring-boot-logging-log4j2/pom.xml @@ -55,6 +55,10 @@ org.springframework.boot spring-boot-starter-log4j2 + + org.apache.logging.log4j + log4j-spring-boot + org.projectlombok lombok diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/extensions/SpringBootLog4j2ExtensionsApplication.java b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/extensions/SpringBootLog4j2ExtensionsApplication.java new file mode 100644 index 0000000000..7802653c1c --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/java/com/baeldung/extensions/SpringBootLog4j2ExtensionsApplication.java @@ -0,0 +1,25 @@ +package com.baeldung.extensions; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@PropertySource("classpath:application-log4j2-extensions.properties") +public class SpringBootLog4j2ExtensionsApplication { + + private static final Logger logger = LogManager.getLogger(SpringBootLog4j2ExtensionsApplication.class); + + public static void main(String[] args) { + SpringApplication.run(SpringBootLog4j2ExtensionsApplication.class, args); + + logger.trace("Trace log message"); + logger.debug("Debug log message"); + logger.info("Info log message"); + logger.error("Error log message"); + logger.warn("Warn log message"); + logger.fatal("Fatal log message"); + } +} diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log4j2-extensions.properties b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log4j2-extensions.properties new file mode 100644 index 0000000000..8db25c3ff5 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/application-log4j2-extensions.properties @@ -0,0 +1,2 @@ +logging.config=classpath:log4j2-spring.xml +spring.application.name=log4j2-extension \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml index 77a2074b30..c6906e1698 100644 --- a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2-spring.xml @@ -7,6 +7,11 @@ pattern="%style{%d{ISO8601}}{black} %highlight{%-5level }[%style{%t}{bright,blue}] %style{%C{1.}}{bright,yellow}: %msg%n%throwable" /> + + + + @@ -37,7 +42,20 @@ - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2.system.properties b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2.system.properties new file mode 100644 index 0000000000..ba79a90a64 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-log4j2/src/main/resources/log4j2.system.properties @@ -0,0 +1 @@ +log4j2.debug=true \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-logging-logback/.gitignore b/spring-boot-modules/spring-boot-logging-logback/.gitignore new file mode 100644 index 0000000000..d129c74ec9 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/.gitignore @@ -0,0 +1,29 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/logs/ +/bin/ +/mvnw +/mvnw.cmd diff --git a/spring-boot-modules/spring-boot-logging-logback/README.md b/spring-boot-modules/spring-boot-logging-logback/README.md new file mode 100644 index 0000000000..0a9d6d8bba --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/README.md @@ -0,0 +1,6 @@ +## Spring Boot Logging with Logback + +This module contains articles about logging in Spring Boot projects with Logback. + +### Relevant Articles: + diff --git a/spring-boot-modules/spring-boot-logging-logback/pom.xml b/spring-boot-modules/spring-boot-logging-logback/pom.xml new file mode 100644 index 0000000000..deb591c9f0 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + spring-boot-logging-logback + spring-boot-logging-logback + jar + Demo project for Spring Boot Logging with Logback + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-logging-logback/src/main/java/com/baeldung/extensions/SpringBootLogbackExtensionsApplication.java b/spring-boot-modules/spring-boot-logging-logback/src/main/java/com/baeldung/extensions/SpringBootLogbackExtensionsApplication.java new file mode 100644 index 0000000000..28e455a7ea --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/src/main/java/com/baeldung/extensions/SpringBootLogbackExtensionsApplication.java @@ -0,0 +1,22 @@ +package com.baeldung.extensions; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootLogbackExtensionsApplication { + + private static final Logger logger = LoggerFactory.getLogger(SpringBootLogbackExtensionsApplication.class); + + public static void main(String[] args) { + SpringApplication.run(SpringBootLogbackExtensionsApplication.class, args); + + logger.debug("Debug log message"); + logger.info("Info log message"); + logger.error("Error log message"); + logger.warn("Warn log message"); + logger.trace("Trace log message"); + } +} diff --git a/spring-boot-modules/spring-boot-logging-logback/src/main/resources/application.properties b/spring-boot-modules/spring-boot-logging-logback/src/main/resources/application.properties new file mode 100644 index 0000000000..d4860d5765 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=logback-extension \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-logging-logback/src/main/resources/logback-spring.xml b/spring-boot-modules/spring-boot-logging-logback/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000..b2bf8bcd07 --- /dev/null +++ b/spring-boot-modules/spring-boot-logging-logback/src/main/resources/logback-spring.xml @@ -0,0 +1,33 @@ + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + ${LOGS}/${application.name}.log + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOGS}/archived/${application.name}-%d{yyyy-MM-dd}.log + + + + + + + + diff --git a/spring-boot-modules/spring-boot-mvc-5/README.md b/spring-boot-modules/spring-boot-mvc-5/README.md index 95f48ad3f0..782adb7b34 100644 --- a/spring-boot-modules/spring-boot-mvc-5/README.md +++ b/spring-boot-modules/spring-boot-mvc-5/README.md @@ -4,3 +4,4 @@ This module contains articles about Spring Web MVC in Spring Boot projects. ### Relevant Articles: - [Enable and Disable Endpoints at Runtime With Spring Boot](https://www.baeldung.com/spring-boot-enable-disable-endpoints-at-runtime) +- [Extracting a Custom Header From the Request](https://www.baeldung.com/spring-extract-custom-header-request) diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/Application.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/Application.java index 40f5341e29..24174815fa 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/Application.java +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/Application.java @@ -1,19 +1,18 @@ package com.baeldung.web.log.app; -import javax.servlet.ServletRegistration; - import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; -import com.baeldung.web.log.config.CustomeRequestLoggingFilter; - -@EnableAutoConfiguration @ComponentScan("com.baeldung.web.log") @PropertySource("application-log.properties") -@SpringBootApplication +@SpringBootApplication(exclude = { + SecurityAutoConfiguration.class, + ManagementWebSecurityAutoConfiguration.class +}) public class Application { public static void main(final String[] args) { diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedHttpServletRequest.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedHttpServletRequest.java new file mode 100644 index 0000000000..51bbd51ea8 --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedHttpServletRequest.java @@ -0,0 +1,30 @@ +package com.baeldung.web.log.app; + +import org.springframework.util.StreamUtils; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; + +public class CachedHttpServletRequest extends HttpServletRequestWrapper { + + private byte[] cachedPayload; + + public CachedHttpServletRequest(HttpServletRequest request) throws IOException { + super(request); + InputStream requestInputStream = request.getInputStream(); + this.cachedPayload = StreamUtils.copyToByteArray(requestInputStream); + } + + @Override + public ServletInputStream getInputStream() { + return new CachedServletInputStream(this.cachedPayload); + } + + @Override + public BufferedReader getReader() { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedPayload); + return new BufferedReader(new InputStreamReader(byteArrayInputStream)); + } +} diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedServletInputStream.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedServletInputStream.java new file mode 100644 index 0000000000..673d04876b --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/CachedServletInputStream.java @@ -0,0 +1,45 @@ +package com.baeldung.web.log.app; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class CachedServletInputStream extends ServletInputStream { + + private final static Logger LOGGER = LoggerFactory.getLogger(CachedServletInputStream.class); + private InputStream cachedInputStream; + + public CachedServletInputStream(byte[] cachedBody) { + this.cachedInputStream = new ByteArrayInputStream(cachedBody); + } + + @Override + public boolean isFinished() { + try { + return cachedInputStream.available() == 0; + } catch (IOException exp) { + LOGGER.error(exp.getMessage()); + } + return false; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + throw new UnsupportedOperationException(); + } + + @Override + public int read() throws IOException { + return cachedInputStream.read(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/RequestCachingFilter.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/RequestCachingFilter.java new file mode 100644 index 0000000000..e0928550fc --- /dev/null +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/RequestCachingFilter.java @@ -0,0 +1,33 @@ +package com.baeldung.web.log.app; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@Order(value = Ordered.HIGHEST_PRECEDENCE) +@Component +@WebFilter(filterName = "RequestCachingFilter", urlPatterns = "/*") +public class RequestCachingFilter extends OncePerRequestFilter { + + private final static Logger LOGGER = LoggerFactory.getLogger(RequestCachingFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + CachedHttpServletRequest cachedHttpServletRequest = new CachedHttpServletRequest(request); + LOGGER.info("REQUEST DATA: " + IOUtils.toString(cachedHttpServletRequest.getInputStream(), StandardCharsets.UTF_8)); + filterChain.doFilter(cachedHttpServletRequest, response); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/TaxiFareRequestInterceptor.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/TaxiFareRequestInterceptor.java deleted file mode 100644 index 2ea0204dc3..0000000000 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/app/TaxiFareRequestInterceptor.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.baeldung.web.log.app; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; -import org.springframework.web.util.ContentCachingRequestWrapper; - -import com.baeldung.web.log.util.RequestLoggingUtil; - -@Component -public class TaxiFareRequestInterceptor extends HandlerInterceptorAdapter { - - private final static Logger LOGGER = LoggerFactory.getLogger(TaxiFareRequestInterceptor.class); - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String postData; - HttpServletRequest requestCacheWrapperObject = null; - try { - // Uncomment to produce the stream closed issue - // postData = RequestLoggingUtil.getStringFromInputStream(request.getInputStream()); - - // To overcome request stream closed issue - requestCacheWrapperObject = new ContentCachingRequestWrapper(request); - requestCacheWrapperObject.getParameterMap(); - } catch (Exception exception) { - exception.printStackTrace(); - } finally { - postData = RequestLoggingUtil.readPayload(requestCacheWrapperObject); - LOGGER.info("REQUEST DATA: " + postData); - } - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - LOGGER.info("RESPONSE: " + response.getStatus()); - } - -} diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomWebAppInitializer.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomWebAppInitializer.java deleted file mode 100644 index 0f19c6dc48..0000000000 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomWebAppInitializer.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.baeldung.web.log.config; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; - -import org.springframework.web.context.ContextLoaderListener; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.WebApplicationInitializer; - -public class CustomWebAppInitializer implements WebApplicationInitializer { - - @Override - public void onStartup(ServletContext container) throws ServletException { - - AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - context.setConfigLocation("com.baeldung.web.log"); - container.addListener(new ContextLoaderListener(context)); - - ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(context)); - dispatcher.setLoadOnStartup(1); - dispatcher.addMapping("/"); - - container.addFilter("customRequestLoggingFilter", CustomeRequestLoggingFilter.class).addMappingForServletNames(null, false, "dispatcher"); - } -} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomeRequestLoggingFilter.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomeRequestLoggingFilter.java deleted file mode 100644 index 3accb0a06e..0000000000 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/CustomeRequestLoggingFilter.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.baeldung.web.log.config; - -import org.springframework.web.filter.CommonsRequestLoggingFilter; - -public class CustomeRequestLoggingFilter extends CommonsRequestLoggingFilter { - - public CustomeRequestLoggingFilter() { - super.setIncludeQueryString(true); - super.setIncludePayload(true); - super.setMaxPayloadLength(10000); - } -} diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/RequestLoggingFilterConfig.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/RequestLoggingFilterConfig.java index 85728729d5..a0622d45d2 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/RequestLoggingFilterConfig.java +++ b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/RequestLoggingFilterConfig.java @@ -14,7 +14,7 @@ public class RequestLoggingFilterConfig { filter.setIncludePayload(true); filter.setMaxPayloadLength(10000); filter.setIncludeHeaders(false); - filter.setAfterMessagePrefix("REQUEST DATA : "); + filter.setAfterMessagePrefix("REQUEST DATA: "); return filter; } } diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/TaxiFareMVCConfig.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/TaxiFareMVCConfig.java deleted file mode 100644 index fda8a845e9..0000000000 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/config/TaxiFareMVCConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.baeldung.web.log.config; - -import com.baeldung.web.log.app.TaxiFareRequestInterceptor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class TaxiFareMVCConfig implements WebMvcConfigurer { - - @Autowired - private TaxiFareRequestInterceptor taxiFareRequestInterceptor; - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(taxiFareRequestInterceptor).addPathPatterns("/taxifare/*/"); - } -} diff --git a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/util/RequestLoggingUtil.java b/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/util/RequestLoggingUtil.java deleted file mode 100644 index 70c4eaee90..0000000000 --- a/spring-boot-modules/spring-boot-runtime/src/main/java/com/baeldung/web/log/util/RequestLoggingUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.baeldung.web.log.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.io.IOUtils; -import org.springframework.web.util.ContentCachingRequestWrapper; -import org.springframework.web.util.WebUtils; - -public class RequestLoggingUtil { - - public static String getStringFromInputStream(InputStream is) { - StringWriter writer = new StringWriter(); - String encoding = "UTF-8"; - try { - IOUtils.copy(is, writer, encoding); - } catch (IOException e) { - e.printStackTrace(); - } - return writer.toString(); - } - - public static String readPayload(final HttpServletRequest request) throws IOException { - String payloadData = null; - ContentCachingRequestWrapper contentCachingRequestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); - if (null != contentCachingRequestWrapper) { - byte[] buf = contentCachingRequestWrapper.getContentAsByteArray(); - if (buf.length > 0) { - payloadData = new String(buf, 0, buf.length, contentCachingRequestWrapper.getCharacterEncoding()); - } - } - return payloadData; - } - -} diff --git a/spring-boot-modules/spring-boot-runtime/src/main/resources/logback.xml b/spring-boot-modules/spring-boot-runtime/src/main/resources/logback.xml index 7d900d8ea8..4552282d23 100644 --- a/spring-boot-modules/spring-boot-runtime/src/main/resources/logback.xml +++ b/spring-boot-modules/spring-boot-runtime/src/main/resources/logback.xml @@ -7,6 +7,10 @@ + + + + diff --git a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/TaxiFareControllerIntegrationTest.java b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/TaxiFareControllerIntegrationTest.java index 97d669d3fa..b98c77ad2b 100644 --- a/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/TaxiFareControllerIntegrationTest.java +++ b/spring-boot-modules/spring-boot-runtime/src/test/java/com/baeldung/web/controller/TaxiFareControllerIntegrationTest.java @@ -1,23 +1,19 @@ package com.baeldung.web.controller; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; - +import com.baeldung.web.log.app.Application; +import com.baeldung.web.log.data.TaxiRide; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.context.annotation.Configuration; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import com.baeldung.web.log.app.Application; -import com.baeldung.web.log.data.TaxiRide; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; @RunWith(SpringRunner.class) -@SpringBootTest(classes = { Application.class, TaxiFareControllerIntegrationTest.SecurityConfig.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = { Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class TaxiFareControllerIntegrationTest { @LocalServerPort @@ -25,8 +21,6 @@ public class TaxiFareControllerIntegrationTest { @Test public void givenRequest_whenFetchTaxiFareRateCard_thanOK() { - - System.out.println(port); String URL = "http://localhost:" + port + "/spring-rest"; TestRestTemplate testRestTemplate = new TestRestTemplate(); TaxiRide taxiRide = new TaxiRide(true, 10l); @@ -37,16 +31,4 @@ public class TaxiFareControllerIntegrationTest { assertThat(fare, equalTo("200")); } - @Configuration - static class SecurityConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - System.out.println("security being set"); - http - .authorizeRequests() - .anyRequest().permitAll() - .and() - .csrf().disable(); - } - } } \ No newline at end of file diff --git a/spring-core-6/pom.xml b/spring-core-6/pom.xml index 2df7167ca1..a3dda0374f 100644 --- a/spring-core-6/pom.xml +++ b/spring-core-6/pom.xml @@ -10,27 +10,39 @@ http://www.baeldung.com - com.baeldung - parent-modules - 1.0.0-SNAPSHOT + org.springframework.boot + spring-boot-starter-parent + 3.0.1 + org.springframework.boot spring-boot-starter-web - ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-webflux + + + org.mock-server + mockserver-netty + ${mockserver.version} + + + org.mock-server + mockserver-client-java + ${mockserver.version} org.springframework.boot spring-boot-starter-test - ${spring.boot.version} test - org.junit.jupiter - junit-jupiter-api - ${junit-jupiter.version} + io.projectreactor + reactor-test test @@ -76,13 +88,23 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + UTF-8 - 11 - 11 - 2.7.5 + 17 + 17 + 5.14.0 \ No newline at end of file diff --git a/spring-core-6/src/main/java/com/baeldung/httpinterface/Book.java b/spring-core-6/src/main/java/com/baeldung/httpinterface/Book.java new file mode 100644 index 0000000000..a38085852e --- /dev/null +++ b/spring-core-6/src/main/java/com/baeldung/httpinterface/Book.java @@ -0,0 +1,3 @@ +package com.baeldung.httpinterface; + +public record Book(long id, String title, String author, int year) {} diff --git a/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksClient.java b/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksClient.java new file mode 100644 index 0000000000..3034f4f528 --- /dev/null +++ b/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksClient.java @@ -0,0 +1,23 @@ +package com.baeldung.httpinterface; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.support.WebClientAdapter; +import org.springframework.web.service.invoker.HttpServiceProxyFactory; + +@Component +public class BooksClient { + + private final BooksService booksService; + + public BooksClient(WebClient webClient) { + HttpServiceProxyFactory httpServiceProxyFactory = + HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)) + .build(); + booksService = httpServiceProxyFactory.createClient(BooksService.class); + } + + public BooksService getBooksService() { + return booksService; + } +} diff --git a/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksService.java b/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksService.java new file mode 100644 index 0000000000..a9cf6ec58a --- /dev/null +++ b/spring-core-6/src/main/java/com/baeldung/httpinterface/BooksService.java @@ -0,0 +1,26 @@ +package com.baeldung.httpinterface; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.service.annotation.DeleteExchange; +import org.springframework.web.service.annotation.GetExchange; +import org.springframework.web.service.annotation.PostExchange; + +import java.util.List; + +interface BooksService { + + @GetExchange("/books") + List getBooks(); + + @GetExchange("/books/{id}") + Book getBook(@PathVariable long id); + + @PostExchange("/books") + Book saveBook(@RequestBody Book book); + + @DeleteExchange("/books/{id}") + ResponseEntity deleteBook(@PathVariable long id); + +} diff --git a/spring-core-6/src/main/java/com/baeldung/reinitializebean/cache/ConfigManager.java b/spring-core-6/src/main/java/com/baeldung/reinitializebean/cache/ConfigManager.java index 1e4dee6cc4..240fb350c2 100644 --- a/spring-core-6/src/main/java/com/baeldung/reinitializebean/cache/ConfigManager.java +++ b/spring-core-6/src/main/java/com/baeldung/reinitializebean/cache/ConfigManager.java @@ -5,7 +5,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; diff --git a/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockServerTest.java b/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockServerTest.java new file mode 100644 index 0000000000..22e00c16ae --- /dev/null +++ b/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockServerTest.java @@ -0,0 +1,217 @@ +package com.baeldung.httpinterface; + +import org.apache.http.HttpException; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockserver.client.MockServerClient; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.configuration.Configuration; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.List; + +import org.mockserver.model.HttpRequest; +import org.mockserver.model.MediaType; +import org.mockserver.verify.VerificationTimes; +import org.slf4j.event.Level; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.matchers.Times.exactly; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BooksServiceMockServerTest { + + private static final String SERVER_ADDRESS = "localhost"; + private static final String PATH = "/books"; + + private static int serverPort; + private static ClientAndServer mockServer; + private static String serviceUrl; + + @BeforeAll + static void startServer() throws IOException { + serverPort = getFreePort(); + serviceUrl = "http://" + SERVER_ADDRESS + ":" + serverPort; + + Configuration config = Configuration.configuration().logLevel(Level.WARN); + mockServer = startClientAndServer(config, serverPort); + + mockAllBooksRequest(); + mockBookByIdRequest(); + mockSaveBookRequest(); + mockDeleteBookRequest(); + } + + @AfterAll + static void stopServer() { + mockServer.stop(); + } + + @Test + void givenMockedGetResponse_whenGetBooksServiceMethodIsCalled_thenTwoBooksAreReturned() { + BooksClient booksClient = new BooksClient(WebClient.builder().baseUrl(serviceUrl).build()); + BooksService booksService = booksClient.getBooksService(); + + List books = booksService.getBooks(); + assertEquals(2, books.size()); + + mockServer.verify( + HttpRequest.request() + .withMethod(HttpMethod.GET.name()) + .withPath(PATH), + VerificationTimes.exactly(1) + ); + } + + @Test + void givenMockedGetResponse_whenGetExistingBookServiceMethodIsCalled_thenCorrectBookIsReturned() { + BooksClient booksClient = new BooksClient(WebClient.builder().baseUrl(serviceUrl).build()); + BooksService booksService = booksClient.getBooksService(); + + Book book = booksService.getBook(1); + assertEquals("Book_1", book.title()); + + mockServer.verify( + HttpRequest.request() + .withMethod(HttpMethod.GET.name()) + .withPath(PATH + "/1"), + VerificationTimes.exactly(1) + ); + } + + @Test + void givenMockedGetResponse_whenGetNonExistingBookServiceMethodIsCalled_thenCorrectBookIsReturned() { + BooksClient booksClient = new BooksClient(WebClient.builder().baseUrl(serviceUrl).build()); + BooksService booksService = booksClient.getBooksService(); + + assertThrows(WebClientResponseException.class, () -> booksService.getBook(9)); + } + + @Test + void givenCustomErrorHandlerIsSet_whenGetNonExistingBookServiceMethodIsCalled_thenCustomExceptionIsThrown() { + BooksClient booksClient = new BooksClient(WebClient.builder() + .defaultStatusHandler(HttpStatusCode::isError, resp -> + Mono.just(new MyServiceException("Custom exception"))) + .baseUrl(serviceUrl) + .build()); + + BooksService booksService = booksClient.getBooksService(); + assertThrows(MyServiceException.class, () -> booksService.getBook(9)); + } + + @Test + void givenMockedPostResponse_whenSaveBookServiceMethodIsCalled_thenCorrectBookIsReturned() { + BooksClient booksClient = new BooksClient(WebClient.builder().baseUrl(serviceUrl).build()); + BooksService booksService = booksClient.getBooksService(); + + Book book = booksService.saveBook(new Book(3, "Book_3", "Author_3", 2000)); + assertEquals("Book_3", book.title()); + + mockServer.verify( + HttpRequest.request() + .withMethod(HttpMethod.POST.name()) + .withPath(PATH), + VerificationTimes.exactly(1) + ); + } + + @Test + void givenMockedDeleteResponse_whenDeleteBookServiceMethodIsCalled_thenCorrectCodeIsReturned() { + BooksClient booksClient = new BooksClient(WebClient.builder().baseUrl(serviceUrl).build()); + BooksService booksService = booksClient.getBooksService(); + + ResponseEntity response = booksService.deleteBook(3); + assertEquals(HttpStatusCode.valueOf(200), response.getStatusCode()); + + mockServer.verify( + HttpRequest.request() + .withMethod(HttpMethod.DELETE.name()) + .withPath(PATH + "/3"), + VerificationTimes.exactly(1) + ); + } + + private static int getFreePort () throws IOException { + try (ServerSocket serverSocket = new ServerSocket(0)) { + return serverSocket.getLocalPort(); + } + } + + private static void mockAllBooksRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH) + .withMethod(HttpMethod.GET.name()), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + .withContentType(MediaType.APPLICATION_JSON) + .withBody("[{\"id\":1,\"title\":\"Book_1\",\"author\":\"Author_1\",\"year\":1998},{\"id\":2,\"title\":\"Book_2\",\"author\":\"Author_2\",\"year\":1999}]") + ); + } + + private static void mockBookByIdRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH + "/1") + .withMethod(HttpMethod.GET.name()), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + .withContentType(MediaType.APPLICATION_JSON) + .withBody("{\"id\":1,\"title\":\"Book_1\",\"author\":\"Author_1\",\"year\":1998}") + ); + } + + private static void mockSaveBookRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH) + .withMethod(HttpMethod.POST.name()) + .withContentType(MediaType.APPLICATION_JSON) + .withBody("{\"id\":3,\"title\":\"Book_3\",\"author\":\"Author_3\",\"year\":2000}"), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + .withContentType(MediaType.APPLICATION_JSON) + .withBody("{\"id\":3,\"title\":\"Book_3\",\"author\":\"Author_3\",\"year\":2000}") + ); + } + + private static void mockDeleteBookRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH + "/3") + .withMethod(HttpMethod.DELETE.name()), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + ); + } + +} diff --git a/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockitoTest.java b/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockitoTest.java new file mode 100644 index 0000000000..7a82835ef3 --- /dev/null +++ b/spring-core-6/src/test/java/com/baeldung/httpinterface/BooksServiceMockitoTest.java @@ -0,0 +1,88 @@ +package com.baeldung.httpinterface; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import static org.mockito.BDDMockito.*; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +class BooksServiceMockitoTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private WebClient webClient; + + @InjectMocks + private BooksClient booksClient; + + @Test + void givenMockedWebClientReturnsTwoBooks_whenGetBooksServiceMethodIsCalled_thenListOfTwoBooksIsReturned() { + given(webClient.method(HttpMethod.GET) + .uri(anyString(), anyMap()) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>(){})) + .willReturn(Mono.just(List.of( + new Book(1,"Book_1", "Author_1", 1998), + new Book(2, "Book_2", "Author_2", 1999) + ))); + + BooksService booksService = booksClient.getBooksService(); + List books = booksService.getBooks(); + assertEquals(2, books.size()); + } + + @Test + void givenMockedWebClientReturnsBook_whenGetBookServiceMethodIsCalled_thenBookIsReturned() { + given(webClient.method(HttpMethod.GET) + .uri(anyString(), anyMap()) + .retrieve() + .bodyToMono(new ParameterizedTypeReference(){})) + .willReturn(Mono.just(new Book(1,"Book_1", "Author_1", 1998))); + + BooksService booksService = booksClient.getBooksService(); + Book book = booksService.getBook(1); + assertEquals("Book_1", book.title()); + } + + @Test + void givenMockedWebClientReturnsBook_whenSaveBookServiceMethodIsCalled_thenBookIsReturned() { + given(webClient.method(HttpMethod.POST) + .uri(anyString(), anyMap()) + .retrieve() + .bodyToMono(new ParameterizedTypeReference(){})) + .willReturn(Mono.just(new Book(3, "Book_3", "Author_3", 2000))); + + BooksService booksService = booksClient.getBooksService(); + Book book = booksService.saveBook(new Book(3, "Book_3", "Author_3", 2000)); + assertEquals("Book_3", book.title()); + } + + @Test + void givenMockedWebClientReturnsOk_whenDeleteBookServiceMethodIsCalled_thenOkCodeIsReturned() { + given(webClient.method(HttpMethod.DELETE) + .uri(anyString(), anyMap()) + .retrieve() + .toBodilessEntity() + .block(any()) + .getStatusCode()) + .willReturn(HttpStatusCode.valueOf(200)); + + BooksService booksService = booksClient.getBooksService(); + ResponseEntity response = booksService.deleteBook(3); + assertEquals(HttpStatusCode.valueOf(200), response.getStatusCode()); + } + +} diff --git a/spring-core-6/src/test/java/com/baeldung/httpinterface/MyServiceException.java b/spring-core-6/src/test/java/com/baeldung/httpinterface/MyServiceException.java new file mode 100644 index 0000000000..e09335a211 --- /dev/null +++ b/spring-core-6/src/test/java/com/baeldung/httpinterface/MyServiceException.java @@ -0,0 +1,9 @@ +package com.baeldung.httpinterface; + +public class MyServiceException extends RuntimeException { + + MyServiceException(String msg) { + super(msg); + } + +} diff --git a/spring-reactive-modules/README.md b/spring-reactive-modules/README.md index 3522efec17..57c3eebbff 100644 --- a/spring-reactive-modules/README.md +++ b/spring-reactive-modules/README.md @@ -1,4 +1,3 @@ ## Spring Reactive This module contains modules about Spring Reactive -- [How to Resolve Spring Webflux DataBufferLimitException](https://www.baeldung.com/spring-webflux-databufferlimitexception) diff --git a/spring-reactive-modules/spring-5-reactive-filters/README.md b/spring-reactive-modules/spring-5-reactive-filters/README.md index aa8d2800e2..815ca35442 100644 --- a/spring-reactive-modules/spring-5-reactive-filters/README.md +++ b/spring-reactive-modules/spring-5-reactive-filters/README.md @@ -7,9 +7,4 @@ The "REST With Spring" Classes: https://bit.ly/restwithspring ### Relevant Articles -- [Exploring the Spring 5 WebFlux URL Matching](https://www.baeldung.com/spring-5-mvc-url-matching) -- [Reactive WebSockets with Spring 5](https://www.baeldung.com/spring-5-reactive-websockets) - [Spring WebFlux Filters](https://www.baeldung.com/spring-webflux-filters) -- [How to Set a Header on a Response with Spring 5](https://www.baeldung.com/spring-response-header) -- [A Guide to Spring Session Reactive Support: WebSession](https://www.baeldung.com/spring-session-reactive) -- More articles: [[next -->]](../spring-5-reactive-2) diff --git a/spring-web-modules/spring-mvc-file/README.md b/spring-web-modules/spring-mvc-file/README.md index c1622f80c2..c4843031ff 100644 --- a/spring-web-modules/spring-mvc-file/README.md +++ b/spring-web-modules/spring-mvc-file/README.md @@ -6,4 +6,4 @@ ### Relevant Articles: - +- [Convert byte[] to MultipartFile in Java](https://www.baeldung.com/java-convert-byte-array-to-multipartfile) diff --git a/testing-modules/instancio/.gitignore b/testing-modules/instancio/.gitignore new file mode 100644 index 0000000000..7f300600e6 --- /dev/null +++ b/testing-modules/instancio/.gitignore @@ -0,0 +1,14 @@ +*.class + +.settings +.project + +#folders# +/target +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear diff --git a/testing-modules/instancio/README.md b/testing-modules/instancio/README.md new file mode 100644 index 0000000000..881477f036 --- /dev/null +++ b/testing-modules/instancio/README.md @@ -0,0 +1 @@ +### Relevant articles diff --git a/testing-modules/instancio/pom.xml b/testing-modules/instancio/pom.xml new file mode 100644 index 0000000000..137da91897 --- /dev/null +++ b/testing-modules/instancio/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + instancio + instancio + jar + + + com.baeldung + testing-modules + 1.0.0-SNAPSHOT + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + + org.instancio + instancio-junit + ${instancio.version} + test + + + org.assertj + assertj-core + ${assertj.version} + + + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + + + + 2.6.0 + 2.14.1 + 5.9.2 + + \ No newline at end of file diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/abstracttype/AbstractItem.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/abstracttype/AbstractItem.java new file mode 100644 index 0000000000..4dd21e6ffb --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/abstracttype/AbstractItem.java @@ -0,0 +1,6 @@ +package com.baeldung.instancio.abstracttype; + +public interface AbstractItem { + + T getValue(); +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Item.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Item.java new file mode 100644 index 0000000000..19450a936c --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Item.java @@ -0,0 +1,18 @@ +package com.baeldung.instancio.generics; + +import com.baeldung.instancio.abstracttype.AbstractItem; + +public class Item implements AbstractItem { + + private T value; + + @Override + public T getValue() { + return value; + } + + @Override + public String toString() { + return String.format("Item[value=%s]", value); + } +} \ No newline at end of file diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Pair.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Pair.java new file mode 100644 index 0000000000..48b43fe9aa --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Pair.java @@ -0,0 +1,20 @@ +package com.baeldung.instancio.generics; + +public class Pair { + + private L left; + private R right; + + public L getLeft() { + return left; + } + + public R getRight() { + return right; + } + + @Override + public String toString() { + return String.format("Pair[left=%s, right=%s]", left, right); + } +} \ No newline at end of file diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Triplet.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Triplet.java new file mode 100644 index 0000000000..528c4d7c98 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/generics/Triplet.java @@ -0,0 +1,24 @@ +package com.baeldung.instancio.generics; + +public class Triplet { + private L left; + private M middle; + private R right; + + public L getLeft() { + return left; + } + + public M getMiddle() { + return middle; + } + + public R getRight() { + return right; + } + + @Override + public String toString() { + return String.format("Triplet[left=%s, middle=%s, right=%s]", left, right, middle); + } +} \ No newline at end of file diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Address.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Address.java new file mode 100644 index 0000000000..cda34690e5 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Address.java @@ -0,0 +1,31 @@ +package com.baeldung.instancio.student.model; + +import com.baeldung.instancio.util.PrettyToString; + +public class Address { + private String street; + private String city; + private String country; + + public String getStreet() { + return street; + } + + public String getCity() { + return city; + } + + public String getCountry() { + return country; + } + + public void setCountry(final String country) { + this.country = country; + } + + @Override + public String toString() { + return PrettyToString.toPrettyString(this); + } + +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/ContactInfo.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/ContactInfo.java new file mode 100644 index 0000000000..71534192c6 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/ContactInfo.java @@ -0,0 +1,28 @@ +package com.baeldung.instancio.student.model; + +import com.baeldung.instancio.util.PrettyToString; + +import java.util.List; + +public class ContactInfo { + private Address address; + private List phones; + private String email; + + public Address getAddress() { + return address; + } + + public List getPhones() { + return phones; + } + + public String getEmail() { + return email; + } + + @Override + public String toString() { + return PrettyToString.toPrettyString(this); + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Course.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Course.java new file mode 100644 index 0000000000..543f6eaf4e --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Course.java @@ -0,0 +1,53 @@ +package com.baeldung.instancio.student.model; + +import com.baeldung.instancio.util.PrettyToString; + +import java.time.Duration; +import java.time.LocalDate; +import java.util.Objects; + +public class Course { + private String title; + private String code; + private LocalDate startDate; + private Duration duration; + private String instructor; + + public String getTitle() { + return title; + } + + public String getCode() { + return code; + } + + public LocalDate getStartDate() { + return startDate; + } + + public Duration getDuration() { + return duration; + } + + public String getInstructor() { + return instructor; + } + + @Override + public String toString() { + return PrettyToString.toPrettyString(this); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof Course)) return false; + final Course course = (Course) o; + return Objects.equals(getCode(), course.getCode()); + } + + @Override + public int hashCode() { + return Objects.hash(getCode()); + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/EmergencyContact.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/EmergencyContact.java new file mode 100644 index 0000000000..7c5ce21738 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/EmergencyContact.java @@ -0,0 +1,14 @@ +package com.baeldung.instancio.student.model; + +public class EmergencyContact { + private String name; + private Phone phone; + + public String getName() { + return name; + } + + public Phone getPhone() { + return phone; + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Grade.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Grade.java new file mode 100644 index 0000000000..8bd9341a18 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Grade.java @@ -0,0 +1,5 @@ +package com.baeldung.instancio.student.model; + +public enum Grade { + A, B, C, D, F +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Phone.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Phone.java new file mode 100644 index 0000000000..887cce5c7d --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Phone.java @@ -0,0 +1,23 @@ +package com.baeldung.instancio.student.model; + +import com.baeldung.instancio.util.PrettyToString; + +public class Phone { + + private String countryCode; + private String number; + + public String getCountryCode() { + return countryCode; + } + + public String getNumber() { + return number; + } + + @Override + public String toString() { + return PrettyToString.toPrettyString(this); + } + +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Student.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Student.java new file mode 100644 index 0000000000..fd6b3cbc1c --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/model/Student.java @@ -0,0 +1,57 @@ +package com.baeldung.instancio.student.model; + + +import com.baeldung.instancio.util.PrettyToString; + +import java.time.LocalDate; +import java.time.Year; +import java.util.Map; +import java.util.UUID; + +public class Student { + private UUID id; + private String firstName; + private String lastName; + private LocalDate dateOfBirth; + private ContactInfo contactInfo; + private EmergencyContact emergencyContact; + private Year enrollmentYear; + private Map courseGrades; + + public UUID getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public LocalDate getDateOfBirth() { + return dateOfBirth; + } + + public ContactInfo getContactInfo() { + return contactInfo; + } + + public EmergencyContact getEmergencyContact() { + return emergencyContact; + } + + public Year getEnrollmentYear() { + return enrollmentYear; + } + + public Map getCourseGrades() { + return courseGrades; + } + + @Override + public String toString() { + return PrettyToString.toPrettyString(this); + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/CourseService.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/CourseService.java new file mode 100644 index 0000000000..e71da0b23e --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/CourseService.java @@ -0,0 +1,11 @@ +package com.baeldung.instancio.student.service; + +import com.baeldung.instancio.student.model.Course; + +public class CourseService { + + public Course getByCode(String courseCode) { + throw new UnsupportedOperationException( + "This class should be mocked. Persistence is not available in a unit test"); + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentException.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentException.java new file mode 100644 index 0000000000..2398556b73 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentException.java @@ -0,0 +1,8 @@ +package com.baeldung.instancio.student.service; + +public class EnrollmentException extends RuntimeException { + + public EnrollmentException(final String message) { + super(message); + } +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentService.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentService.java new file mode 100644 index 0000000000..d505a5881c --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/student/service/EnrollmentService.java @@ -0,0 +1,30 @@ +package com.baeldung.instancio.student.service; + +import com.baeldung.instancio.student.model.Course; +import com.baeldung.instancio.student.model.Grade; +import com.baeldung.instancio.student.model.Student; + +import java.util.Collection; + +public class EnrollmentService { + + private CourseService courseService; + + public boolean enrollStudent(Student student, Course course) { + Collection grades = student.getCourseGrades().values(); + if (grades.contains(Grade.F)) { + throw new EnrollmentException(String.format("Student %s has at least 1 failed course", student.getId())); + } + // process enrollment... + return true; + } + + public boolean enrollStudent(Student student, String courseCode) { + Course course = courseService.getByCode(courseCode); + if (course == null) { + throw new EnrollmentException("Course not found: " + courseCode); + } + return enrollStudent(student, course); + } + +} diff --git a/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java b/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java new file mode 100644 index 0000000000..163f3673c7 --- /dev/null +++ b/testing-modules/instancio/src/main/java/com/baeldung/instancio/util/PrettyToString.java @@ -0,0 +1,21 @@ +package com.baeldung.instancio.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +public class PrettyToString { + + private static final ObjectWriter objectWriter = new ObjectMapper() + .registerModules(new JavaTimeModule()) + .writerWithDefaultPrettyPrinter(); + + public static String toPrettyString(Object obj) { + try { + return objectWriter.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java new file mode 100644 index 0000000000..524c8dbc91 --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/basics/CreateStudentUnitTest.java @@ -0,0 +1,174 @@ +package com.baeldung.instancio.basics; + +import com.baeldung.instancio.student.model.Address; +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.Course; +import com.baeldung.instancio.student.model.Grade; +import com.baeldung.instancio.student.model.Phone; +import com.baeldung.instancio.student.model.Student; +import org.instancio.Instancio; +import org.instancio.Model; +import org.instancio.junit.InstancioExtension; +import org.instancio.junit.InstancioSource; +import org.instancio.junit.Seed; +import org.instancio.junit.WithSettings; +import org.instancio.settings.Keys; +import org.instancio.settings.Settings; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; + +import java.time.LocalDate; +import java.time.Year; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.all; +import static org.instancio.Select.field; + +/** + * Sample test class using Instancio to generate test objects. + * + *

Note: using {@link InstancioExtension} is optional. The extension adds support for: + *

+ * - reporting seed value if a test fails + * - {@link Seed} annotation for reproducing failed tests + * - {@link WithSettings} for injecting custom settings, if needed + */ +@ExtendWith(InstancioExtension.class) +class CreateStudentUnitTest { + + /** + * Common settings to be used by all test methods. + */ + @WithSettings + private static final Settings settings = Settings.create() + .set(Keys.COLLECTION_MAX_SIZE, 3); + + /** + * A {@link Model} is a template for creating objects. + * Objects created from a model can be created as is, or customized, if needed. + */ + private static Model studentModel() { + return Instancio.of(Student.class) + .generate(field(Student::getDateOfBirth), gen -> gen.temporal().localDate().past()) + .generate(field(Student::getEnrollmentYear), gen -> gen.temporal().year().past()) + .generate(field(ContactInfo::getEmail), gen -> gen.text().pattern("#a#a#a#a#a#a@example.com")) + .generate(field(Phone::getCountryCode), gen -> gen.string().prefix("+").digits().maxLength(2)) + .withNullable(field(Student::getEmergencyContact)) + .toModel(); + } + + private static void assertModelProperties(Student student) { + assertThat(student.getDateOfBirth()).isBefore(LocalDate.now()); + assertThat(student.getEnrollmentYear()).isLessThan(Year.now()); + assertThat(student.getContactInfo().getEmail()).matches("^[a-zA-Z0-9]+@example.com$"); + assertThat(student.getContactInfo().getPhones()) + .extracting(Phone::getCountryCode) + .allSatisfy(countryCode -> assertThat(countryCode).matches("^\\+\\d\\d?$")); + } + + /** + * Generates random Student objects based on the Model. + */ + @Test + void whenGivenAModel_thenShouldCreateAStudentBasedOnModel() { + Student student = Instancio.create(studentModel()); + + assertModelProperties(student); + } + + /** + * Generate a list of international students based on the Model. + */ + @Test + void whenGivenAModel_thenShouldCreateAListOfStudents() { + // Given + final int numberOfStudents = 100; + final List countries = Arrays.asList( + "China", "Germany", "India", "Poland", "Romania", "Sweden", "Switzerland"); + + // When + List studentList = Instancio.ofList(studentModel()) + .size(numberOfStudents) + .generate(field(Address::getCountry), gen -> gen.oneOf(countries)) + .create(); + + // Then + assertThat(studentList).hasSize(numberOfStudents) + .allSatisfy(CreateStudentUnitTest::assertModelProperties) + .extracting(student -> student.getContactInfo().getAddress().getCountry()) + .allSatisfy(country -> assertThat(country).isIn(countries)); + } + + /** + * Use the Model to create a student with a failed course. + * This test also demonstrates how Instancio can provide + * arguments to parameterized tests. + * + * @param failedCourse provided by Instancio + */ + @InstancioSource + @ParameterizedTest + void whenGivenFailingGrade_thenStudentShouldHaveAFailedCourse(final Course failedCourse) { + // Given + final Model model = studentModel(); + final Grade failingGrade = Grade.F; + + // When + Student student = Instancio.of(model) + .generate(field(Student::getCourseGrades), gen -> gen.map().with(failedCourse, failingGrade)) + .create(); + + // Then + Map courseGrades = student.getCourseGrades(); + assertModelProperties(student); + assertThat(courseGrades).containsEntry(failedCourse, failingGrade); + } + + /** + * Generate a student with only Grades A and/or B. + */ + @Test + void whenGivenGoodGrades_thenCreatedStudentShouldHaveExpectedGrades() { + // Given + final int numOfCourses = 10; + final Grade[] grades = {Grade.A, Grade.B}; + + // When + Student student = Instancio.of(studentModel()) + .generate(all(Grade.class), gen -> gen.oneOf(grades)) + .generate(field(Student::getCourseGrades), gen -> gen.map().size(numOfCourses)) + .create(); + + // Then + Map courseGrades = student.getCourseGrades(); + assertModelProperties(student); + assertThat(courseGrades.values()) + .hasSize(numOfCourses) + .containsAnyOf(grades) + .doesNotContain(Grade.C, Grade.D, Grade.F); + } + + /** + * Generate String fields prefixed with the field's name. + */ + @Test + void whenGivenCustomSettings_thenStudentShouldBeCreatedUsingTheSettings() { + // Given + Settings customSettings = Settings.create() + .set(Keys.STRING_FIELD_PREFIX_ENABLED, true); + + // When + Student student = Instancio.of(studentModel()) + .withSettings(customSettings) + .create(); + + // Then + assertThat(student.getFirstName()).startsWith("firstName_"); + assertThat(student.getLastName()).startsWith("lastName_"); + assertThat(student.getContactInfo().getAddress().getCity()).startsWith("city_"); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java new file mode 100644 index 0000000000..8e5643baec --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generators/UsingCustomGeneratorUnitTest.java @@ -0,0 +1,57 @@ +package com.baeldung.instancio.generators; + +import com.baeldung.instancio.student.model.Address; +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.Phone; +import com.baeldung.instancio.student.model.Student; +import org.instancio.Instancio; +import org.instancio.Random; +import org.instancio.generator.AfterGenerate; +import org.instancio.generator.Generator; +import org.instancio.generator.Hints; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.all; +import static org.instancio.Select.field; + +class UsingCustomGeneratorUnitTest { + private static final String ARGENTINA = "Argentina"; + private static final int PHONES_SIZE = 5; + + private static final Generator

ADDRESS_GENERATOR = new Generator
() { + @Override + public Address generate(final Random random) { + Address address = new Address(); + address.setCountry(ARGENTINA); + return address; + } + + @Override + public Hints hints() { + // The hint telling the engine to populate any field that has a null value + return Hints.afterGenerate(AfterGenerate.POPULATE_NULLS); + } + }; + + @Test + void whenGivenAGenerator_objectShouldBeCreatedUsingCustomGenerator() { + Student student = Instancio.of(Student.class) + .supply(all(Address.class), ADDRESS_GENERATOR) + .generate(field(ContactInfo::getPhones), gen -> gen.collection().size(PHONES_SIZE)) + .create(); + + ContactInfo contactInfo = student.getContactInfo(); + Address address = contactInfo.getAddress(); + List phones = contactInfo.getPhones(); + + assertThat(phones).hasSize(PHONES_SIZE); + assertThat(address.getCountry()).isEqualTo(ARGENTINA); + // null fields were populated with random values + assertThat(address.getStreet()).isNotNull(); + assertThat(address.getCity()).isNotNull(); + } + +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java new file mode 100644 index 0000000000..4ed22abeaf --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/generics/CreatingGenericTypesUnitTest.java @@ -0,0 +1,100 @@ +package com.baeldung.instancio.generics; + +import com.baeldung.instancio.student.model.Address; +import org.instancio.Instancio; +import org.instancio.TypeToken; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.allLongs; +import static org.instancio.Select.allStrings; + +/** + * Examples of creating generic types using {@link TypeToken}. + */ +class CreatingGenericTypesUnitTest { + + + @Test + void whenGivenTypeToken_shouldCreateItem() { + Item item = Instancio.create(new TypeToken>() {}); + + assertThat(item.getValue()).isNotBlank(); + } + + @Test + void whenGivenTypeToken_shouldCreateCustomizedItem() { + Pair pair = Instancio.of(new TypeToken>() {}) + .generate(allStrings(), gen -> gen.oneOf("foo", "bar")) + .generate(allLongs(), gen -> gen.longs().range(5L, 10L)) + .create(); + + assertThat(pair.getLeft()).isIn("foo", "bar"); + assertThat(pair.getRight()).isBetween(5L, 10L); + } + + @Test + void whenGivenTypeToken_shouldCreateTriplet() { + Triplet triplet = Instancio.create(new TypeToken>() {}); + + assertThat(triplet.getLeft()).isNotBlank(); + assertThat(triplet.getMiddle()).isNotNull(); + assertThat(triplet.getRight()).isNotNull(); + } + + @Test + void whenGivenTypeToken_shouldCreateCollection() { + List list = Instancio.create(new TypeToken>() {}); + + assertThat(list).isNotEmpty().doesNotContainNull(); + } + + @Test + void whenGivenTypeToken_shouldCreateMap() { + Map map = Instancio.create(new TypeToken>() {}); + + assertThat(map).isNotEmpty(); + } + + /** + * Using type token to create more complex generic objects. + */ + @Test + void whenGivenTypeTokenWithNestGenerics_shouldCreateAnInstanceOfSpecifiedType() { + List>> list = Instancio.create( + new TypeToken>>>() {}); + + assertThat(list) + .isNotEmpty() + .allSatisfy(triplet -> { + assertThat(triplet.getLeft()).isInstanceOf(Integer.class); + assertThat(triplet.getMiddle()).isInstanceOf(LocalDate.class); + assertThat(triplet.getRight()) + .isInstanceOf(Item.class) + .satisfies(item -> assertThat(item.getValue()).isNotBlank()); + }); + + // Sample output + list.forEach(System.out::println); + } + + + /** + * Alternative way to create generic objects is using 'withTypeParameters'. + * However, this approach generates an "unchecked assignment" warning. + */ + @Test + @SuppressWarnings("unchecked") + void whenGivenClassWithTypeParameters_shouldCreateGenericType() { + Map map = Instancio.of(Map.class) + .withTypeParameters(UUID.class, Address.class) + .create(); + + assertThat(map).isNotEmpty(); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/mockito/InstancioWithMockitoUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/mockito/InstancioWithMockitoUnitTest.java new file mode 100644 index 0000000000..1382589a84 --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/mockito/InstancioWithMockitoUnitTest.java @@ -0,0 +1,45 @@ +package com.baeldung.instancio.mockito; + +import com.baeldung.instancio.student.model.Course; +import com.baeldung.instancio.student.model.Grade; +import com.baeldung.instancio.student.model.Student; +import com.baeldung.instancio.student.service.CourseService; +import com.baeldung.instancio.student.service.EnrollmentService; +import org.instancio.Instancio; +import org.instancio.junit.InstancioExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.all; +import static org.mockito.Mockito.when; + +@ExtendWith({MockitoExtension.class, InstancioExtension.class}) +class InstancioWithMockitoUnitTest { + + @Mock + private CourseService courseService; + + @InjectMocks + private EnrollmentService enrollmentService; + + @Test + void givenStudentWithoutGradeF_thenShouldEnrollStudentInCourse() { + // Given + Student student = Instancio.of(Student.class) + .generate(all(Grade.class), gen -> gen.enumOf(Grade.class).excluding(Grade.F)) + .create(); + + Course course = Instancio.create(Course.class); + when(courseService.getByCode(course.getCode())).thenReturn(course); + + // When + boolean isEnrolled = enrollmentService.enrollStudent(student, course.getCode()); + + // Then + assertThat(isEnrolled).isTrue(); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/reproducing/ReproducingTestFailureUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/reproducing/ReproducingTestFailureUnitTest.java new file mode 100644 index 0000000000..0d6c8cfa9a --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/reproducing/ReproducingTestFailureUnitTest.java @@ -0,0 +1,60 @@ +package com.baeldung.instancio.reproducing; + +import com.baeldung.instancio.student.model.Course; +import com.baeldung.instancio.student.model.Student; +import com.baeldung.instancio.student.service.EnrollmentService; +import org.instancio.Instancio; +import org.instancio.junit.InstancioExtension; +import org.instancio.junit.Seed; +import org.instancio.junit.WithSettings; +import org.instancio.settings.Keys; +import org.instancio.settings.Settings; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(InstancioExtension.class) +class ReproducingTestFailureUnitTest { + + @WithSettings + private static final Settings settings = Settings.create() + .set(Keys.COLLECTION_MIN_SIZE, 50) + .set(Keys.COLLECTION_MAX_SIZE, 100) + .lock(); + + private final EnrollmentService enrollmentService = new EnrollmentService(); + + /** + * This test fails because the {@code enrollInCourse()} method + * throws an exception if the student has at least one grade F. + * + *

Sample error message generated by {@link InstancioExtension}: + * + *

+     * timestamp = 2023-01-24T13:50:12.436704221, Instancio = Test method 'enrollStudent' failed with seed: 1234
+     * 
+ *

+ * Using the reported seed value we can reproduce the test failure. + */ + @Test + @Seed(1234) + @Disabled("This test fails on purpose to demonstrate failure reporting by InstancioExtension") + void whenGivenNoFailingGrades_thenShouldEnrollStudentInCourse() { + // Given + Course course = Instancio.create(Course.class); + Student student = Instancio.of(Student.class) + // The test can be fixed by uncommenting the line below: + //.generate(all(Grade.class), gen -> gen.enumOf(Grade.class).excluding(Grade.F)) + .create(); + + System.out.println(student); // same data generated on each run + + // When + boolean isEnrolled = enrollmentService.enrollStudent(student, course); + + // Then + assertThat(isEnrolled).isTrue(); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorScopesUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorScopesUnitTest.java new file mode 100644 index 0000000000..683b510f0c --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorScopesUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.instancio.selectors; + +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.EmergencyContact; +import com.baeldung.instancio.student.model.Phone; +import com.baeldung.instancio.student.model.Student; +import org.instancio.Instancio; +import org.instancio.Scope; +import org.instancio.Select; +import org.instancio.TargetSelector; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.allStrings; +import static org.instancio.Select.scope; + +/** + * Examples of various selector {@link Scope}. + * Scopes allow narrowing down selector targets. + */ +class SelectorScopesUnitTest { + + /** + * Prefix all String fields in Phone class with "phone_". + */ + @Test + void whenGivenClassScope_shouldSelectTargetsWithinClass() { + // Given + final String prefix = "phone_"; + final Scope phoneClass = scope(Phone.class); + + // When + Student student = Instancio.of(Student.class) + .generate(allStrings().within(phoneClass), gen -> gen.string().prefix(prefix)) + .create(); + + // Then + + // matches phone numbers + Phone emergencyContactPhone = student.getEmergencyContact().getPhone(); + assertThat(emergencyContactPhone.getCountryCode()).startsWith(prefix); + assertThat(emergencyContactPhone.getNumber()).startsWith(prefix); + assertThat(student.getContactInfo().getPhones()).allSatisfy(phone -> { + assertThat(phone.getCountryCode()).startsWith(prefix); + assertThat(phone.getNumber()).startsWith(prefix); + }); + + // does not match other fields + assertThat(student.getContactInfo().getAddress().getCity()).doesNotStartWith(prefix); + } + + /** + * Using scope to set student's and their emergency contact's + * phone number to different values. + */ + @Test + void whenGivenFieldScope_shouldSelectTargetsWithinField() { + // Given + TargetSelector studentPhone = Select.field(Phone::getNumber).within(scope(ContactInfo.class)); + TargetSelector emergencyPhone = Select.field(Phone::getNumber).within(scope(EmergencyContact.class)); + + // When + Student student = Instancio.of(Student.class) + .set(studentPhone, "student") + .set(emergencyPhone, "emergency") + .create(); + + // Then + assertThat(student.getContactInfo().getPhones()) + .isNotEmpty() + .allSatisfy(phone -> assertThat(phone.getNumber()).isEqualTo("student")); + + assertThat(student.getEmergencyContact().getPhone().getNumber()) + .isEqualTo("emergency"); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorsUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorsUnitTest.java new file mode 100644 index 0000000000..fe6b9cc3f4 --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/selectors/SelectorsUnitTest.java @@ -0,0 +1,104 @@ +package com.baeldung.instancio.selectors; + +import com.baeldung.instancio.student.model.Address; +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.Phone; +import com.baeldung.instancio.student.model.Student; +import org.instancio.FieldSelectorBuilder; +import org.instancio.Instancio; +import org.instancio.Select; +import org.instancio.Selector; +import org.instancio.SelectorGroup; +import org.instancio.TargetSelector; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.all; +import static org.instancio.Select.field; +import static org.instancio.Select.fields; +import static org.instancio.Select.types; + +/** + * Examples of various types of selectors provided by the {@link Select} class. + */ +class SelectorsUnitTest { + + @Test + void whenGivenFieldSelector_shouldCustomizeSelectedField() { + Address address = Instancio.of(Address.class) + .set(field(Address::getCity), "London") + .set(field(Address.class, "country"), "UK") + .create(); + + assertThat(address.getCity()).isEqualTo("London"); + assertThat(address.getCountry()).isEqualTo("UK"); + } + + @Test + void whenGivenClassSelector_shouldCustomizeSelectedClass() { + // Given + final Selector allStrings = all(String.class); + final String prefix = "test_"; + + // When + Address address = Instancio.of(Address.class) + .generate(allStrings, gen -> gen.string().prefix(prefix)) + .create(); + + // Then + assertThat(address.getCity()).startsWith(prefix); + assertThat(address.getCountry()).startsWith(prefix); + } + + @Test + void whenGivenPredicateFieldSelector_shouldCustomizeMatchingFields() { + // Given: regie matching 'city' and 'country' fields + final FieldSelectorBuilder fieldsMatchingRegex = fields().matching("c.*y"); + + // When + Address address = Instancio.of(Address.class) + .ignore(fieldsMatchingRegex) + .create(); + + // Then + assertThat(address.getCity()).isNull(); + assertThat(address.getCountry()).isNull(); + assertThat(address.getStreet()).isNotBlank(); + } + + @Test + void whenGivenPredicateClassSelector_shouldCustomizeMatchingClasses() { + // Given + final TargetSelector allTypesOfCollections = types().of(Collection.class); + final int size = 3; + + // When + ContactInfo contactInfo = Instancio.of(ContactInfo.class) + .generate(allTypesOfCollections, gen -> gen.collection().size(size)) + .create(); + + // Then + List phones = contactInfo.getPhones(); + assertThat(phones).hasSize(size); + } + + @Test + void whenGivenSelectorGroup_shouldCustomizeSelectedFields() { + // Given + SelectorGroup ignoredFields = all( + field(Student::getId), + field(Student::getDateOfBirth)); + + // When + Student student = Instancio.of(Student.class) + .ignore(ignoredFields) + .create(); + + // Then + assertThat(student.getId()).isNull(); + assertThat(student.getDateOfBirth()).isNull(); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/CustomSettingsUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/CustomSettingsUnitTest.java new file mode 100644 index 0000000000..b9b8b4ac6d --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/CustomSettingsUnitTest.java @@ -0,0 +1,67 @@ +package com.baeldung.instancio.settings; + +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.Phone; +import org.instancio.Instancio; +import org.instancio.junit.InstancioExtension; +import org.instancio.junit.WithSettings; +import org.instancio.settings.Keys; +import org.instancio.settings.Settings; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * {@link InstancioExtension} allows injecting custom settings + * using the {@link WithSettings} annotation. + */ +@ExtendWith(InstancioExtension.class) +class CustomSettingsUnitTest { + + private static final int MIN_SIZE = 0; + private static final int MAX_SIZE = 3; + + /** + * Common settings to be used by all test methods. + */ + @WithSettings + private static final Settings settings = Settings.create() + .set(Keys.COLLECTION_MIN_SIZE, MIN_SIZE) + .set(Keys.COLLECTION_MAX_SIZE, MAX_SIZE) + .lock(); + + + @Test + void whenGivenInjectedSettings_shouldUseCustomSettings() { + ContactInfo info = Instancio.create(ContactInfo.class); + + List phones = info.getPhones(); + assertThat(phones).hasSizeBetween(MIN_SIZE, MAX_SIZE); + } + + @Test + void whenSettingsOverridden_shouldUseTheOverrides() { + // Given + final int collectionSize = 50; + Settings additionalSettings = Settings.create() + .set(Keys.STRING_FIELD_PREFIX_ENABLED, true) + .set(Keys.COLLECTION_MIN_SIZE, collectionSize) + .set(Keys.COLLECTION_MAX_SIZE, collectionSize); + + // When + ContactInfo info = Instancio.of(ContactInfo.class) + .withSettings(additionalSettings) + .create(); + + // Then + assertThat(info.getPhones()) + .hasSize(collectionSize) + .allSatisfy(phone -> { + assertThat(phone.getCountryCode()).startsWith("countryCode_"); + assertThat(phone.getNumber()).startsWith("number_"); + }); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/UsingInstancioPropertiesUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/UsingInstancioPropertiesUnitTest.java new file mode 100644 index 0000000000..6a14d8dc88 --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/settings/UsingInstancioPropertiesUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.instancio.settings; + +import org.instancio.Instancio; +import org.instancio.settings.Keys; +import org.instancio.settings.Settings; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class UsingInstancioPropertiesUnitTest { + + /** + * Instancio automatically loads {@code instancio.properties} file, + * if it's present, from the root of the classpath. + * + *

Float range was overridden to [0, 100]. + * See: {@code src/test/resources/instancio.properties} + */ + @Test + void whenInstancioPropertiesAreOnClasspath_shouldUseConfiguredProperties() { + Set floats = Instancio.ofSet(Float.class).create(); + + assertThat(floats) + .isNotEmpty() + .allSatisfy(f -> assertThat(f).isBetween(0f, 100f)); + } + + /** + * We can override global configuration using {@link Settings}. + */ + @Test + void whenCustomSettingsAreProvided_shouldOverrideInstancioProperties() { + // Given + Settings settings = Settings.create() + .set(Keys.FLOAT_MIN, 100f) + .set(Keys.FLOAT_MAX, 200f); + + // When + Set floats = Instancio.ofSet(Float.class) + .withSettings(settings) + .create(); + + // Then + assertThat(floats) + .isNotEmpty() + .allSatisfy(f -> assertThat(f).isBetween(100f, 200f)); + } +} diff --git a/testing-modules/instancio/src/test/java/com/baeldung/instancio/subtype/SubtypeUnitTest.java b/testing-modules/instancio/src/test/java/com/baeldung/instancio/subtype/SubtypeUnitTest.java new file mode 100644 index 0000000000..7bbfaa497f --- /dev/null +++ b/testing-modules/instancio/src/test/java/com/baeldung/instancio/subtype/SubtypeUnitTest.java @@ -0,0 +1,54 @@ +package com.baeldung.instancio.subtype; + +import com.baeldung.instancio.abstracttype.AbstractItem; +import com.baeldung.instancio.generics.Item; +import com.baeldung.instancio.student.model.ContactInfo; +import com.baeldung.instancio.student.model.Student; +import org.instancio.Instancio; +import org.instancio.TypeToken; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.LinkedList; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.instancio.Select.all; +import static org.instancio.Select.field; + +/** + * Using {@code subtype()} method we can specify a specific implementation + * class for an abstract type, or a specialized subclass for a concrete class. + */ +class SubtypeUnitTest { + + @Test + void whenGivenCollectionSubtype_shouldUseSpecifiedCollectionClass() { + // Given + final Class subtype = LinkedList.class; + + // When + Student student = Instancio.of(Student.class) + .subtype(field(ContactInfo::getPhones), subtype) + .create(); + + // Then + assertThat(student.getContactInfo().getPhones()) + .isNotEmpty() + .isExactlyInstanceOf(subtype); + } + + @Test + void whenGivenSubtypeForGenericAbstractType_shouldUseSpecifiedConcreteClass() { + // Given + final Class subtype = Item.class; + + // When + AbstractItem abstractItem = Instancio.of(new TypeToken>() {}) + .subtype(all(AbstractItem.class), subtype) + .create(); + + // Then + assertThat(abstractItem).isExactlyInstanceOf(subtype); + assertThat(abstractItem.getValue()).isNotNull(); + } +} diff --git a/testing-modules/instancio/src/test/resources/instancio.properties b/testing-modules/instancio/src/test/resources/instancio.properties new file mode 100644 index 0000000000..a314af0a3f --- /dev/null +++ b/testing-modules/instancio/src/test/resources/instancio.properties @@ -0,0 +1,2 @@ +float.min=1 +float.max=100 diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml index f237d2d6fc..64546b5064 100644 --- a/testing-modules/pom.xml +++ b/testing-modules/pom.xml @@ -22,6 +22,7 @@ gatling groovy-spock hamcrest + instancio junit-4 junit-5-advanced junit-5-basics diff --git a/testing-modules/testing-libraries/pom.xml b/testing-modules/testing-libraries/pom.xml index f9443fa792..b7c9b8c0bd 100644 --- a/testing-modules/testing-libraries/pom.xml +++ b/testing-modules/testing-libraries/pom.xml @@ -90,6 +90,11 @@ + + org.codehaus.mojo + findbugs-maven-plugin + ${findbugs-plugin.version} + @@ -111,8 +116,9 @@ 4.8.0 3.0.0 1.19.0 - 1.0.0 + 1.2.1 2.4.3 + 3.0.5 \ No newline at end of file