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

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; } } }