From d3a5d5c8684720afec71950c7b773143a64f5c20 Mon Sep 17 00:00:00 2001 From: Sashir Estela Date: Fri, 10 Jan 2025 01:10:35 +0000 Subject: [PATCH] Refactor to use different Http clients --- .../cleverclient/CleverClient.java | 19 +- .../client/HttpClientAdapter.java | 107 +++++++++ .../client/JavaHttpClientAdapter.java | 223 ++++++++++++++++++ .../cleverclient/client/RequestData.java | 39 +++ .../cleverclient/client/ResponseData.java | 31 +++ .../cleverclient/http/HttpConnector.java | 121 ---------- .../cleverclient/http/HttpProcessor.java | 13 +- .../sender/HttpAsyncBinarySender.java | 27 --- .../sender/HttpAsyncCustomSender.java | 27 --- .../sender/HttpAsyncGenericSender.java | 28 --- .../sender/HttpAsyncListSender.java | 27 --- .../sender/HttpAsyncPlainTextSender.java | 26 -- .../sender/HttpAsyncStreamEventSender.java | 43 ---- .../sender/HttpAsyncStreamSender.java | 37 --- .../cleverclient/sender/HttpSender.java | 82 ------- .../sender/HttpSenderFactory.java | 66 ------ .../sender/HttpSyncBinarySender.java | 34 --- .../sender/HttpSyncCustomSender.java | 34 --- .../sender/HttpSyncGenericSender.java | 35 --- .../sender/HttpSyncListSender.java | 34 --- .../sender/HttpSyncPlainTextSender.java | 33 --- .../sender/HttpSyncStreamEventSender.java | 48 ---- .../sender/HttpSyncStreamSender.java | 42 ---- .../cleverclient/support/ReturnType.java | 11 +- .../cleverclient/CleverClientTest.java | 10 +- .../cleverclient/http/HttpProcessorTest.java | 18 +- 26 files changed, 443 insertions(+), 772 deletions(-) create mode 100644 src/main/java/io/github/sashirestela/cleverclient/client/HttpClientAdapter.java create mode 100644 src/main/java/io/github/sashirestela/cleverclient/client/JavaHttpClientAdapter.java create mode 100644 src/main/java/io/github/sashirestela/cleverclient/client/RequestData.java create mode 100644 src/main/java/io/github/sashirestela/cleverclient/client/ResponseData.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/http/HttpConnector.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncBinarySender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncCustomSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncGenericSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncListSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncPlainTextSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamEventSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSenderFactory.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncBinarySender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncCustomSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncGenericSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncListSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncPlainTextSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamEventSender.java delete mode 100644 src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamSender.java diff --git a/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java b/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java index 7386337..71841aa 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java +++ b/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java @@ -1,6 +1,8 @@ package io.github.sashirestela.cleverclient; import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.sashirestela.cleverclient.client.HttpClientAdapter; +import io.github.sashirestela.cleverclient.client.JavaHttpClientAdapter; import io.github.sashirestela.cleverclient.http.HttpProcessor; import io.github.sashirestela.cleverclient.http.HttpRequestData; import io.github.sashirestela.cleverclient.support.Configurator; @@ -12,7 +14,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.http.HttpClient; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -21,8 +22,8 @@ import java.util.function.UnaryOperator; /** - * Main class and entry point to use this library. This is a kind of wrapper that makes it easier to - * use the Java's HttpClient component to call http services by using annotated interfaces. + * Main class and entry point to use this library. This is a smart wrapper that makes it easier to + * use a Http client component to call http services by using annotated interfaces. */ @Getter public class CleverClient { @@ -31,7 +32,7 @@ public class CleverClient { private final String baseUrl; private final Map headers; - private final HttpClient httpClient; + private final HttpClientAdapter clientAdapter; private final UnaryOperator requestInterceptor; private final Consumer bodyInspector; private final HttpProcessor httpProcessor; @@ -41,8 +42,8 @@ public class CleverClient { * * @param baseUrl Root of the url of the API service to call. Mandatory. * @param headers Http headers for all the API service. Optional. - * @param httpClient Custom Java's HttpClient component. One is created by default if none - * is passed. Optional. + * @param clientAdapter Component to call http services. If none is passed the + * JavaHttpClientAdapter will be used. Optional. * @param requestInterceptor Function to modify the request once it has been built. * @param bodyInspector Function to inspect the Body request parameter. * @param endsOfStream Texts used to mark the final of streams when handling server sent @@ -50,18 +51,18 @@ public class CleverClient { * @param objectMapper Provides Json conversions either to and from objects. Optional. */ @Builder - public CleverClient(@NonNull String baseUrl, @Singular Map headers, HttpClient httpClient, + public CleverClient(@NonNull String baseUrl, @Singular Map headers, HttpClientAdapter clientAdapter, UnaryOperator requestInterceptor, Consumer bodyInspector, @Singular("endOfStream") List endsOfStream, ObjectMapper objectMapper) { this.baseUrl = baseUrl; this.headers = Optional.ofNullable(headers).orElse(Map.of()); - this.httpClient = Optional.ofNullable(httpClient).orElse(HttpClient.newHttpClient()); + this.clientAdapter = Optional.ofNullable(clientAdapter).orElse(new JavaHttpClientAdapter()); this.requestInterceptor = requestInterceptor; this.bodyInspector = bodyInspector; this.httpProcessor = HttpProcessor.builder() .baseUrl(this.baseUrl) .headers(CommonUtil.mapToListOfString(this.headers)) - .httpClient(this.httpClient) + .clientAdapter(this.clientAdapter) .requestInterceptor(this.requestInterceptor) .bodyInspector(bodyInspector) .build(); diff --git a/src/main/java/io/github/sashirestela/cleverclient/client/HttpClientAdapter.java b/src/main/java/io/github/sashirestela/cleverclient/client/HttpClientAdapter.java new file mode 100644 index 0000000..ac5e107 --- /dev/null +++ b/src/main/java/io/github/sashirestela/cleverclient/client/HttpClientAdapter.java @@ -0,0 +1,107 @@ +package io.github.sashirestela.cleverclient.client; + +import io.github.sashirestela.cleverclient.ResponseInfo; +import io.github.sashirestela.cleverclient.ResponseInfo.RequestInfo; +import io.github.sashirestela.cleverclient.http.HttpRequestData; +import io.github.sashirestela.cleverclient.support.CleverClientException; +import io.github.sashirestela.cleverclient.util.CommonUtil; +import io.github.sashirestela.cleverclient.util.Constant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.function.UnaryOperator; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class HttpClientAdapter { + + private static Logger logger = LoggerFactory.getLogger(HttpClientAdapter.class); + + public Object sendRequest(RequestData originalRequest, UnaryOperator requestInterceptor) { + var actualRequest = interceptRequest(originalRequest, requestInterceptor); + logger.debug("Http Call : {} {}", actualRequest.getHttpMethod(), actualRequest.getUrl()); + var formattedHeaders = formattedHeaders(actualRequest.getHeaders()); + logger.debug("Request Headers : {}", formattedHeaders); + if (actualRequest.getReturnType().isAsync()) { + return sendAsync(actualRequest); + } else { + return send(actualRequest); + } + } + + protected abstract Object sendAsync(RequestData request); + + protected abstract Object send(RequestData request); + + private RequestData interceptRequest(RequestData originalRequest, + UnaryOperator requestInterceptor) { + if (requestInterceptor != null) { + var httpRequestData = originalRequest.getHttpRequestData(); + httpRequestData = requestInterceptor.apply(httpRequestData); + return originalRequest + .withUrl(httpRequestData.getUrl()) + .withBody(httpRequestData.getBody()) + .withHeaders(CommonUtil.mapToListOfString(httpRequestData.getHeaders())); + } else { + return originalRequest; + } + } + + @SuppressWarnings("unchecked") + protected void throwExceptionIfErrorIsPresent(ResponseData response) { + if (!CommonUtil.isInHundredsOf(response.getStatusCode(), Constant.HTTP_SUCCESSFUL)) { + String data = ""; + if (response.getBody() instanceof Stream) { + data = ((Stream) response.getBody()) + .collect(Collectors.joining(System.getProperty("line.separator"))); + } else if (response.getBody() instanceof InputStream) { + try { + data = new String(((InputStream) response.getBody()).readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new CleverClientException(e); + } + } else { + data = (String) response.getBody(); + } + logger.error("Response : {}", data); + throw new CleverClientException(fillResponseInfo(response, data)); + } + } + + private ResponseInfo fillResponseInfo(ResponseData response, String data) { + var request = response.getRequest(); + return ResponseInfo.builder() + .statusCode(response.getStatusCode()) + .data(data) + .headers(response.getHeaders()) + .request(request != null ? RequestInfo.builder() + .httpMethod(request.getHttpMethod()) + .url(request.getUrl()) + .headers(request.getHeaders()) + .build() : null) + .build(); + } + + private String formattedHeaders(List headers) { + final String RESERVED_REGEX = "(authorization|api.?key)"; + var pattern = Pattern.compile(RESERVED_REGEX, Pattern.CASE_INSENSITIVE); + var print = new StringBuilder("{"); + for (var i = 0; i < headers.size(); i += 2) { + if (i > 1) { + print.append(", "); + } + var headerKey = headers.get(i); + var matcher = pattern.matcher(headerKey); + var headerVal = matcher.find() ? "*".repeat(10) : headers.get(i + 1); + print.append(headerKey + " = " + headerVal); + } + print.append("}"); + return print.toString(); + } + +} diff --git a/src/main/java/io/github/sashirestela/cleverclient/client/JavaHttpClientAdapter.java b/src/main/java/io/github/sashirestela/cleverclient/client/JavaHttpClientAdapter.java new file mode 100644 index 0000000..9c0a06c --- /dev/null +++ b/src/main/java/io/github/sashirestela/cleverclient/client/JavaHttpClientAdapter.java @@ -0,0 +1,223 @@ +package io.github.sashirestela.cleverclient.client; + +import io.github.sashirestela.cleverclient.Event; +import io.github.sashirestela.cleverclient.support.CleverClientException; +import io.github.sashirestela.cleverclient.support.CleverClientSSE; +import io.github.sashirestela.cleverclient.support.ContentType; +import io.github.sashirestela.cleverclient.support.HttpMultipart; +import io.github.sashirestela.cleverclient.support.ReturnType; +import io.github.sashirestela.cleverclient.support.ReturnType.Category; +import io.github.sashirestela.cleverclient.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.EnumMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class JavaHttpClientAdapter extends HttpClientAdapter { + + private static Logger logger = LoggerFactory.getLogger(JavaHttpClientAdapter.class); + + private HttpClient httpClient; + private EnumMap functionsByCategoryMap; + + public JavaHttpClientAdapter(HttpClient httpClient) { + this.httpClient = httpClient; + fillFunctionsByCategory(); + } + + public JavaHttpClientAdapter() { + this(HttpClient.newHttpClient()); + } + + @Override + protected Object send(RequestData request) { + var returnType = request.getReturnType(); + var functions = getFunctions(returnType); + var httpRequest = convertToHttpRequest(request); + try { + var httpResponse = httpClient.send(httpRequest, functions.bodyHandler.get()); + logger.debug("Response Code : {}", httpResponse.statusCode()); + throwExceptionIfErrorIsPresent(convertToResponseData(httpResponse)); + var response = httpResponse.body(); + if (!returnType.isStream()) { + logger.debug("Response : {}", response); + } + return functions.responseConverter.apply(response, returnType); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new CleverClientException(e); + } + } + + @Override + protected Object sendAsync(RequestData request) { + var returnType = request.getReturnType(); + var functions = getFunctions(returnType); + var httpRequest = convertToHttpRequest(request); + var httpResponseFuture = httpClient.sendAsync(httpRequest, functions.bodyHandler.get()); + return httpResponseFuture.thenApply(httpResponse -> { + logger.debug("Response Code : {}", httpResponse.statusCode()); + throwExceptionIfErrorIsPresent(convertToResponseData(httpResponse)); + var response = httpResponse.body(); + if (!returnType.isStream()) { + logger.debug("Response : {}", response); + } + return functions.responseConverter.apply(response, returnType); + }); + } + + private FunctionsByCategory getFunctions(ReturnType returnType) { + var functions = functionsByCategoryMap.get(returnType.category()); + if (functions == null) { + throw new CleverClientException("Unsupported return type {0}.", returnType.getFullClassName(), null); + } + return functions; + } + + private HttpRequest convertToHttpRequest(RequestData request) { + var bodyPublisher = createBodyPublisher(request.getBody(), request.getContentType()); + var headersArray = request.getHeaders().toArray(new String[0]); + var httpRequestBuilder = HttpRequest.newBuilder() + .uri(URI.create(request.getUrl())) + .method(request.getHttpMethod(), bodyPublisher); + if (headersArray.length > 0) { + httpRequestBuilder.headers(headersArray); + } + return httpRequestBuilder.build(); + } + + @SuppressWarnings("unchecked") + private BodyPublisher createBodyPublisher(Object bodyObject, ContentType contentType) { + BodyPublisher bodyPublisher = null; + if (contentType == null) { + logger.debug("Request Body : (Empty)"); + bodyPublisher = BodyPublishers.noBody(); + } else if (contentType == ContentType.MULTIPART_FORMDATA) { + logger.debug("Request Body : {}", bodyObject); + var bodyBytes = HttpMultipart.toByteArrays((Map) bodyObject); + bodyPublisher = BodyPublishers.ofByteArrays(bodyBytes); + } else if (contentType == ContentType.APPLICATION_JSON) { + logger.debug("Request Body : {}", bodyObject); + bodyPublisher = BodyPublishers.ofString((String) bodyObject); + } + return bodyPublisher; + } + + private ResponseData convertToResponseData(HttpResponse httpResponse) { + var httpRequest = httpResponse.request(); + return ResponseData.builder() + .statusCode(httpResponse.statusCode()) + .body(httpResponse.body()) + .headers(httpResponse.headers().map()) + .request(httpRequest != null ? ResponseData.Request.builder() + .httpMethod(httpRequest.method()) + .url(httpRequest.uri().toString()) + .headers(httpRequest.headers().map()) + .build() : null) + .build(); + } + + private class FunctionsByCategory { + + Supplier> bodyHandler; + BiFunction responseConverter; + + public FunctionsByCategory(Supplier> bodyHandler, + BiFunction responseConverter) { + this.bodyHandler = bodyHandler; + this.responseConverter = responseConverter; + } + + } + + @SuppressWarnings("unchecked") + private void fillFunctionsByCategory() { + this.functionsByCategoryMap = new EnumMap<>(Category.class); + functionsByCategoryMap.put(Category.SYNC_BINARY, new FunctionsByCategory( + () -> BodyHandlers.ofInputStream(), + (r, t) -> r)); + functionsByCategoryMap.put(Category.SYNC_PLAIN_TEXT, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> r)); + functionsByCategoryMap.put(Category.SYNC_CUSTOM, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToObject((String) r, t.getBaseClass()))); + functionsByCategoryMap.put(Category.SYNC_GENERIC, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToParametricObject((String) r, t.getGenericClassIfExists(), t.getBaseClass()))); + functionsByCategoryMap.put(Category.SYNC_LIST, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToList((String) r, t.getBaseClass()))); + functionsByCategoryMap.put(Category.SYNC_STREAM, new FunctionsByCategory( + () -> BodyHandlers.ofLines(), + (r, t) -> convertToStreamOfObjects((Stream) r, t))); + functionsByCategoryMap.put(Category.SYNC_STREAM_EVENT, new FunctionsByCategory( + () -> BodyHandlers.ofLines(), + (r, t) -> convertToStreamOfEvents((Stream) r, t))); + functionsByCategoryMap.put(Category.ASYNC_BINARY, new FunctionsByCategory( + () -> BodyHandlers.ofInputStream(), + (r, t) -> r)); + functionsByCategoryMap.put(Category.ASYNC_PLAIN_TEXT, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> r)); + functionsByCategoryMap.put(Category.ASYNC_CUSTOM, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToObject((String) r, t.getBaseClass()))); + functionsByCategoryMap.put(Category.ASYNC_GENERIC, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToParametricObject((String) r, t.getGenericClassIfExists(), t.getBaseClass()))); + functionsByCategoryMap.put(Category.ASYNC_LIST, new FunctionsByCategory( + () -> BodyHandlers.ofString(), + (r, t) -> JsonUtil.jsonToList((String) r, t.getBaseClass()))); + functionsByCategoryMap.put(Category.ASYNC_STREAM, new FunctionsByCategory( + () -> BodyHandlers.ofLines(), + (r, t) -> convertToStreamOfObjects((Stream) r, t))); + functionsByCategoryMap.put(Category.ASYNC_STREAM_EVENT, new FunctionsByCategory( + () -> BodyHandlers.ofLines(), + (r, t) -> convertToStreamOfEvents((Stream) r, t))); + } + + private Stream convertToStreamOfObjects(Stream response, ReturnType returnType) { + final var lineRecord = new CleverClientSSE.LineRecord(); + return response + .map(line -> { + logger.debug("Response : {}", line); + lineRecord.updateWith(line); + return new CleverClientSSE(lineRecord); + }) + .filter(CleverClientSSE::isActualData) + .map(item -> JsonUtil.jsonToObject(item.getActualData(), returnType.getBaseClass())); + } + + private Stream convertToStreamOfEvents(Stream response, ReturnType returnType) { + final var lineRecord = new CleverClientSSE.LineRecord(); + final var events = returnType.getClassByEvent().keySet(); + + return response + .map(line -> { + logger.debug("Response : {}", line); + lineRecord.updateWith(line); + return new CleverClientSSE(lineRecord, events); + }) + .filter(CleverClientSSE::isActualData) + .map(item -> Event.builder() + .name(item.getMatchedEvent()) + .data(JsonUtil.jsonToObject(item.getActualData(), + returnType.getClassByEvent().get(item.getMatchedEvent()))) + .build()); + } + +} diff --git a/src/main/java/io/github/sashirestela/cleverclient/client/RequestData.java b/src/main/java/io/github/sashirestela/cleverclient/client/RequestData.java new file mode 100644 index 0000000..e8204c9 --- /dev/null +++ b/src/main/java/io/github/sashirestela/cleverclient/client/RequestData.java @@ -0,0 +1,39 @@ +package io.github.sashirestela.cleverclient.client; + +import io.github.sashirestela.cleverclient.http.HttpRequestData; +import io.github.sashirestela.cleverclient.support.ContentType; +import io.github.sashirestela.cleverclient.support.ReturnType; +import io.github.sashirestela.cleverclient.util.CommonUtil; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.With; + +import java.util.List; + +@AllArgsConstructor +@Builder +@Getter +public class RequestData { + + @With + private final String url; + @With + private final Object body; + @With + private final List headers; + private final String httpMethod; + private final ContentType contentType; + private final ReturnType returnType; + + public HttpRequestData getHttpRequestData() { + return HttpRequestData.builder() + .url(url) + .body(body) + .headers(CommonUtil.listToMapOfString(headers)) + .contentType(contentType) + .httpMethod(httpMethod) + .build(); + } + +} diff --git a/src/main/java/io/github/sashirestela/cleverclient/client/ResponseData.java b/src/main/java/io/github/sashirestela/cleverclient/client/ResponseData.java new file mode 100644 index 0000000..44fe81f --- /dev/null +++ b/src/main/java/io/github/sashirestela/cleverclient/client/ResponseData.java @@ -0,0 +1,31 @@ +package io.github.sashirestela.cleverclient.client; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; +import java.util.Map; + +@AllArgsConstructor +@Builder +@Getter +public class ResponseData { + + private final int statusCode; + private final Object body; + private final Map> headers; + private final Request request; + + @AllArgsConstructor + @Builder + @Getter + static class Request { + + private final String httpMethod; + private final String url; + private final Map> headers; + + } + +} diff --git a/src/main/java/io/github/sashirestela/cleverclient/http/HttpConnector.java b/src/main/java/io/github/sashirestela/cleverclient/http/HttpConnector.java deleted file mode 100644 index fb7f5b8..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/http/HttpConnector.java +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.sashirestela.cleverclient.http; - -import io.github.sashirestela.cleverclient.sender.HttpSenderFactory; -import io.github.sashirestela.cleverclient.support.ContentType; -import io.github.sashirestela.cleverclient.support.HttpMultipart; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.CommonUtil; -import lombok.AllArgsConstructor; -import lombok.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.BodyPublisher; -import java.net.http.HttpRequest.BodyPublishers; -import java.util.List; -import java.util.Map; -import java.util.function.UnaryOperator; - -/** - * HttpConnector prepares the request and receives the response to/from the Java's HttpClient - * component. - */ -@AllArgsConstructor -@Builder -public class HttpConnector { - - private static final Logger logger = LoggerFactory.getLogger(HttpConnector.class); - - private HttpClient httpClient; - private String url; - private String httpMethod; - private ReturnType returnType; - private Object bodyObject; - private ContentType contentType; - private List headers; - private UnaryOperator requestInterceptor; - - /** - * Prepares the request to call Java's HttpClient and delegates it to a specialized HttpSender based - * on the method's return type. - * - * @return The response coming from the HttpSender's sendRequest method. - */ - public Object sendRequest() { - if (requestInterceptor != null) { - interceptRequest(); - } - var formattedHeaders = printHeaders(headers); - logger.debug("Http Call : {} {}", httpMethod, url); - logger.debug("Request Headers : {}", formattedHeaders); - - var bodyPublisher = createBodyPublisher(bodyObject, contentType); - var headersArray = headers.toArray(new String[0]); - HttpRequest httpRequest = null; - if (headersArray.length > 0) { - httpRequest = HttpRequest.newBuilder() - .uri(URI.create(url)) - .headers(headersArray) - .method(httpMethod, bodyPublisher) - .build(); - } else { - httpRequest = HttpRequest.newBuilder() - .uri(URI.create(url)) - .method(httpMethod, bodyPublisher) - .build(); - } - var httpSender = HttpSenderFactory.get().createSender(returnType); - return httpSender.sendRequest(httpClient, httpRequest, returnType); - } - - private void interceptRequest() { - var httpRequestData = HttpRequestData.builder() - .url(url) - .body(bodyObject) - .headers(CommonUtil.listToMapOfString(headers)) - .httpMethod(httpMethod) - .contentType(contentType) - .build(); - - httpRequestData = requestInterceptor.apply(httpRequestData); - - url = httpRequestData.getUrl(); - bodyObject = httpRequestData.getBody(); - headers = CommonUtil.mapToListOfString(httpRequestData.getHeaders()); - } - - @SuppressWarnings("unchecked") - private BodyPublisher createBodyPublisher(Object bodyObject, ContentType contentType) { - BodyPublisher bodyPublisher = null; - if (contentType == null) { - logger.debug("Request Body : (Empty)"); - bodyPublisher = BodyPublishers.noBody(); - } else if (contentType == ContentType.MULTIPART_FORMDATA) { - logger.debug("Request Body : {}", bodyObject); - var bodyBytes = HttpMultipart.toByteArrays((Map) bodyObject); - bodyPublisher = BodyPublishers.ofByteArrays(bodyBytes); - } else if (contentType == ContentType.APPLICATION_JSON) { - logger.debug("Request Body : {}", bodyObject); - bodyPublisher = BodyPublishers.ofString((String) bodyObject); - } - return bodyPublisher; - } - - private String printHeaders(List headers) { - var print = new StringBuilder("{"); - for (var i = 0; i < headers.size(); i += 2) { - if (i > 1) { - print.append(", "); - } - var headerKey = headers.get(i); - var headerVal = headerKey.equals("Authorization") ? "*".repeat(10) : headers.get(i + 1); - print.append(headerKey + " = " + headerVal); - } - print.append("}"); - return print.toString(); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java b/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java index a700203..dfded74 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java +++ b/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java @@ -1,5 +1,7 @@ package io.github.sashirestela.cleverclient.http; +import io.github.sashirestela.cleverclient.client.HttpClientAdapter; +import io.github.sashirestela.cleverclient.client.RequestData; import io.github.sashirestela.cleverclient.metadata.InterfaceMetadata.MethodMetadata; import io.github.sashirestela.cleverclient.metadata.InterfaceMetadataStore; import io.github.sashirestela.cleverclient.support.ContentType; @@ -13,7 +15,6 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.net.http.HttpClient; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -29,7 +30,7 @@ public class HttpProcessor implements InvocationHandler { private final String baseUrl; private final List headers; - private final HttpClient httpClient; + private final HttpClientAdapter clientAdapter; private final UnaryOperator requestInterceptor; private final Consumer bodyInspector; @@ -107,17 +108,15 @@ private Object resolve(Method method, Object[] arguments) { var fullHeaders = new ArrayList<>(this.headers); fullHeaders.addAll(calculateHeaderContentType(contentType)); fullHeaders.addAll(interfaceMetadata.getFullHeadersByMethod(methodMetadata)); - var httpConnector = HttpConnector.builder() - .httpClient(httpClient) + var request = RequestData.builder() .url(url) .httpMethod(httpMethod) .returnType(returnType) - .bodyObject(bodyObject) + .body(bodyObject) .contentType(contentType) .headers(fullHeaders) - .requestInterceptor(requestInterceptor) .build(); - return httpConnector.sendRequest(); + return clientAdapter.sendRequest(request, requestInterceptor); } private Object getAndInspectBody(MethodMetadata methodMetadata, Object[] arguments) { diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncBinarySender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncBinarySender.java deleted file mode 100644 index 1e199c1..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncBinarySender.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.ReturnType; - -import java.io.InputStream; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpAsyncBinarySender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofInputStream()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, InputStream.class); - - logger.debug("Response : {}", response.body()); - - return response.body(); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncCustomSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncCustomSender.java deleted file mode 100644 index 7e46415..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncCustomSender.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpAsyncCustomSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofString()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, null); - - logger.debug("Response : {}", response.body()); - - return JsonUtil.jsonToObject(response.body(), returnType.getBaseClass()); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncGenericSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncGenericSender.java deleted file mode 100644 index c8eb13a..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncGenericSender.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpAsyncGenericSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofString()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, null); - - logger.debug("Response : {}", response.body()); - - return JsonUtil.jsonToParametricObject(response.body(), returnType.getGenericClassIfExists(), - returnType.getBaseClass()); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncListSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncListSender.java deleted file mode 100644 index f3ddccd..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncListSender.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpAsyncListSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofString()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, null); - - logger.debug("Response : {}", response.body()); - - return JsonUtil.jsonToList(response.body(), returnType.getBaseClass()); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncPlainTextSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncPlainTextSender.java deleted file mode 100644 index 7d68c4d..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncPlainTextSender.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.ReturnType; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpAsyncPlainTextSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofString()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, null); - - logger.debug("Response : {}", response.body()); - - return response.body(); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamEventSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamEventSender.java deleted file mode 100644 index 1430755..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamEventSender.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.Event; -import io.github.sashirestela.cleverclient.support.CleverClientSSE; -import io.github.sashirestela.cleverclient.support.CleverClientSSE.LineRecord; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.stream.Stream; - -public class HttpAsyncStreamEventSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofLines()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, Stream.class); - - final var lineRecord = new LineRecord(); - final var events = returnType.getClassByEvent().keySet(); - - return response.body() - .map(line -> { - logger.debug("Response : {}", line); - lineRecord.updateWith(line); - return new CleverClientSSE(lineRecord, events); - }) - .filter(CleverClientSSE::isActualData) - .map(item -> Event.builder() - .name(item.getMatchedEvent()) - .data(JsonUtil.jsonToObject(item.getActualData(), - returnType.getClassByEvent().get(item.getMatchedEvent()))) - .build()); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamSender.java deleted file mode 100644 index f565ca7..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpAsyncStreamSender.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientSSE; -import io.github.sashirestela.cleverclient.support.CleverClientSSE.LineRecord; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.stream.Stream; - -public class HttpAsyncStreamSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - - var httpResponseFuture = httpClient.sendAsync(httpRequest, BodyHandlers.ofLines()); - - return httpResponseFuture.thenApply(response -> { - - throwExceptionIfErrorIsPresent(response, Stream.class); - - final var lineRecord = new LineRecord(); - - return response.body() - .map(line -> { - logger.debug("Response : {}", line); - lineRecord.updateWith(line); - return new CleverClientSSE(lineRecord); - }) - .filter(CleverClientSSE::isActualData) - .map(item -> JsonUtil.jsonToObject(item.getActualData(), returnType.getBaseClass())); - }); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSender.java deleted file mode 100644 index 036ef60..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSender.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.ResponseInfo; -import io.github.sashirestela.cleverclient.ResponseInfo.RequestInfo; -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.CommonUtil; -import io.github.sashirestela.cleverclient.util.Constant; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * HttpSender is an abstract class for a set of concrete classes that implement different - * interactions with the Java's HttpClient based on the method's return type. - */ -public abstract class HttpSender { - - protected static Logger logger = LoggerFactory.getLogger(HttpSender.class); - - /** - * Method to be implementd for concrete classes to send request to the Java's HttpClient and receive - * response. - * - * @param httpClient Java's HttpClient component. - * @param httpRequest Java's HttpRequest component. - * @param returnType Response class and generic class if exists. - * @return Response coming from Java's HttpClient. - */ - public abstract Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType); - - /** - * Exception handling that will be called by any concrete class. - * - * @param response Java's HttpResponse component. - * @param clazz Response class. - */ - @SuppressWarnings("unchecked") - protected void throwExceptionIfErrorIsPresent(HttpResponse response, Class clazz) { - logger.debug("Response Code : {}", response.statusCode()); - if (!CommonUtil.isInHundredsOf(response.statusCode(), Constant.HTTP_SUCCESSFUL)) { - var data = ""; - if (Stream.class.equals(clazz)) { - data = ((Stream) response.body()) - .collect(Collectors.joining(System.getProperty("line.separator"))); - } else if (InputStream.class.equals(clazz)) { - try { - data = new String(((InputStream) response.body()).readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new CleverClientException(e); - } - } else { - data = (String) response.body(); - } - logger.error("Response : {}", data); - throw new CleverClientException(fillResponseInfo(response, data)); - } - } - - private ResponseInfo fillResponseInfo(HttpResponse response, String data) { - var request = response.request(); - return ResponseInfo.builder() - .statusCode(response.statusCode()) - .data(data) - .headers(response.headers().map()) - .request(RequestInfo.builder() - .httpMethod(request.method()) - .url(request.uri().toString()) - .headers(request.headers().map()) - .build()) - .build(); - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSenderFactory.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSenderFactory.java deleted file mode 100644 index 25da3c2..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSenderFactory.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.support.ReturnType.Category; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.EnumMap; -import java.util.function.Supplier; - -/** - * Factory for the abstrac class HttpSender. - */ -public class HttpSenderFactory { - - private static Logger logger = LoggerFactory.getLogger(HttpSenderFactory.class); - - private static HttpSenderFactory factory = null; - - private EnumMap> sendersMap; - - private HttpSenderFactory() { - sendersMap = new EnumMap<>(Category.class); - sendersMap.put(Category.ASYNC_STREAM_EVENT, HttpAsyncStreamEventSender::new); - sendersMap.put(Category.ASYNC_STREAM, HttpAsyncStreamSender::new); - sendersMap.put(Category.ASYNC_LIST, HttpAsyncListSender::new); - sendersMap.put(Category.ASYNC_GENERIC, HttpAsyncGenericSender::new); - sendersMap.put(Category.ASYNC_CUSTOM, HttpAsyncCustomSender::new); - sendersMap.put(Category.ASYNC_BINARY, HttpAsyncBinarySender::new); - sendersMap.put(Category.ASYNC_PLAIN_TEXT, HttpAsyncPlainTextSender::new); - sendersMap.put(Category.SYNC_STREAM_EVENT, HttpSyncStreamEventSender::new); - sendersMap.put(Category.SYNC_STREAM, HttpSyncStreamSender::new); - sendersMap.put(Category.SYNC_LIST, HttpSyncListSender::new); - sendersMap.put(Category.SYNC_GENERIC, HttpSyncGenericSender::new); - sendersMap.put(Category.SYNC_CUSTOM, HttpSyncCustomSender::new); - sendersMap.put(Category.SYNC_BINARY, HttpSyncBinarySender::new); - sendersMap.put(Category.SYNC_PLAIN_TEXT, HttpSyncPlainTextSender::new); - } - - public static HttpSenderFactory get() { - if (factory == null) { - factory = new HttpSenderFactory(); - } - return factory; - } - - /** - * Instances a HttpSender concrete class based on the return type. - * - * @param returnType The method return type. - * @return A HttpSender concrete class. - */ - public HttpSender createSender(ReturnType returnType) { - HttpSender sender = null; - var category = returnType.category(); - if (category != null && sendersMap.containsKey(category)) { - sender = sendersMap.get(category).get(); - logger.debug("Created Sender : {}", sender.getClass().getSimpleName()); - } else { - throw new CleverClientException("Unsupported return type {0}.", returnType.getFullClassName(), null); - } - return sender; - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncBinarySender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncBinarySender.java deleted file mode 100644 index 875d1ef..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncBinarySender.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; - -import java.io.IOException; -import java.io.InputStream; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpSyncBinarySender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofInputStream()); - - throwExceptionIfErrorIsPresent(httpResponse, InputStream.class); - - var rawData = httpResponse.body(); - - logger.debug("Response : {}", rawData); - - return rawData; - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncCustomSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncCustomSender.java deleted file mode 100644 index 960e0b8..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncCustomSender.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpSyncCustomSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()); - - throwExceptionIfErrorIsPresent(httpResponse, null); - - var rawData = httpResponse.body(); - - logger.debug("Response : {}", rawData); - - return JsonUtil.jsonToObject(rawData, returnType.getBaseClass()); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncGenericSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncGenericSender.java deleted file mode 100644 index 6789470..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncGenericSender.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpSyncGenericSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()); - - throwExceptionIfErrorIsPresent(httpResponse, null); - - var rawData = httpResponse.body(); - - logger.debug("Response : {}", rawData); - - return JsonUtil.jsonToParametricObject(rawData, returnType.getGenericClassIfExists(), - returnType.getBaseClass()); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncListSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncListSender.java deleted file mode 100644 index 3ec33b1..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncListSender.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpSyncListSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()); - - throwExceptionIfErrorIsPresent(httpResponse, null); - - var rawData = httpResponse.body(); - - logger.debug("Response : {}", rawData); - - return JsonUtil.jsonToList(rawData, returnType.getBaseClass()); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncPlainTextSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncPlainTextSender.java deleted file mode 100644 index 3bd0adf..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncPlainTextSender.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.ReturnType; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; - -public class HttpSyncPlainTextSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()); - - throwExceptionIfErrorIsPresent(httpResponse, null); - - var rawData = httpResponse.body(); - - logger.debug("Response : {}", rawData); - - return rawData; - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamEventSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamEventSender.java deleted file mode 100644 index 5b69710..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamEventSender.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.Event; -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.CleverClientSSE; -import io.github.sashirestela.cleverclient.support.CleverClientSSE.LineRecord; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.stream.Stream; - -public class HttpSyncStreamEventSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofLines()); - - throwExceptionIfErrorIsPresent(httpResponse, Stream.class); - - final var lineRecord = new LineRecord(); - final var events = returnType.getClassByEvent().keySet(); - - return httpResponse.body() - .map(line -> { - logger.debug("Response : {}", line); - lineRecord.updateWith(line); - return new CleverClientSSE(lineRecord, events); - }) - .filter(CleverClientSSE::isActualData) - .map(item -> Event.builder() - .name(item.getMatchedEvent()) - .data(JsonUtil.jsonToObject(item.getActualData(), - returnType.getClassByEvent().get(item.getMatchedEvent()))) - .build()); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamSender.java b/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamSender.java deleted file mode 100644 index 2e2146f..0000000 --- a/src/main/java/io/github/sashirestela/cleverclient/sender/HttpSyncStreamSender.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.sashirestela.cleverclient.sender; - -import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.CleverClientSSE; -import io.github.sashirestela.cleverclient.support.CleverClientSSE.LineRecord; -import io.github.sashirestela.cleverclient.support.ReturnType; -import io.github.sashirestela.cleverclient.util.JsonUtil; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.stream.Stream; - -public class HttpSyncStreamSender extends HttpSender { - - @Override - public Object sendRequest(HttpClient httpClient, HttpRequest httpRequest, ReturnType returnType) { - try { - - var httpResponse = httpClient.send(httpRequest, BodyHandlers.ofLines()); - - throwExceptionIfErrorIsPresent(httpResponse, Stream.class); - - final var lineRecord = new LineRecord(); - - return httpResponse.body() - .map(line -> { - logger.debug("Response : {}", line); - lineRecord.updateWith(line); - return new CleverClientSSE(lineRecord); - }) - .filter(CleverClientSSE::isActualData) - .map(item -> JsonUtil.jsonToObject(item.getActualData(), returnType.getBaseClass())); - - } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CleverClientException(e); - } - } - -} diff --git a/src/main/java/io/github/sashirestela/cleverclient/support/ReturnType.java b/src/main/java/io/github/sashirestela/cleverclient/support/ReturnType.java index 2c3f6a1..2fd9aa6 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/support/ReturnType.java +++ b/src/main/java/io/github/sashirestela/cleverclient/support/ReturnType.java @@ -80,10 +80,9 @@ private Optional getInnerAnnotationIfExists(Method method, private Map> calculateClassByEvent(StreamType[] streamTypeList) { Map> map = new ConcurrentHashMap<>(); - Arrays.stream(streamTypeList).forEach(streamType -> { - Arrays.stream(streamType.events()) - .forEach(event -> map.put(event, streamType.type())); - }); + Arrays.stream(streamTypeList) + .forEach(streamType -> Arrays.stream(streamType.events()) + .forEach(event -> map.put(event, streamType.type()))); return map; } @@ -169,11 +168,11 @@ private Category syncCategory() { } } - private boolean isAsync() { + public boolean isAsync() { return size > 1 && ASYNC.equals(returnTypeArray[firstIndex]); } - private boolean isStream() { + public boolean isStream() { return size > 1 && STREAM.equals(returnTypeArray[prevLastIndex]); } diff --git a/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java b/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java index 2ba218c..f2b2129 100644 --- a/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java +++ b/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java @@ -4,6 +4,7 @@ import io.github.sashirestela.cleverclient.annotation.GET; import io.github.sashirestela.cleverclient.annotation.Query; import io.github.sashirestela.cleverclient.annotation.Resource; +import io.github.sashirestela.cleverclient.client.JavaHttpClientAdapter; import io.github.sashirestela.cleverclient.http.HttpRequestData; import io.github.sashirestela.cleverclient.support.ContentType; import io.github.sashirestela.cleverclient.util.HttpRequestBodyTestUtility; @@ -39,7 +40,6 @@ void shouldSetPropertiesToDefaultValuesWhenBuilderIsCalledWithoutThoseProperties .baseUrl("https://test") .build(); assertEquals(Map.of(), cleverClient.getHeaders()); - assertEquals(HttpClient.Version.HTTP_2, cleverClient.getHttpClient().version()); assertNotNull(cleverClient.getBaseUrl()); assertNotNull(cleverClient.getHttpProcessor()); assertNull(cleverClient.getRequestInterceptor()); @@ -51,7 +51,7 @@ void shouldImplementInterfaceWhenCallingCreate() { var cleverClient = CleverClient.builder() .baseUrl("https://test") .header("headerName", "headerValue") - .httpClient(HttpClient.newHttpClient()) + .clientAdapter(new JavaHttpClientAdapter()) .build(); var test = cleverClient.create(TestCleverClient.class); assertNotNull(test); @@ -61,7 +61,7 @@ void shouldImplementInterfaceWhenCallingCreate() { void shouldThrownExceptionWhenTryingToPassAnEmptyBaseUrl() { var cleverClientBuilder = CleverClient.builder() .header("headerName", "headerValue") - .httpClient(HttpClient.newHttpClient()); + .clientAdapter(new JavaHttpClientAdapter()); assertThrows(NullPointerException.class, cleverClientBuilder::build); } @@ -103,7 +103,7 @@ void shouldModifyRequestWhenPassingInterceptorFunction() { var cleverClient = CleverClient.builder() .baseUrl("https://test") .requestInterceptor(requestInterceptor) - .httpClient(httpClient) + .clientAdapter(new JavaHttpClientAdapter(httpClient)) .build(); when(httpClient.sendAsync(any(), any())) .thenReturn(CompletableFuture.completedFuture(mock(HttpResponse.class))); @@ -136,7 +136,7 @@ void shouldNotThrownExceptionWhenBodyInspectorEndsSuccessfully() { var cleverClient = CleverClient.builder() .baseUrl("https://test") .bodyInspector(bodyInspector) - .httpClient(httpClient) + .clientAdapter(new JavaHttpClientAdapter(httpClient)) .build(); when(httpClient.sendAsync(any(), any())) .thenReturn(CompletableFuture.completedFuture(mock(HttpResponse.class))); diff --git a/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java b/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java index 221afaf..b1fbca9 100644 --- a/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java +++ b/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java @@ -1,5 +1,6 @@ package io.github.sashirestela.cleverclient.http; +import io.github.sashirestela.cleverclient.client.JavaHttpClientAdapter; import io.github.sashirestela.cleverclient.support.CleverClientException; import io.github.sashirestela.cleverclient.support.Configurator; import org.junit.jupiter.api.BeforeAll; @@ -59,7 +60,7 @@ void init() { httpProcessor = HttpProcessor.builder() .baseUrl("https://api.demo") .headers(List.of()) - .httpClient(httpClient) + .clientAdapter(new JavaHttpClientAdapter(httpClient)) .build(); } @@ -76,6 +77,7 @@ void shouldReturnAStringSyncWhenMethodReturnTypeIsAString() throws IOException, when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(httpResponse); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); var service = httpProcessor.createProxy(ITest.SyncService.class); @@ -100,6 +102,7 @@ void shouldshouldReturnABinarySyncWhenMethodReturnTypeIsABinary() throws IOExcep when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) .thenReturn(httpResponseBinary); when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseBinary.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); when(httpResponseBinary.body()).thenReturn(binaryData); var service = httpProcessor.createProxy(ITest.SyncService.class); @@ -123,6 +126,7 @@ void shouldReturnAnObjectSyncWhenMethodReturnTypeIsAnObject() throws IOException when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(httpResponse); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); var service = httpProcessor.createProxy(ITest.SyncService.class); @@ -146,6 +150,7 @@ void shouldReturnAGenericSyncWhenMethodReturnTypeIsAnObject() throws IOException when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(httpResponse); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()) .thenReturn("{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); @@ -171,6 +176,7 @@ void shouldReturnAListSyncWhenMethodReturnTypeIsAList() throws IOException, Inte when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(httpResponse); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); var service = httpProcessor.createProxy(ITest.SyncService.class); @@ -195,6 +201,7 @@ void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStream() throws IOException, when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) .thenReturn(httpResponseStream); when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponseStream.body()) .thenReturn(Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); @@ -221,6 +228,7 @@ void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStreamEvent() throws IOExcept when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) .thenReturn(httpResponseStream); when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponseStream.body()) .thenReturn(Stream.of("event: created", "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); @@ -248,6 +256,7 @@ void shouldReturnAStringAsyncWhenMethodReturnTypeIsAString() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponse)); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); var service = httpProcessor.createProxy(ITest.AsyncService.class); @@ -263,6 +272,7 @@ void shouldReturnABinaryAsyncWhenMethodReturnTypeIsABinary() throws FileNotFound when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponseBinary)); when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseBinary.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); when(httpResponseBinary.body()).thenReturn(binaryData); var service = httpProcessor.createProxy(ITest.AsyncService.class); @@ -277,6 +287,7 @@ void shouldReturnAnObjectAsyncWhenMethodReturnTypeIsAnObject() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponse)); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); var service = httpProcessor.createProxy(ITest.AsyncService.class); @@ -291,6 +302,7 @@ void shouldReturnAGenericAsyncWhenMethodReturnTypeIsAnObject() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponse)); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()) .thenReturn("{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); @@ -307,6 +319,7 @@ void shouldReturnAListAsyncWhenMethodReturnTypeIsAList() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponse)); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); var service = httpProcessor.createProxy(ITest.AsyncService.class); @@ -322,6 +335,7 @@ void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStream() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponseStream.body()) .thenReturn(Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); @@ -338,6 +352,7 @@ void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStreamEvent() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponseStream.body()) .thenReturn(Stream.of("event: created", "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); @@ -355,6 +370,7 @@ void shouldReturnAnObjectWhenMethodIsAnnotatedWithMultipart() { when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) .thenReturn(CompletableFuture.completedFuture(httpResponse)); when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); var service = httpProcessor.createProxy(ITest.AsyncService.class);