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

com.undefinedlabs.scope.network.HttpClientImpl Maven / Gradle / Ivy

package com.undefinedlabs.scope.network;

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import com.undefinedlabs.scope.logger.ScopeLogger;
import com.undefinedlabs.scope.logger.ScopeLoggerResolver;
import com.undefinedlabs.scope.settings.ScopeSettings;
import com.undefinedlabs.scope.settings.credentials.CredentialsUtils;
import com.undefinedlabs.scope.settings.credentials.ScopeCredentials;
import com.undefinedlabs.scope.utils.StringUtils;
import com.undefinedlabs.scope.utils.props.SystemProps;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class HttpClientImpl implements HttpClient {

  private static final ScopeLogger LOGGER = ScopeLoggerResolver.INSTANCE.get();

  private static final int DEFAULT_TOTAL_RETRIES = 3;
  private static final long DEFAULT_BACKOFF_RETRY = 1000;

  private static final int DEFAULT_CONNECTION_POOL_SIZE = 5;
  private static final long DEFAULT_CONNECTION_KEEP_ALIVE_DURATION = 300000;

  private final int totalRetries;
  private final long backoffRetry;

  private final String apiEndpoint;
  private final OkHttpClient okHttpClient;

  public HttpClientImpl(final Builder builder) {
    totalRetries = builder.totalRetries;
    backoffRetry = builder.backoffRetry;

    apiEndpoint = builder.credentials.getApiEndpoint();
    okHttpClient =
        new OkHttpClient.Builder()
            .connectionPool(
                new ConnectionPool(
                    builder.poolSize, builder.keepAliveDuration, TimeUnit.MILLISECONDS))
            .addInterceptor(new UserAgentFilter(builder.userAgent))
            .addInterceptor(new ApiKeyFilter(builder.credentials.getApiKey()))
            .build();
  }

  @Override
  public Response get(final String path) {
    final Request request =
        new Request.Builder().url(HttpClientUtils.INSTANCE.createUrl(apiEndpoint, path)).build();
    return execute(request);
  }

  @Override
  public Response post(final String path, final RequestBody requestBody) throws NetworkException {
    final Request request =
        new Request.Builder()
            .url(HttpClientUtils.INSTANCE.createUrl(apiEndpoint, path))
            .post(requestBody)
            .build();
    return execute(request);
  }

  Response execute(final Request originalReq) {
    final String idReq = UUID.randomUUID().toString();
    final String url = originalReq.url().toString();

    int retries = totalRetries;
    boolean mustRetry = false;

    Response lastResponse = null;
    Throwable lastError = null;

    do {
      if (lastResponse != null) {
        lastResponse.close();
      }

      final Request request = originalReq.newBuilder().build();

      final long startReqTimestamp = System.currentTimeMillis();
      try {
        lastResponse = okHttpClient.newCall(request).execute();
        HttpClientUtils.INSTANCE.logRequestExecutionDuration(
            idReq, url, startReqTimestamp, originalReq.body(), lastResponse);

        if (!lastResponse.isSuccessful()) {
          mustRetry = lastResponse.code() >= 500 && lastResponse.code() != 501;
        } else {
          if (mustRetry) {
            LOGGER.info(
                "[idReq:"
                    + idReq
                    + "] ScopeAgent --> Backend ("
                    + url
                    + ") successful after "
                    + (DEFAULT_TOTAL_RETRIES - retries)
                    + " retries.");
          }
        }

      } catch (final Throwable e) {
        LOGGER.error("[idReq:" + idReq + "] -- Error ScopeAgent --> Backend (" + url + "): " + e);
        lastError = e;
        mustRetry = false;
      }

      if (retries == 0) {
        mustRetry = false;
      }

      if (mustRetry) {
        try {
          Thread.sleep(backoffRetry);
        } catch (final InterruptedException ignored) {
          // Ignored
        }

        LOGGER.debug(
            "[idReq:"
                + idReq
                + "] -- Retrying ScopeAgent --> Backend ("
                + url
                + "). Left "
                + retries
                + " retries.");
        retries -= 1;
      }

    } while (mustRetry);

    if (lastError != null) {
      throw new NetworkException(lastError);
    }

    return lastResponse;
  }

  public static class Builder {

    private final ScopeCredentials credentials;
    private final int poolSize;
    private final long keepAliveDuration;
    private final String userAgent;
    private int totalRetries;
    private long backoffRetry;

    public Builder(final ScopeSettings settings) {
      try {
        credentials =
            CredentialsUtils.INSTANCE.extractFromDsn(
                (String) settings.getSetting(ScopeSettings.SCOPE_DSN));
        final Object connectionPoolSize =
            settings.getSetting(ScopeSettings.SCOPE_TRACER_DISPATCHER_CONNECTIONS_POOL_SIZE);
        final Object connectionKeepAliveDuration =
            settings.getSetting(
                ScopeSettings.SCOPE_TRACER_DISPATCHER_CONNECTIONS_KEEP_ALIVE_DURATION);

        poolSize =
            connectionPoolSize != null ? (int) connectionPoolSize : DEFAULT_CONNECTION_POOL_SIZE;
        keepAliveDuration =
            connectionKeepAliveDuration != null
                ? (long) connectionKeepAliveDuration
                : DEFAULT_CONNECTION_KEEP_ALIVE_DURATION;
        userAgent =
            "scope-agent-java"
                + (StringUtils.isNotEmpty(SystemProps.AGENT_VERSION)
                    ? "/" + SystemProps.AGENT_VERSION
                    : "");

        totalRetries = DEFAULT_TOTAL_RETRIES;
        backoffRetry = DEFAULT_BACKOFF_RETRY;
      } catch (final Exception e) {
        throw new IllegalArgumentException(e.getMessage());
      }
    }

    public Builder withTotalRetries(final int totalRetries) {
      this.totalRetries = totalRetries;
      return this;
    }

    public Builder withBackoffRetry(final long backoffRetry) {
      this.backoffRetry = backoffRetry;
      return this;
    }

    public HttpClient build() {
      return new HttpClientImpl(this);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy