
com.trimble.id.HttpClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trimble-id Show documentation
Show all versions of trimble-id Show documentation
Trimble Identity OAuth Client library holds the client classes that are used for communicating with Trimble Identity Service
The newest version!
package com.trimble.id;
import static com.trimble.id.AuthenticationConstants.FILE;
import static com.trimble.id.AuthenticationConstants.TEXT;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* HttpClient class to make HTTP requests.
*/
public class HttpClient {
private URI baseUri;
private String accessToken;
private OkHttpClient client;
private HttpClientConfiguration configuration;
private final Lock lock = new ReentrantLock();
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* Default constructor
*/
public HttpClient() {
}
/**
* Constructor with configuration
*
* @param configuration - HttpClientConfiguration
*/
public HttpClient(HttpClientConfiguration configuration) {
this.configuration = configuration;
}
/**
* Constructor with baseUri
*
* @param baseUri - baseUri
*/
public HttpClient(URI baseUri) {
this.baseUri = baseUri;
}
/**
* Constructor with baseUri and configuration
*
* @param baseUri - baseUri
* @param configuration - HttpClientConfiguration
*/
public HttpClient(URI baseUri, HttpClientConfiguration configuration) {
this.baseUri = baseUri;
this.configuration = configuration;
}
/**
* Get method
*
* @param - responseType
* @param url - url
* @param headers - headers
* @param responseType - responseType
* @return - Future that resolves to Response
* @throws TCPServiceException - If an error occurs due to API or
* network-related
* issues.
* @throws SDKClientException - If a client-side error occurs within the SDK.
*/
public CompletableFuture> get(String url, List headers, Class responseType)
throws TCPServiceException, SDKClientException {
try {
String finalUrl = buildUrl(url);
Request.Builder builder = new Request.Builder().url(finalUrl).get();
addHeaders(headers, builder);
Request request = builder.build();
return executeRequest(request, responseType);
} catch (Exception e) {
return handleException(e);
}
}
/**
* Post method
*
* @param - responseType
* @param url - url
* @param payload - It has the payload data and content type
* @param headers - headers
* @param responseType - responseType
* @return - Future that resolves to Response
* @throws TCPServiceException - If an error occurs due to API or
* network-related
* issues.
* @throws SDKClientException - If a client-side error occurs within the SDK.
*/
public CompletableFuture> post(String url, RequestPayload payload, List headers,
Class responseType) throws TCPServiceException, SDKClientException {
try {
String finalUrl = buildUrl(url);
RequestBody requestBody = createRequestBody(payload);
Request.Builder builder = new Request.Builder().url(finalUrl).post(requestBody);
addHeaders(headers, builder);
Request request = builder.build();
return executeRequest(request, responseType);
} catch (Exception e) {
return handleException(e);
}
}
/**
* Patch method
*
* @param - responseType
* @param url - url
* @param payload - It has the payload data and content type
* @param headers - headers
* @param responseType - responseType
* @return
* @throws TCPServiceException - If an error occurs due to API or
* network-related
* issues.
* @throws SDKClientException - If a client-side error occurs within the SDK.
*/
public CompletableFuture> patch(String url, RequestPayload payload, List headers,
Class responseType) throws TCPServiceException, SDKClientException {
try {
String finalUrl = buildUrl(url);
RequestBody requestBody = createRequestBody(payload);
Request.Builder builder = new Request.Builder().url(finalUrl).patch(requestBody);
addHeaders(headers, builder);
Request request = builder.build();
return executeRequest(request, responseType);
} catch (Exception e) {
return handleException(e);
}
}
/**
* Put method
*
* @param - responseType
* @param url - url
* @param payload - It has the payload data and content type
* @param headers - headers
* @param responseType - responseType
* @return - Future that resolves to Response
* @throws TCPServiceException - If an error occurs due to API or
* network-related
* issues.
* @throws SDKClientException - If a client-side error occurs within the SDK.
*/
public CompletableFuture> put(String url, RequestPayload payload, List headers,
Class responseType)
throws TCPServiceException, SDKClientException {
try {
String finalUrl = buildUrl(url);
RequestBody requestBody = createRequestBody(payload);
Request.Builder builder = new Request.Builder();
addHeaders(headers, builder);
Request request = builder.url(finalUrl).put(requestBody).build();
return executeRequest(request, responseType);
} catch (Exception e) {
return handleException(e);
}
}
/**
* Delete method
*
* @param - responseType
* @param url - url
* @param headers - headers
* @param responseType - responseType
* @return - Future that resolves to Response
* @throws TCPServiceException - If an error occurs due to API or
* network-related
* issues.
* @throws SDKClientException - If a client-side error occurs within the SDK.
*/
public CompletableFuture> delete(String url, List headers, Class responseType)
throws TCPServiceException, SDKClientException {
try {
String finalUrl = this.buildUrl(url);
Request.Builder builder = new Request.Builder();
addHeaders(headers, builder);
Request request = builder.url(finalUrl).delete().build();
return executeRequest(request, responseType);
} catch (Exception e) {
return handleException(e);
}
}
/**
* Shutdown the HttpClient
*/
public void dispose() {
if (client != null) {
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
}
}
private CompletableFuture> handleException(Exception e) {
CompletableFuture> future = new CompletableFuture<>();
future.completeExceptionally(e);
return future;
}
CompletableFuture> executeRequest(Request request, Class responseType)
throws TCPServiceException, SDKClientException {
CompletableFuture> future = new CompletableFuture<>();
if (this.configuration == null) {
this.configuration = new HttpClientConfiguration();
}
this.client = new OkHttpClient.Builder()
.connectTimeout(Duration.ofMinutes(5))
.callTimeout(configuration.getHttpTimeout())
.addInterceptor(new RetryInterceptor(configuration))
.build();
long startTime = System.currentTimeMillis();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
future.completeExceptionally(e);
}
@Override
public void onResponse(Call call, okhttp3.Response response) throws IOException {
try {
int statusCode = response.code();
Map> headers = response.headers().toMultimap();
String requestId = getRequestId(headers);
String apiEndpoint = request.url().toString();
if (!response.isSuccessful()) {
String responseBody = response.body() != null ? response.body().string() : null;
String errorMessage = responseBody != null ? responseBody
: response.message();
future.completeExceptionally(new TCPServiceException(errorMessage, requestId, statusCode));
} else {
Response result = handleSuccessfulResponse(response, responseType, statusCode, headers,
requestId);
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
result.setApiEndpoint(apiEndpoint);
result.setResponseTime(responseTime);
future.complete(result);
}
} catch (Exception e) {
future.completeExceptionally(new SDKClientException(e));
} finally {
if (response != null) {
response.close();
}
}
}
});
return future;
}
private String getRequestId(Map> headers) {
return headers.get("tcp-request-id") != null ? headers.get("tcp-request-id").get(0) : null;
}
private Response handleSuccessfulResponse(okhttp3.Response response, Class responseType, int statusCode,
Map> headers, String requestId) throws IOException {
if (responseType == byte[].class) {
byte[] responseBodyBytes;
responseBodyBytes = response.body() != null ? response.body().bytes() : null;
return new Response<>(statusCode, headers, responseType.cast(responseBodyBytes), requestId, null);
} else {
String responseBody = response.body() != null ? response.body().string() : null;
return handleResponse(responseBody, statusCode, headers, responseType, requestId);
}
}
private Response handleResponse(String responseBody, int statusCode, Map> headers,
Class responseType, String requestId) {
if (responseBody == null || responseBody.isEmpty()) {
return new Response<>(statusCode, headers, null, requestId, null);
}
try {
objectMapper.readTree(responseBody);
T responseObject = objectMapper.readValue(responseBody, responseType);
return new Response<>(statusCode, headers, responseObject, requestId, null);
} catch (JsonProcessingException e) {
// Handle invalid JSON, treat responseBody as plain string
T castedResponseBody = null;
castedResponseBody = responseType.cast(responseBody);
return new Response<>(statusCode, headers, castedResponseBody, requestId, null);
}
}
private void addHeaders(List headers, Request.Builder builder) {
if (headers != null && !headers.isEmpty()) {
for (NameValuePair header : headers) {
builder.addHeader(header.getName(), header.getValue());
}
}
if (accessToken != null) {
builder.header("Authorization", accessToken);
}
}
private RequestBody createRequestBody(RequestPayload payload) {
if (payload == null) {
return RequestBody.create(new byte[0], MediaType.parse(AuthenticationConstants.APPLICATION_OCTET_STREAM));
}
String contentType = payload.getContentType();
if (AuthenticationConstants.APPLICATION_JSON.equals(contentType)) {
String jsonPayload;
try {
jsonPayload = objectMapper.writeValueAsString(payload.getPayload());
return RequestBody.create(jsonPayload, MediaType.parse(AuthenticationConstants.APPLICATION_JSON));
} catch (JsonProcessingException e) {
throw new SDKClientException(e);
}
} else if (AuthenticationConstants.APPLICATION_OCTET_STREAM.equals(contentType)) {
return createOctetStreamRequestBody(payload);
} else if (AuthenticationConstants.MULTIPART_FORM_DATA.equals(contentType)) {
return createMultipartRequestBody(payload);
}
// Default case: convert to string and use application/json
return RequestBody.create(payload.toString(), MediaType.parse(AuthenticationConstants.APPLICATION_JSON));
}
private RequestBody createOctetStreamRequestBody(RequestPayload payload) {
Object payloadData = payload.getPayload();
MediaType mediaType = MediaType.parse(AuthenticationConstants.APPLICATION_OCTET_STREAM);
if (payloadData instanceof byte[]) {
return RequestBody.create((byte[]) payloadData, mediaType);
}
if (payloadData instanceof File) {
return RequestBody.create((File) payloadData, mediaType);
} else {
throw new SDKClientException("Pass byte[] or File as payload for application/octet-stream");
}
}
private RequestBody createMultipartRequestBody(RequestPayload payload) {
MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
Set> formEntries = payload.getFormPayload().entrySet();
for (Entry formEntry : formEntries) {
FormData form = formEntry.getValue();
if (FILE.equals(formEntry.getKey())) {
multipartBodyBuilder.addFormDataPart(form.getFieldName(), form.getFileName(),
RequestBody.create(form.getFile(), MediaType.parse(form.getMimeType())));
} else if (TEXT.equals(formEntry.getKey())) {
multipartBodyBuilder.addFormDataPart(form.getFieldName(), form.getFieldValue());
}
}
return multipartBodyBuilder.build();
}
private static class RetryInterceptor implements Interceptor {
private final HttpClientConfiguration configuration;
public RetryInterceptor(HttpClientConfiguration configuration) {
this.configuration = configuration;
}
@Override
public okhttp3.Response intercept(Chain chain) throws SDKClientException, TCPServiceException {
Request request = chain.request();
IOException exception = null;
int tryCount = 0;
int code = 0;
okhttp3.Response response = null;
// Retry for server error and network error
while (tryCount < configuration.getRetries()) {
try {
response = chain.proceed(request);
code = response.code();
if (!isServerError(code)) {
return response;
} else {
tryCount++;
handleRetryDelay();
}
} catch (IOException e) {
exception = e;
tryCount++;
handleRetryDelay();
} finally {
if (response != null && isServerError(code)) {
response.close();
}
}
}
if (isServerError(code)) {
throw new TCPServiceException("Internal Server error", null, code);
} else {
throw new TCPServiceException(exception, null, code);
}
}
private boolean isServerError(int code) {
return code >= 500 && code < 600;
}
private void handleRetryDelay() throws SDKClientException {
try {
Thread.sleep(configuration.getRetryInterval().toMillis());
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new SDKClientException(ie);
}
}
}
void setAuthorizationToken(String accessToken) {
lock.lock();
try {
this.accessToken = "Bearer ".concat(accessToken);
} finally {
lock.unlock();
}
}
private String buildUrl(String url) {
if (baseUri != null) {
return baseUri.resolve(url).toString();
}
return url;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy