![JAR search and dependency download from the Maven repository](/logo.png)
com.arm.mbed.cloud.sdk.common.CloudCaller Maven / Gradle / Ivy
package com.arm.mbed.cloud.sdk.common;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import com.arm.mbed.cloud.sdk.annotations.Internal;
import com.arm.mbed.cloud.sdk.annotations.Preamble;
import com.arm.mbed.cloud.sdk.common.GenericAdapter.Mapper;
import okhttp3.Headers;
import okhttp3.Request;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
@Preamble(description = "Utility in charge of calling Arm Mbed Cloud APIs")
@Internal
public class CloudCaller {
private final CloudCall caller;
private final Mapper mapper;
private final SdkLogger logger;
private final String apiName;
private final boolean storeMetadata;
private final AbstractApi module;
/**
* Defines a request to Arm Mbed Cloud.
*
* @param
* type of response object.
*/
public interface CloudCall {
Call call();
}
private CloudCaller(String apiName, CloudCall caller, Mapper mapper, AbstractApi module,
boolean storeMetada) {
super();
this.caller = caller;
this.mapper = mapper;
this.logger = module.logger;
this.apiName = apiName;
this.module = module;
this.storeMetadata = storeMetada;
}
/**
* Executes a call to Arm Mbed Cloud.
*
* Note: call metadata are recorded
*
* @param module
* API module
* @param functionName
* API function name.
* @param mapper
* object mapper
* @param caller
* request
*
* @param
* type of HTTP response object.
* @param
* type of API response object.
* @return request result
* @throws MbedCloudException
* if an error occurred during the call
*/
public static U call(AbstractApi module, String functionName, Mapper mapper, CloudCall caller)
throws MbedCloudException {
return call(module, functionName, mapper, caller, true);
}
/**
* Executes a call to Arm Mbed Cloud.
*
* @param module
* API module
* @param functionName
* API function name.
* @param mapper
* object mapper
* @param caller
* request
* @param storeMetadata
* states whether metadata should be recorded
*
* @param
* type of HTTP response object.
* @param
* type of API response object.
* @return request result
* @throws MbedCloudException
* if an error occurred during the call
*/
public static U call(AbstractApi module, String functionName, Mapper mapper, CloudCall caller,
boolean storeMetadata) throws MbedCloudException {
return callWithFeedback(module, functionName, mapper, caller, storeMetadata).getResult();
}
/**
* Executes a call to Arm Mbed Cloud.
*
* @param module
* API module
* @param functionName
* API function name.
* @param mapper
* object mapper
* @param
* type of HTTP response object.
* @param
* type of API response object.
* @param caller
* request
* @param storeMetadata
* states whether metadata should be recorded
* @return CallFeedback @see {@link CallFeedback}
* @throws MbedCloudException
* if an error occurred during the call
*/
public static CallFeedback callWithFeedback(AbstractApi module, String functionName, Mapper mapper,
CloudCall caller, boolean storeMetadata) throws MbedCloudException {
return new CloudCaller<>(functionName, caller, mapper, module, storeMetadata).execute();
}
/**
* Executes a call to Arm Mbed Cloud.
*
* @return result objects of type U
* @throws MbedCloudException
* if an error occurred during the call
*/
public CallFeedback execute() throws MbedCloudException {
try {
logger.logInfo("Calling Arm Mbed Cloud API: " + apiName);
clearPreviousApiMetadata();
final Response response = caller.call().execute();
final CallFeedback comms = new CallFeedback<>(logger);
comms.setMetadataFromResponse(response);
if (storeMetadata) {
storeApiMetadata(comms.getMetadata());
}
checkResponse(response, comms);
comms.setResultFromResponse(mapper, response);
return comms;
} catch (Exception exception) {
logger.throwSdkException("An error occurred when calling SDK function [" + apiName + "]", exception);
}
return null;
}
private void clearPreviousApiMetadata() {
module.metadataCache.clearMetadata();
}
private void storeApiMetadata(ApiMetadata metadata) {
module.metadataCache.storeMetadata(metadata);
}
private void checkResponse(Response response, CallFeedback comms) throws MbedCloudException {
if (response == null) {
logger.throwSdkException("An error occurred when calling Arm Mbed Cloud: no response was received");
}
if (response != null && !response.isSuccessful()) {
String errorMessage = null;
Error error = null;
try {
error = ErrorJsonConverter.INSTANCE.convert(response.errorBody());
if (comms != null) {
comms.setErrorMessage(error);
}
} catch (Exception exception) {
try {
errorMessage = response.errorBody().string();
} catch (IOException e1) {
// Nothing to do
}
// Nothing to do
}
logger.throwSdkException(
"An error occurred when calling Arm Mbed Cloud: [" + response.code() + "] " + response.message(),
error == null ? errorMessage == null ? null : new MbedCloudException(errorMessage)
: new MbedCloudException(error.toString()));
}
}
private static class ErrorJsonConverter {
private final JsonSerialiser jsonSerialiser = new JsonSerialiser();
public static final ErrorJsonConverter INSTANCE = new ErrorJsonConverter();
private Error convert(ResponseBody value) {
if (value == null) {
return null;
}
return jsonSerialiser.fromJson(value.charStream(), Error.class);
}
}
/**
*
* Defines a call (Metadata + response) of a call to Arm Mbed Cloud.
*
* @param
* type of the result object
*/
public static class CallFeedback {
private final SdkLogger logger;
ApiMetadata metadata;
U result;
/**
* Constructor.
*
* @param logger
* logger
*/
public CallFeedback(SdkLogger logger) {
super();
this.logger = logger;
}
/**
* Gets call metadata.
*
* @see ApiMetadata
* @return the metadata
*/
public ApiMetadata getMetadata() {
return metadata;
}
/**
* Gets call result.
*
* @return the result
*/
public U getResult() {
return result;
}
/**
* Sets call metadata.
*
* @see ApiMetadata
* @param metadata
* the metadata to set
*/
public void setMetadata(ApiMetadata metadata) {
this.metadata = metadata;
}
/**
* Sets call result.
*
* @param result
* the result to set
*/
public void setResult(U result) {
this.result = result;
}
/**
* Sets result from an HTTP response.
*
* @param mapper
* object mapper
* @param
* type of the result
* @param response
* HTTP response
*/
public void setResultFromResponse(Mapper mapper, Response response) {
setResult((mapper == null) ? null : mapper.map(response.body()));
}
/**
* Sets metadata from an HTTP response.
*
* @param
* type of the result
* @param response
* HTTP response
*/
public void setMetadataFromResponse(Response response) {
setMetadata(retrieveMetadata(response));
}
/**
* Sets error message.
*
* @param error
* error message @see Error
*/
public void setErrorMessage(Error error) {
if (metadata != null) {
metadata.setErrorMessage(error);
}
}
private ApiMetadata retrieveMetadata(Response response) {
if (response == null) {
return null;
}
final ApiMetadata callMetadata = new ApiMetadata();
final Request request = response.raw().request();
if (request != null) {
callMetadata.setMethod(request.method());
callMetadata.setUrl(request.url().url());
}
callMetadata.setStatusCode(response.code());
final Headers headers = response.headers();
if (headers != null) {
callMetadata.setHeaders(headers.toMultimap());
callMetadata.setRequestId(headers.get("x-request-id"));
try {
callMetadata.setDateFromString(headers.get("date"));
} catch (Exception exception) {
logger.logError("Error occurred when trying to fetch server date from API metadata", exception);
callMetadata.setDate(new Date());
}
}
final T body = response.body();
if (body != null) {
callMetadata.setObject(body.getClass());
callMetadata.setEtag(fetchEtagField(body));
}
return callMetadata;
}
private String fetchEtagField(T body) {
try {
final Method getEtagMethod = body.getClass().getMethod("getEtag");
if (getEtagMethod != null) {
final Object etag = getEtagMethod.invoke(body);
return (etag == null) ? null : (etag instanceof String) ? (String) etag : etag.toString();
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException exception) {
logger.logError("Error occurred when trying to fetch etag from API metadata", exception);
} catch (NoSuchMethodException exception) {
return null;
}
return null;
}
}
}