All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yudichev.jiotty.common.rest.RestClients Maven / Gradle / Ivy

package net.yudichev.jiotty.common.rest;

import com.fasterxml.jackson.databind.JsonNode;
import net.yudichev.jiotty.common.lang.Json;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public final class RestClients {
    private static final Logger logger = LoggerFactory.getLogger(RestClients.class);

    private static final int DEFAULT_CALL_RETRY_COUNT = 3;
    private static final Duration DEFAULT_HTTP_TIMEOUT = Duration.ofSeconds(60);

    private RestClients() {
    }

    public static OkHttpClient newClient() {
        return newClient(builder -> {});
    }

    public static OkHttpClient newClient(Consumer customizer) {
        AtomicReference clientRef = new AtomicReference<>();
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .followRedirects(true)
                .followSslRedirects(true)
                .connectTimeout(DEFAULT_HTTP_TIMEOUT)
                .callTimeout(DEFAULT_HTTP_TIMEOUT)
                .readTimeout(DEFAULT_HTTP_TIMEOUT)
                .writeTimeout(DEFAULT_HTTP_TIMEOUT)
                .addInterceptor(new RedirectSupportInterceptor(clientRef::get));
        customizer.accept(builder);
        OkHttpClient client = builder.build();
        clientRef.set(client);
        return client;
    }

    public static  CompletableFuture call(Request request, Class responseType) {
        return call(newClient().newCall(request), responseType);
    }

    public static  CompletableFuture call(Call theCall, Class responseType) {
        return call(theCall, responseType, DEFAULT_CALL_RETRY_COUNT);
    }

    public static  CompletableFuture call(Call theCall, Class responseType, int retryCount) {
        CompletableFuture future = new CompletableFuture<>();
        theCall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                logger.debug("Call failed: {}, retries left: {}", call, retryCount, e);
                if (retryCount == 0) {
                    future.completeExceptionally(new RuntimeException("call failed: " + call, e));
                } else {
                    call(call.clone(), responseType, retryCount - 1)
                            .whenComplete((result, exception) -> {
                                if (exception == null) {
                                    future.complete(result);
                                } else {
                                    future.completeExceptionally(exception);
                                }
                            });
                }
            }

            @Override
            public void onResponse(Call call, okhttp3.Response response) {
                ResponseBody responseBody = requireNonNull(response.body());
                try {
                    String responseString = responseBody.string();
                    future.complete(Json.parse(responseString, responseType));
                } catch (RuntimeException | IOException e) {
                    future.completeExceptionally(new RuntimeException("failed to parse response body to json: " + responseBody, e));
                }
            }
        });
        return future;
    }

    public static JsonNode getRequiredNode(JsonNode parentNode, String nodeName) {
        JsonNode childNode = parentNode.get(nodeName);
        checkState(childNode != null,
                "no '%s' node in response: %s", nodeName, parentNode);
        return childNode;
    }

    public static String getRequiredNodeString(JsonNode parentNode, String nodeName) {
        JsonNode childNode = getRequiredNode(parentNode, nodeName);
        checkState(childNode.isTextual(), "node '%s' is not textual in %s", nodeName, parentNode);
        return childNode.asText();
    }

    public static int getRequiredNodeInt(JsonNode parentNode, String nodeName) {
        JsonNode childNode = getRequiredNode(parentNode, nodeName);
        checkState(childNode.isInt(), "node '%s' is not an integer in %s", nodeName, parentNode);
        return childNode.asInt();
    }

    public static long getRequiredNodeLong(JsonNode parentNode, String nodeName) {
        JsonNode childNode = getRequiredNode(parentNode, nodeName);
        checkState(childNode.isLong(), "node '%s' is not a long in %s", nodeName, parentNode);
        return childNode.asLong();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy