From f24b9af096aa74ca2a6bc816400f90f33174ad6a Mon Sep 17 00:00:00 2001 From: Daniel Strmecki Date: Wed, 11 May 2022 09:57:02 +0200 Subject: [PATCH] Bael 5481 java httpclient post (#12118) * BAEL-5481: Create new module * BAEL-5481: Sync and async example * BAEL-5481: Concurrent example * BAEL-5481: Concurrent example * BAEL-5481: JSON body example * BAEL-5481: Form data example * BAEL-5481: File upload example * BAEL-5481: PR comments + Jenkins * BAEL-5481: Update aftifact ID * BAEL-5481: Spaces --- .../core-java-httpclient/README.md | 6 + .../core-java-httpclient/pom.xml | 58 +++++++ .../baeldung/httpclient/HttpClientPost.java | 162 ++++++++++++++++++ .../httpclient/HttpClientPostUnitTest.java | 99 +++++++++++ .../httpclient/PostRequestMockServer.java | 61 +++++++ pom.xml | 1 + 6 files changed, 387 insertions(+) create mode 100644 core-java-modules/core-java-httpclient/README.md create mode 100644 core-java-modules/core-java-httpclient/pom.xml create mode 100644 core-java-modules/core-java-httpclient/src/main/java/com/baeldung/httpclient/HttpClientPost.java create mode 100644 core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientPostUnitTest.java create mode 100644 core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/PostRequestMockServer.java diff --git a/core-java-modules/core-java-httpclient/README.md b/core-java-modules/core-java-httpclient/README.md new file mode 100644 index 0000000000..24ff7d9941 --- /dev/null +++ b/core-java-modules/core-java-httpclient/README.md @@ -0,0 +1,6 @@ +## Java HttpClient + +This module contains articles about Java HttpClient + +### Relevant articles +- TODO diff --git a/core-java-modules/core-java-httpclient/pom.xml b/core-java-modules/core-java-httpclient/pom.xml new file mode 100644 index 0000000000..57b23e96c1 --- /dev/null +++ b/core-java-modules/core-java-httpclient/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + core-java-httpclient + 0.1.0-SNAPSHOT + core-java-httpclient + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + ../../pom.xml + + + + + org.mock-server + mockserver-netty + ${mockserver.version} + + + org.mock-server + mockserver-client-java + ${mockserver.version} + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source.version} + ${maven.compiler.target.version} + + + + + + + 11 + 11 + 3.22.0 + 5.11.2 + + + \ No newline at end of file diff --git a/core-java-modules/core-java-httpclient/src/main/java/com/baeldung/httpclient/HttpClientPost.java b/core-java-modules/core-java-httpclient/src/main/java/com/baeldung/httpclient/HttpClientPost.java new file mode 100644 index 0000000000..d08a7bf183 --- /dev/null +++ b/core-java-modules/core-java-httpclient/src/main/java/com/baeldung/httpclient/HttpClientPost.java @@ -0,0 +1,162 @@ +package com.baeldung.httpclient; + +import java.io.IOException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class HttpClientPost { + + public static HttpResponse sendSynchronousPost(String serviceUrl) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + public static CompletableFuture> sendAsynchronousPost(String serviceUrl) { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + + CompletableFuture> futureResponse = client + .sendAsync(request, HttpResponse.BodyHandlers.ofString()); + + return futureResponse; + } + + public static List>> sendConcurrentPost(List serviceUrls) { + HttpClient client = HttpClient.newHttpClient(); + + List>> completableFutures = serviceUrls.stream() + .map(URI::create) + .map(HttpRequest::newBuilder) + .map(builder -> builder.POST(HttpRequest.BodyPublishers.noBody())) + .map(HttpRequest.Builder::build) + .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString())) + .collect(Collectors.toList()); + + return completableFutures; + } + + public static HttpResponse sendPostWithAuthHeader(String serviceUrl) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.noBody()) + .header("Authorization", "Basic " + Base64.getEncoder() + .encodeToString(("baeldung:123456").getBytes())) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + public static HttpResponse sendPostWithAuthClient(String serviceUrl) throws IOException, InterruptedException { + HttpClient client = HttpClient.newBuilder() + .authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication( + "baeldung", + "123456".toCharArray()); + } + }) + .build(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + public static HttpResponse sendPostWithJsonBody(String serviceUrl) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.ofString("{\"action\":\"hello\"}")) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + public static HttpResponse sendPostWithFormData(String serviceUrl) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + Map formData = new HashMap<>(); + formData.put("username", "baeldung"); + formData.put("message", "hello"); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData))) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + public static HttpResponse sendPostWithFileData(String serviceUrl, Path file) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(serviceUrl)) + .POST(HttpRequest.BodyPublishers.ofFile(file)) + .build(); + + HttpResponse response = client + .send(request, HttpResponse.BodyHandlers.ofString()); + + return response; + } + + private static String getFormDataAsString(Map formData) { + StringBuilder formBodyBuilder = new StringBuilder(); + for (Map.Entry singleEntry : formData.entrySet()) { + if (formBodyBuilder.length() > 0) { + formBodyBuilder.append("&"); + } + formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8)); + formBodyBuilder.append("="); + formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8)); + } + return formBodyBuilder.toString(); + } + +} diff --git a/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientPostUnitTest.java b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientPostUnitTest.java new file mode 100644 index 0000000000..b43cf08649 --- /dev/null +++ b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/HttpClientPostUnitTest.java @@ -0,0 +1,99 @@ +package com.baeldung.httpclient; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.*; + +class HttpClientPostUnitTest extends PostRequestMockServer { + + @Test + void givenSyncPostRequest_whenServerIsAvailable_thenOkStatusIsReceived() throws IOException, InterruptedException { + HttpResponse response = HttpClientPost.sendSynchronousPost(serviceUrl); + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenAsyncPostRequest_whenServerIsAvailable_thenOkStatusIsReceived() throws ExecutionException, InterruptedException { + CompletableFuture> futureResponse = HttpClientPost.sendAsynchronousPost(serviceUrl); + HttpResponse response = futureResponse.get(); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenConcurrentPostRequests_whenServerIsAvailable_thenOkStatusIsReceived() throws ExecutionException, InterruptedException { + List>> completableFutures = HttpClientPost + .sendConcurrentPost(List.of(serviceUrl, serviceUrl)); + + CompletableFuture>> combinedFutures = CompletableFuture + .allOf(completableFutures.toArray(new CompletableFuture[0])) + .thenApply(future -> + completableFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList())); + + List> responses = combinedFutures.get(); + responses.forEach((response) -> { + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + }); + } + + @Test + void givenPostRequestWithAuthClient_whenServerIsAvailable_thenOkStatusIsReceived() throws IOException, InterruptedException { + HttpResponse response = HttpClientPost.sendPostWithAuthClient(serviceUrl); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenPostRequestWithAuthHeader_whenServerIsAvailable_thenOkStatusIsReceived() throws IOException, InterruptedException { + HttpResponse response = HttpClientPost.sendPostWithAuthHeader(serviceUrl); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenPostRequestWithJsonBody_whenServerIsAvailable_thenOkStatusIsReceived() throws IOException, InterruptedException { + HttpResponse response = HttpClientPost.sendPostWithJsonBody(serviceUrl); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenPostRequestWithFormData_whenServerIsAvailable_thenOkStatusIsReceived() throws IOException, InterruptedException { + HttpResponse response = HttpClientPost.sendPostWithFormData(serviceUrl); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + + @Test + void givenPostRequestWithFileData_whenServerIsAvailable_thenOkStatusIsReceived(@TempDir Path tempDir) throws IOException, InterruptedException { + Path file = tempDir.resolve("temp.txt"); + List lines = Arrays.asList("1", "2", "3"); + Files.write(file, lines); + + HttpResponse response = HttpClientPost.sendPostWithFileData(serviceUrl, file); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}"); + } + +} diff --git a/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/PostRequestMockServer.java b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/PostRequestMockServer.java new file mode 100644 index 0000000000..fa594897a3 --- /dev/null +++ b/core-java-modules/core-java-httpclient/src/test/java/com/baeldung/httpclient/PostRequestMockServer.java @@ -0,0 +1,61 @@ +package com.baeldung.httpclient; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockserver.client.MockServerClient; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpStatusCode; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URISyntaxException; + +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public abstract class PostRequestMockServer { + + public static ClientAndServer mockServer; + public static String serviceUrl; + + private static int serverPort; + + public static final String SERVER_ADDRESS = "127.0.0.1"; + public static final String PATH = "/test1"; + public static final String METHOD = "POST"; + + @BeforeAll + static void startServer() throws IOException, URISyntaxException { + serverPort = getFreePort(); + serviceUrl = "http://" + SERVER_ADDRESS + ":" + serverPort + PATH; + mockServer = startClientAndServer(serverPort); + mockBasicPostRequest(); + } + + @AfterAll + static void stopServer() { + mockServer.stop(); + } + + private static void mockBasicPostRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH) + .withMethod(METHOD) + ) + .respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withBody("{\"message\":\"ok\"}") + ); + } + + private static int getFreePort () throws IOException { + try (ServerSocket serverSocket = new ServerSocket(0)) { + return serverSocket.getLocalPort(); + } + } + +} diff --git a/pom.xml b/pom.xml index f06c75b3be..5b64baac7f 100644 --- a/pom.xml +++ b/pom.xml @@ -1318,6 +1318,7 @@ core-java-modules/core-java-networking-3 core-java-modules/multimodulemavenproject core-java-modules/core-java-strings + core-java-modules/core-java-httpclient ddd-modules docker apache-httpclient-2