All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.api.GeotabApi Maven / Gradle / Ivy
/*
*
* 2020 Copyright (C) Geotab Inc. All rights reserved.
*/
package com.geotab.api;
import static com.geotab.http.invoker.ServerInvoker.DEFAULT_TIMEOUT;
import com.geotab.http.exception.InvalidUserException;
import com.geotab.http.invoker.ServerInvoker;
import com.geotab.http.request.AuthenticatedRequest;
import com.geotab.http.request.BaseRequest;
import com.geotab.http.request.MultiCallRequest;
import com.geotab.http.request.param.Parameters;
import com.geotab.http.response.AuthenticateResponse;
import com.geotab.http.response.BaseResponse;
import com.geotab.model.login.Credentials;
import com.geotab.model.login.LoginResult;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@Slf4j
public class GeotabApi implements Api {
public static final String THIS_SERVER = "ThisServer";
public static final String DEFAULT_SERVER = "my.geotab.com";
public static final String PROTOCOL = "https://";
protected final Credentials credentials;
protected final String server;
protected final int timeout;
protected final AtomicReference loginResultReference = new AtomicReference<>();
protected final AtomicReference serverInvokerReference = new AtomicReference<>();
/**
* Create new api instance.
*
* @param credentials {@link Credentials} used to authenticate.
*/
public GeotabApi(Credentials credentials) {
this(credentials, DEFAULT_SERVER, DEFAULT_TIMEOUT);
}
/**
* Create new api instance.
*
* @param credentials {@link Credentials} used to authenticate.
* @param server Server url without protocol. Example: my.geotab.com
* @param timeout Request timeout
*/
public GeotabApi(Credentials credentials, String server, int timeout) {
this(credentials, server, timeout, null, null);
}
/**
* Create new api instance.
*
* @param credentials {@link Credentials} used to authenticate.
* @param server Server url without protocol. Example: my.geotab.com
* @param timeout Request timeout
* @param servicePath Service path. Default value is: apiv1
* @param httpClient Custom {@link org.apache.hc.client5.http.classic.HttpClient} in case cutom
* configuration is needed
*/
public GeotabApi(Credentials credentials, String server, int timeout, String servicePath,
CloseableHttpClient httpClient) {
log.debug(
"API params: \n credentials = {} \n server = {} \n timeout = {} \n servicePath = {} \n {}",
credentials, server, timeout, servicePath,
httpClient != null ? "custom http client" : "default http client");
if (credentials == null) {
throw new IllegalArgumentException("Credentials not provided");
}
credentials.validate();
this.credentials = credentials;
this.timeout = timeout;
this.server = Optional.ofNullable(server).orElse(DEFAULT_SERVER);
this.serverInvokerReference
.set(buildServerInvoker(PROTOCOL + this.server, this.timeout, servicePath, httpClient));
}
@Override
public LoginResult authenticate() throws Exception {
if (isAuthenticated()) {
return loginResultReference.get();
}
LoginResult loginResult;
if (StringUtils.isNotEmpty(credentials.getSessionId())) {
log.debug("Geotab session id is provided as part of the credentials; "
+ "will not call Geotab Authenticate method");
loginResult = LoginResult.builder()
.credentials(
Credentials.builder()
.database(credentials.getDatabase())
.userName(credentials.getUserName())
.password(null)
.sessionId(credentials.getSessionId())
.build()
)
.path(server)
.build();
loginResultReference.set(loginResult);
} else {
log.debug("Calling Geotab Authenticate method ...");
// Authenticate
BaseRequest extends Parameters> authenticationRequest = BaseRequest.requestBuilder()
.method("Authenticate")
.params(this.credentials)
.build();
loginResult = serverInvokerReference.get()
.invoke(authenticationRequest, AuthenticateResponse.class)
.orElse(null);
loginResultReference.set(loginResult);
log.info("Geotab Authenticate is successful");
}
// Change invoker to use the request URL to that of the db as returned by Authenticate.
if (!THIS_SERVER.equals(loginResultReference.get().getPath())) {
String newPath = buildServerPath();
serverInvokerReference.updateAndGet(serverInvoker -> {
serverInvoker.setUrl(newPath);
return serverInvoker;
});
}
return loginResult;
}
@Override
@SuppressWarnings({"Indentation", "LineLength"})
public , ResponseT extends BaseResponse, ResultT>
Optional call(RequestT request, Class responseType) throws Exception {
log.debug("Method params: \n request = {} \n responseType = {}",
request, responseType);
validateCallParameters(request, responseType);
boolean retry = false;
while (true) {
// Must be authenticated; will auto authenticate if not authenticated.
if ((!request.getCredentials().isPresent() || retry) && !isAuthenticated()) {
authenticate();
}
if (!request.getCredentials().isPresent()) {
LoginResult loginResult = loginResultReference.get();
request.setCredentials(loginResult.getCredentials());
}
// Invoke
try {
return serverInvokerReference.get().invoke(request, responseType);
} catch (InvalidUserException invalidUserException) {
request.setCredentials(null);
loginResultReference.set(null);
if (retry || StringUtils.isEmpty(credentials.getPassword())) {
// If we retried request after a success full re-authentication throw, there is something
// happening between the time we authenticate and try to make a request
// Must have password to login again
log.error(
"Geotab call failed due to authentication; trying to re-authenticate also failed.",
invalidUserException);
throw invalidUserException;
}
// Token may have expired or DB may have moved so try to authenticate and retry request
log.warn("Geotab call failed due to authentication; trying to re-authenticate and re-call");
retry = true;
}
}
}
@Override
@SuppressWarnings("Indentation")
public , ResultT>
Optional multiCall(RequestT request, Class responseType) throws Exception {
log.debug("Method params: \n request = {} \n responseType = {}",
request, responseType);
return call(request, responseType);
}
@Override
public boolean isAuthenticated() {
LoginResult loginResult = loginResultReference.get();
return loginResult != null && loginResult.getCredentials() != null;
}
public void disconnect() {
if (serverInvokerReference.get() != null) {
log.debug("Disconnecting API ...");
serverInvokerReference.get().disconnect();
}
}
@Override
public void close() {
disconnect();
}
protected ServerInvoker buildServerInvoker(String url, Integer timeout, String servicePath,
CloseableHttpClient httpClient) {
return new ServerInvoker(url, timeout, servicePath, httpClient);
}
private String buildServerPath() {
String newPath = PROTOCOL + loginResultReference.get().getPath();
newPath = newPath.endsWith("/") ? newPath.substring(0, newPath.length() - 1) : newPath;
return newPath;
}
@SuppressWarnings("Indentation")
private , ResponseT extends BaseResponse>> void
validateCallParameters(RequestT request, Class responseType) {
if (request == null) {
throw new IllegalArgumentException("Request must be provided");
}
if (responseType == null) {
throw new IllegalArgumentException("Response type must be provided");
}
request.validate();
}
}