Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.geotab.http.invoker.ServerInvoker Maven / Gradle / Ivy
/*
*
* 2020 Copyright (C) Geotab Inc. All rights reserved.
*/
package com.geotab.http.invoker;
import static com.geotab.http.exception.ErrorHandler.checkForError;
import static org.apache.hc.core5.http.io.entity.HttpEntities.create;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.geotab.http.exception.ResponseFailException;
import com.geotab.http.request.BaseRequest;
import com.geotab.http.response.BaseResponse;
import com.geotab.model.serialization.ApiJsonSerializer;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.util.Timeout;
/**
* A class for communicating over the Internet to MyGeotab.
*/
@Slf4j
public class ServerInvoker {
public static final int DEFAULT_TIMEOUT = 300000;
public static final String DEFAULT_SERVICE_PATH = "apiv1";
@Getter
private String url;
@Getter
private Integer timeout;
@Getter
private String servicePath;
@Getter
private CloseableHttpClient httpClient;
public ServerInvoker(String url) {
this(url, DEFAULT_TIMEOUT, DEFAULT_SERVICE_PATH);
}
public ServerInvoker(String url, Integer timeout, String servicePath) {
this(url, timeout, servicePath, null);
}
public ServerInvoker(String url, Integer timeout, String servicePath,
CloseableHttpClient httpClient) {
this.servicePath = servicePath;
setUrl(url);
this.timeout = Optional.ofNullable(timeout).orElse(DEFAULT_TIMEOUT);
this.httpClient = Optional.ofNullable(httpClient).orElse(buildDefaultHttpClient());
log.debug("ServerInvoker params: \n url = {} \n timeout = {} \n {}",
this.url, this.timeout,
httpClient != null ? "custom http client" : "default http client");
}
public void setUrl(String url) {
log.debug("ServerInvoker url set to {}", url);
this.url = url + "/" + Optional.ofNullable(this.servicePath).orElse(DEFAULT_SERVICE_PATH);
}
/**
* Make the http call to MyGeotab server.
*
* @param request The request.
* @param responseType Response type class, used to deserialize the response.
* @return The result for queries (Get) and null for non queries (Add, Set, Remove).
* @throws Exception Exception which can occur while executing the call.
*/
@SuppressWarnings("Indentation")
public , ResultT>
Optional
invoke(RequestT request, Class responseType) throws Exception {
log.debug("Method params: \n request = {} \n responseType = {}", request, responseType);
ResponseT response = doInvoke(this.url, request, responseType);
if (response == null) {
return Optional.empty();
}
return Optional.ofNullable(response.getResult());
}
@SuppressWarnings("Indentation")
private , ResultT> ResponseT
doInvoke(String url, RequestT request, Class responseType) throws Exception {
log.debug("Method params: \n url = {} \n request = {} \n responseType = {}",
url, request, responseType);
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(create(outputStream -> {
ApiJsonSerializer.getInstance().getObjectMapper().writeValue(outputStream, request);
outputStream.flush();
}, ContentType.APPLICATION_JSON));
HttpClientResponseHandler responseHandler = getDefaultResponseHandler(request,
responseType);
ResponseT response = httpClient.execute(httpPost, responseHandler);
checkForError(request.getMethod(), response);
return response;
}
@SuppressWarnings("Indentation")
private
HttpClientResponseHandler
getDefaultResponseHandler(RequestT request, Class responseType) {
return response -> {
if (response == null) {
log.error("Got null response while calling {} with request {}", this.url, request);
throw new HttpException("Unexpected empty response from " + this.url);
}
try (HttpEntity httpEntity = response.getEntity();
InputStream inputStream = httpEntity.getContent()) {
boolean isSuccess =
isSuccessCode(response.getCode()) || isRedirectCode(response.getCode());
if (!isSuccess) {
handleErrorResponse(inputStream, response.getCode());
}
if (isRedirectCode(response.getCode())) {
String redirectUrl = response.getHeader("Location").getValue();
log.info("Got redirect response to {}", redirectUrl);
try {
return (ResponseT) doInvoke(redirectUrl, request, responseType);
} catch (Exception exception) {
log.error("Can not redirect call to url {}", redirectUrl);
throw new HttpException("Can not redirect call to url " + redirectUrl,
new ResponseFailException(redirectUrl, response.getCode(), exception.getMessage(),
exception));
}
}
JsonFactory jsonFactory = new JsonFactory();
JsonParser jsonParser = jsonFactory.createParser(inputStream);
jsonParser.setCodec(ApiJsonSerializer.getInstance().getObjectMapper());
return jsonParser.readValueAs(responseType);
}
};
}
private CloseableHttpClient buildDefaultHttpClient() {
log.debug("Building http client");
return HttpClients.custom()
.disableAutomaticRetries()
.disableRedirectHandling()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(timeout))
.setResponseTimeout(Timeout.ofSeconds(timeout))
.build())
.build();
}
private boolean isSuccessCode(int httpStatusCode) {
return httpStatusCode == HttpStatus.SC_OK || httpStatusCode == HttpStatus.SC_PARTIAL_CONTENT;
}
private boolean isRedirectCode(int httpStatusCode) {
return httpStatusCode == HttpStatus.SC_MOVED_TEMPORARILY
|| httpStatusCode == HttpStatus.SC_MOVED_PERMANENTLY;
}
private void handleErrorResponse(InputStream responseInputStream, int httpStatusCode)
throws HttpException {
try {
String responseAsString = ApiJsonSerializer.getInstance().getObjectMapper()
.readValue(responseInputStream, String.class);
log.error("Unsuccessful response (code {}) : \n{}", httpStatusCode, responseAsString);
throw new HttpException(responseAsString,
new ResponseFailException(this.url, httpStatusCode, responseAsString, null));
} catch (Exception exception) {
log.error("Can not read unsuccessful response (code {})", httpStatusCode, exception);
throw new HttpException(exception.getMessage(),
new ResponseFailException(this.url, httpStatusCode, exception.getMessage(),
exception));
}
}
/**
* Closes httpClient's connections.
*/
public void disconnect() {
try {
log.debug("Disconnecting http client from {} ...", this.url);
if (httpClient != null) {
httpClient.close();
}
log.info("Disconnected http client from {}", this.url);
} catch (IOException e) {
log.error("Can not disconnect http client from {}", this.url, e);
}
}
}