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

com.arm.mbed.cloud.sdk.Connect Maven / Gradle / Ivy

package com.arm.mbed.cloud.sdk;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.arm.mbed.cloud.sdk.annotations.API;
import com.arm.mbed.cloud.sdk.annotations.Daemon;
import com.arm.mbed.cloud.sdk.annotations.DefaultValue;
import com.arm.mbed.cloud.sdk.annotations.Module;
import com.arm.mbed.cloud.sdk.annotations.NonNull;
import com.arm.mbed.cloud.sdk.annotations.Nullable;
import com.arm.mbed.cloud.sdk.annotations.Preamble;
import com.arm.mbed.cloud.sdk.common.AbstractApi;
import com.arm.mbed.cloud.sdk.common.ApiUtils;
import com.arm.mbed.cloud.sdk.common.Callback;
import com.arm.mbed.cloud.sdk.common.CloudCaller;
import com.arm.mbed.cloud.sdk.common.CloudCaller.CloudCall;
import com.arm.mbed.cloud.sdk.common.ConnectionOptions;
import com.arm.mbed.cloud.sdk.common.JsonSerialiser;
import com.arm.mbed.cloud.sdk.common.MbedCloudException;
import com.arm.mbed.cloud.sdk.common.PageRequester;
import com.arm.mbed.cloud.sdk.common.SynchronousMethod;
import com.arm.mbed.cloud.sdk.common.SynchronousMethod.AsynchronousMethod;
import com.arm.mbed.cloud.sdk.common.TimePeriod;
import com.arm.mbed.cloud.sdk.common.TranslationUtils;
import com.arm.mbed.cloud.sdk.common.listing.ListResponse;
import com.arm.mbed.cloud.sdk.common.listing.Paginator;
import com.arm.mbed.cloud.sdk.common.listing.filtering.Filter;
import com.arm.mbed.cloud.sdk.common.listing.filtering.FilterOperator;
import com.arm.mbed.cloud.sdk.connect.adapters.MetricAdapter;
import com.arm.mbed.cloud.sdk.connect.adapters.PresubscriptionAdapter;
import com.arm.mbed.cloud.sdk.connect.adapters.ResourceAdapter;
import com.arm.mbed.cloud.sdk.connect.adapters.WebhookAdapter;
import com.arm.mbed.cloud.sdk.connect.model.AbstractMetricsListOptions;
import com.arm.mbed.cloud.sdk.connect.model.EndPoints;
import com.arm.mbed.cloud.sdk.connect.model.Metric;
import com.arm.mbed.cloud.sdk.connect.model.MetricsPeriodListOptions;
import com.arm.mbed.cloud.sdk.connect.model.MetricsStartEndListOptions;
import com.arm.mbed.cloud.sdk.connect.model.Presubscription;
import com.arm.mbed.cloud.sdk.connect.model.Resource;
import com.arm.mbed.cloud.sdk.connect.model.Webhook;
import com.arm.mbed.cloud.sdk.connect.notificationhandling.NotificationCache;
import com.arm.mbed.cloud.sdk.devicedirectory.model.Device;
import com.arm.mbed.cloud.sdk.devicedirectory.model.DeviceListOptions;
import com.arm.mbed.cloud.sdk.internal.mds.model.AsyncID;
import com.arm.mbed.cloud.sdk.internal.mds.model.NotificationMessage;
import com.arm.mbed.cloud.sdk.internal.mds.model.PresubscriptionArray;
import com.arm.mbed.cloud.sdk.internal.statistics.model.SuccessfulResponse;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import retrofit2.Call;

@Preamble(description = "Specifies Connect API")
@Module
/**
 * API exposing functionality for doing a range of device related actions:
 * 

* 1) Listing connected devices *

* 2) Exploring and managing resources and resource values on said devices *

* 3) Setup resource subscriptions and webhooks for resource monitoring */ public class Connect extends AbstractApi { private static final String TAG_ON_NOTIFICATION_CALLBACK = "on notification callback"; private static final String TAG_WEBHOOK = "webhook"; private static final Filter CONNECTED_DEVICES_FILTER = new Filter("state", FilterOperator.EQUAL, "registered"); private static final String TAG_RESOURCE = "resource"; private static final String FALSE = "false"; private static final String TAG_RESOURCE_PATH = "resource path"; private static final String TAG_METRIC_OPTIONS = "Metric options"; private static final String TAG_DEVICE_ID = "Device Id"; private static final String TAG_DEVICE = "Device"; private final EndPoints endpoint; private final DeviceDirectory deviceDirectory; private final ExecutorService threadPool; private final NotificationCache cache; /** * Connect module constructor. *

* This module spawns threads for retrieving notifications when daemon is started @see * {@link #startNotifications()}. *

* By default, the executor services in charge are defined and managed internally. It is however possible to specify * your own executors @see #Connect(ConnectionOptions, ExecutorService, ExecutorService). * * @param options * connection options @see {@link ConnectionOptions}. */ public Connect(@NonNull ConnectionOptions options) { this(options, null, null); } /** * Connect module constructor. *

* As opposed to {@link #Connect(ConnectionOptions)} which uses default thread pools for retrieving notifications, * this constructor lets you the possibility to specify the executor services to use. * * @param options * connection options @see {@link ConnectionOptions}. * * @param notificationHandlingThreadPool * Threads in charge of retrieving notifications for a specific resource. If null, a default thread pool * will be created internally. * @param notificationPullingThreadPool * Threads in charge of listening to notifications. The pool can either be a scheduled thread pool or a * fixed thread pool depending on what best suits your system. If null, an internal timer will be created * internally. */ public Connect(@NonNull ConnectionOptions options, @Nullable ExecutorService notificationHandlingThreadPool, @Nullable ExecutorService notificationPullingThreadPool) { super(options); endpoint = new EndPoints(options); deviceDirectory = new DeviceDirectory(options); this.threadPool = (notificationHandlingThreadPool == null) ? Executors.newFixedThreadPool(4) : notificationHandlingThreadPool; this.cache = new NotificationCache(this, (notificationPullingThreadPool == null) ? Executors.newScheduledThreadPool(1) : notificationPullingThreadPool, endpoint); } /** * Starts notification pull. *

* If an external callback is not set up (using `update_webhook`) then calling this function is mandatory to get or * set resources. Unless {@link ConnectionOptions#setAutostartDaemon(boolean)} has been set to true or left as * default. *

* Example: * *

     * {@code
     * connectApi.startNotifications();
     * }
     * 
* * @throws MbedCloudException * if a problem occurred during the process. */ @API @Daemon(task = "Notification pull", start = true) public void startNotifications() throws MbedCloudException { Webhook webhook = null; try { if (isForceClear()) { deleteWebhook(); } } catch (MbedCloudException exception) { // Nothing to do } try { webhook = getWebhook(); } catch (MbedCloudException exception) { // Nothing to do } if (webhook != null) { logger.throwSdkException("A webhook is currently set up [" + webhook + "]. Notification pull cannot be used at the same time. Please remove the webhook if you want to use this mechanism instead."); } cache.startNotificationPull(); } private void autostartDaemonIfNeeded() throws MbedCloudException { if (!cache.isPullingActive() && endpoint.isAutostartDaemon()) { startNotifications(); } } /** * Stops notification pull for notifications. *

* Example: * *

     * {@code
     * connectApi.stopNotifications();
     * }
     * 
* * @throws MbedCloudException * if a problem occurred during the process. */ @API @Daemon(task = "Notification pull", stop = true) public void stopNotifications() throws MbedCloudException { cache.stopNotificationPull(); CloudCaller.call(this, "stopNotification()", null, new CloudCall() { @Override public Call call() { return endpoint.getNotifications().v2NotificationPullDelete(); } }); } /** * Shuts down all daemon services. *

* Example: * *

     * {@code
     * connectApi.shutdownConnectService();
     * }
     * 
*/ @API @Daemon(task = "Notification pull", shutdown = true) public void shutdownConnectService() { cache.shutdown(); threadPool.shutdown(); } /** * Lists connected devices. *

* Example: * *

     * {@code
     * try {
     *     DeviceListOptions options = new DeviceListOptions();
     *
     *     Calendar date = GregorianCalendar(2017,10,30,10,20,56);
     *     options.addCreatedAtFilter(date.getTime(), FilterOperator.GREATER_THAN);
     *     
     *     options.addDeviceTypeFilter("default", FilterOperator.EQUAL);
     *
     *     ListResponse devices = connectApi.listConnectedDevices(options);
     *     for (Device device : devices) {
     *         System.out.println("Device ID: " + device.getId());
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param options * filter options * @return the list of connected devices. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable ListResponse listConnectedDevices(DeviceListOptions options) throws MbedCloudException { return deviceDirectory.listDevicesWithExtraFilters("listConnectedDevices()", options, CONNECTED_DEVICES_FILTER); } /** * Gets an iterator over all connected devices according to filter options. *

* Example: * *

     * {@code
     * try {
     *     DeviceListOptions options = new DeviceListOptions();
     *
     *     Calendar date = GregorianCalendar(2017,10,30,10,20,56);
     *     options.addCreatedAtFilter(date.getTime(), FilterOperator.GREATER_THAN);
     *     
     *     options.addDeviceTypeFilter("default", FilterOperator.EQUAL);
     *
     *     Paginator devices = connectApi.listAllDevices(options);
     *     while (devices.hasNext()) {
     *         Device device = devices.next();
     *         System.out.println("Device ID: " + device.getId());
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param options * filter options. * @return paginator @see {@link Paginator} for the list of devices corresponding to filter options. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Paginator listAllConnectedDevices(@Nullable DeviceListOptions options) throws MbedCloudException { final DeviceListOptions finalOptions = options; return new Paginator<>(new PageRequester() { @Override public ListResponse requestNewPage() throws MbedCloudException { return listConnectedDevices(finalOptions); } }); } /** * Lists device's resources. *

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     device.setId("015f4ac587f500000000000100100249");
     *
     *     List resources = connectApi.listResources(device);
     *     for (Resource resource : resources) {
     *         System.out.println("Resource path: " + resource.getPath());
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param device * Device. * @return list of resources present on a device. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable List listResources(@NonNull Device device) throws MbedCloudException { checkNotNull(device, TAG_DEVICE); checkNotNull(device.getId(), TAG_DEVICE_ID); final String finalDeviceId = device.getId(); return CloudCaller.call(this, "listResources()", ResourceAdapter.getListMapper(finalDeviceId), new CloudCall>() { @Override public Call> call() { return endpoint.getEndpoints().v2EndpointsDeviceIdGet(finalDeviceId); } }); } /** * Lists device's observable resources. * * @see Resource#isObservable() *

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     device.setId("015f4ac587f500000000000100100249");
     *
     *     List resources = connectApi.listObservableResources(device);
     *     for (Resource resource : resources) {
     *         System.out.println("Resource path: " + resource.getPath());
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     *      
* * @param device * Device. * @return list of observable resources present on a device. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable List listObservableResources(@NonNull Device device) throws MbedCloudException { final List resources = listResources(device); if (resources == null || resources.isEmpty()) { return null; } final List observableResources = new LinkedList<>(); for (final Resource resource : resources) { if (resource.isObservable()) { observableResources.add(resource); } } return observableResources.isEmpty() ? null : observableResources; } /** * Gets device's resource. *

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     device.setId("015f4ac587f500000000000100100249");
     *
     *     String resourcePath = "/3201/0/5853";
     *
     *     Resource resource = connectApi.getResource(device, resourcePath);
     *     System.out.println("Confirmed resource path: " + resource.getPath());
     *     assert resourcePath == resource.getPath();
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param device * Device. * @param resourcePath * Path of the resource to get * @return resource present on the device. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Resource getResource(@NonNull Device device, @NonNull String resourcePath) throws MbedCloudException { checkNotNull(device, TAG_DEVICE); checkNotNull(device.getId(), TAG_DEVICE_ID); checkNotNull(resourcePath, TAG_RESOURCE_PATH); final List resources = listResources(device); if (resources == null || resources.isEmpty()) { return null; } for (final Resource resource : resources) { if (ApiUtils.comparePaths(resourcePath, resource.getPath())) { return resource; } } return null; } /** * Lists a device's subscriptions. *

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     device.setId("015f4ac587f500000000000100100249");
     *
     *     List subscriptions = connectApi.listDeviceSubscriptions(device);
     *     for (String subscription : subscriptions) {
     *         System.out.println("Device subscription: " + subscription);
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param device * Device. * @return list of subscriptions * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable List listDeviceSubscriptions(@NonNull Device device) throws MbedCloudException { checkNotNull(device, TAG_DEVICE); checkNotNull(device.getId(), TAG_DEVICE_ID); final String finalDeviceId = device.getId(); return CloudCaller.call(this, "listDeviceSubscriptions()", PresubscriptionAdapter.getResourcePathListMapper(), new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDeviceIdGet(finalDeviceId); } }); } /** * Lists metrics. *

* Example: * *

     * {@code
     * try {
     *     Calendar startDate = GregorianCalendar(2017,10,30,10,20,56);
     *     Calendar endDate = GregorianCalendar(2017,11,31,10,20,56);
     * 
     *     MetricsStartEndListOptions listOptions = new MetricsStartEndListOptions();
     *     listOptions.setStart(startDate.getTime());
     *     listOptions.setEnd(endDate.getTime());
     *     listOptions.setInterval(new TimePeriod(360)); //Once an hour
     *
     *     List metrics = connectApi.listMetrics(listOptions);
     *     for (Metric metric : metrics) {
     *         System.out.println("Time: " + dateFormat.format(metric.getTimestamp()));
     *         System.out.println("Successful bootstraps: " + metric.getSuccessfulBootstraps());
     *         System.out.println("Successful api calls: " + metric.getSuccessfulApiCalls());
     *         System.out.println("");
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param options * metrics options. * @param * Type of metrics list options * @return list of metrics for the corresponding options. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable List listMetrics(@NonNull T options) throws MbedCloudException { checkNotNull(options, TAG_METRIC_OPTIONS); final T finalOptions = options; final Date finalStart = options instanceof MetricsStartEndListOptions ? ((MetricsStartEndListOptions) options).getStart() : null; final Date finalEnd = options instanceof MetricsStartEndListOptions ? ((MetricsStartEndListOptions) options).getEnd() : null; final String finalPeriod = options instanceof MetricsPeriodListOptions ? ((MetricsPeriodListOptions) options).getPeriod().toString() : null; return CloudCaller.call(this, "listMetrics()", MetricAdapter.getListMapper(), new CloudCall() { @Override public Call call() { return endpoint.getStatistic().v3MetricsGet( MetricAdapter.mapIncludes(finalOptions.getInclude()), finalOptions.getInterval().toString(), TranslationUtils.toLocalDate(finalStart), TranslationUtils.toLocalDate(finalEnd), finalPeriod, finalOptions.getLimit(), finalOptions.getAfter(), finalOptions.getOrder().toString()); } }); } /** * Gets a resource value for a given device id and resource path. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Future futureLedPattern = connectApi.getResourceValueAsync(deviceId, resourcePath, false, false);
     *     String ledPattern = (String)futureLedPattern.get();
     *     System.out.println("LED pattern from device: " + ledPattern);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param deviceId
     *            The name/id of the device.
     * @param resourcePath
     *            The resource path to get.
     * @param cacheOnly
     *            If true, the response will come only from the cache.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to obtain resource value.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future getResourceValueAsync(@NonNull String deviceId, @NonNull String resourcePath,
            @DefaultValue(value = FALSE) boolean cacheOnly, @DefaultValue(value = FALSE) boolean noResponse)
            throws MbedCloudException {
        checkNotNull(deviceId, TAG_DEVICE_ID);
        checkNotNull(resourcePath, TAG_RESOURCE_PATH);
        final String finalDeviceId = deviceId;
        final String finalResourcePath = resourcePath;
        final boolean finalCacheOnly = cacheOnly;
        final boolean finalNoResponse = noResponse;
        autostartDaemonIfNeeded();
        return cache.fetchAsyncResponse(threadPool, "getResourceValueAsync()", new CloudCall() {

            @SuppressWarnings("boxing")
            @Override
            public Call call() {
                return endpoint.getResources().v2EndpointsDeviceIdResourcePathGet(finalDeviceId,
                        ApiUtils.normalisePath(finalResourcePath), finalCacheOnly, finalNoResponse);
            }
        });

    }

    /**
     * Gets a resource value for a given device id and resource path.
     * 

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     device.setId("015f4ac587f500000000000100100249");     
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = connectApi.getResource(device, resourcePath);
    
     *     Future futureLedPattern = connectApi.getResourceValueAsync(resource, false, false);
     *     String ledPattern = (String)futureLedPattern.get();
     *     System.out.println("LED pattern from device: " + ledPattern);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param resource
     *            The resource to get the value of.
     * @param cacheOnly
     *            If true, the response will come only from the cache.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to obtain resource value.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future getResourceValueAsync(@NonNull Resource resource,
            @DefaultValue(value = FALSE) boolean cacheOnly, @DefaultValue(value = FALSE) boolean noResponse)
            throws MbedCloudException {
        checkNotNull(resource, TAG_RESOURCE);
        return getResourceValueAsync(resource.getDeviceId(), resource.getPath(), cacheOnly, noResponse);

    }

    /**
     * Gets a resource value for a given device id and resource path.
     * 

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String ledPattern = String.valueOf(connectApi.getResourceValue(deviceId, resourcePath, false, false, new TimePeriod(5)));
     *     System.out.println("LED pattern from device: " + ledPattern);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param deviceId * The name/id of the device. * @param resourcePath * The resource path to get. * @param cacheOnly * If true, the response will come only from the cache. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return resource value. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object getResourceValue(@NonNull String deviceId, @NonNull String resourcePath, @DefaultValue(value = FALSE) boolean cacheOnly, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { final String id = deviceId; final String path = resourcePath; final boolean fromCache = cacheOnly; final boolean waitForResponse = noResponse; try { return SynchronousMethod.waitForCompletion(new AsynchronousMethod() { @Override public Future submit() throws MbedCloudException { return getResourceValueAsync(id, path, fromCache, waitForResponse); } }, timeout); } catch (MbedCloudException exception) { logger.throwSdkException(exception); } return null; } /** * Gets a resource value for a given device id and resource path. *

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String ledPattern = String.valueOf(connectApi.getResourceValue(resource, false, false, new TimePeriod(5)));
     *     System.out.println("LED pattern from device: " + ledPattern);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * The resource to get the value of. * @param cacheOnly * If true, the response will come only from the cache. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return resource value. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object getResourceValue(@NonNull Resource resource, @DefaultValue(value = FALSE) boolean cacheOnly, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); return getResourceValue(resource.getDeviceId(), resource.getPath(), cacheOnly, noResponse, timeout); } /** * Gets a resource value for a given device id and resource path. *

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String ledPattern = String.valueOf(connectApi.getResourceValue(resource, new TimePeriod(5)));
     *     System.out.println("LED pattern from device: " + ledPattern);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * * @param resource * The resource path to get the value of. * @param timeout * Timeout for the request. * @return resource value. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object getResourceValue(@NonNull Resource resource, @Nullable TimePeriod timeout) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); return getResourceValue(resource, false, false, timeout); } /** * Sets the value of a resource. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String resourceValue = "500:500:500";
     * 
     *     Future futureLedPattern = connectApi.setResourceValueAsync(deviceId, resourcePath, resourceValue, false);
     *     String setValue = (String)futureLedPattern.get();
     *     assert setValue == resourceValue;
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param deviceId
     *            The name/id of the device.
     * @param resourcePath
     *            The resource path to get.
     * @param resourceValue
     *            value to set.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to set the value.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future setResourceValueAsync(@NonNull String deviceId, @NonNull String resourcePath,
            @Nullable String resourceValue, @DefaultValue(value = FALSE) boolean noResponse) throws MbedCloudException {
        checkNotNull(deviceId, TAG_DEVICE_ID);
        checkNotNull(resourcePath, TAG_RESOURCE_PATH);
        final String finalDeviceId = deviceId;
        final String finalResourcePath = resourcePath;
        final String finalResourceValue = (resourceValue == null) ? null : resourceValue;
        final boolean finalNoResponse = noResponse;
        autostartDaemonIfNeeded();
        return cache.fetchAsyncResponse(threadPool, "setResourceValueAsync()", new CloudCall() {

            @SuppressWarnings("boxing")
            @Override
            public Call call() {
                return endpoint.getResources().v2EndpointsDeviceIdResourcePathPut(finalDeviceId,
                        ApiUtils.normalisePath(finalResourcePath), finalResourceValue, finalNoResponse);
            }
        });
    }

    /**
     * Sets the value of a resource.
     * 

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String resourceValue = "500:500:500";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     Future futureLedPattern = connectApi.setResourceValueAsync(resource, resourceValue, false);
     *     String setValue = (String)futureLedPattern.get();
     *     assert setValue == resourceValue;
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param resource
     *            The resource to set the value of.
     * @param resourceValue
     *            value to set.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to set the value.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future setResourceValueAsync(@NonNull Resource resource, @Nullable String resourceValue,
            @DefaultValue(value = FALSE) boolean noResponse) throws MbedCloudException {
        checkNotNull(resource, TAG_RESOURCE);
        return setResourceValueAsync(resource.getDeviceId(), resource.getPath(), resourceValue, noResponse);
    }

    /**
     * Sets the value of a resource.
     * 

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String resourceValue = "500:500:500";
     * 
     *     Object resultObject = connectApi.setResourceValue(deviceId, resourcePath, resourceValue, false, new TimePeriod(5));
     *     String setValue = (String)resultObject;
     *     assert setValue == resourceValue;
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param deviceId * The name/id of the device. * @param resourcePath * The resource path to get. * @param resourceValue * value to set. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return The value of the new resource. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object setResourceValue(@NonNull String deviceId, @NonNull String resourcePath, @Nullable String resourceValue, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { final String id = deviceId; final String path = resourcePath; final String value = resourceValue; final boolean waitForResponse = noResponse; try { return SynchronousMethod.waitForCompletion(new AsynchronousMethod() { @Override public Future submit() throws MbedCloudException { return setResourceValueAsync(id, path, value, waitForResponse); } }, timeout); } catch (MbedCloudException exception) { logger.throwSdkException(exception); } return null; } /** * Sets the value of a resource. *

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String resourceValue = "500:500:500";
     * 
     *     Object resultObject = connectApi.setResourceValue(resource, resourceValue, new TimePeriod(5));
     *     String setValue = (String)resultObject;
     *     assert setValue == resourceValue;
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * The resource to set the value of. * @param resourceValue * value to set. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return The value of the new resource. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object setResourceValue(@NonNull Resource resource, @Nullable String resourceValue, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); return setResourceValue(resource.getDeviceId(), resource.getPath(), resourceValue, noResponse, timeout); } /** * Sets the value of a resource. *

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String resourceValue = "500:500:500";
     * 
     *     Object resultObject = connectApi.setResourceValue(resource, resourceValue, new TimePeriod(5));
     *     String setValue = (String)resultObject;
     *     assert setValue == resourceValue;
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * The resource to set the value of. * @param resourceValue * value to set. * @param timeout * Timeout for the request. * @return The value of the new resource. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object setResourceValue(@NonNull Resource resource, @Nullable String resourceValue, @Nullable TimePeriod timeout) throws MbedCloudException { return setResourceValue(resource, resourceValue, false, timeout); } /** * Executes a function on a resource. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String functionName = null;
     * 
     *     Future resultObject = connectApi.executeResourceAsync(deviceId, resourcePath, functionName, false);
     *     String resultValue = (String)resultObject.get();
     *     System.out.println("Result from the function executed: " + resultValue);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param deviceId
     *            The name/id of the device.
     * @param resourcePath
     *            The resource path to get.
     * @param functionName
     *            The function to trigger.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to get the value returned from the function executed on the resource.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future executeResourceAsync(@NonNull String deviceId, @NonNull String resourcePath,
            @Nullable String functionName, @DefaultValue(value = FALSE) boolean noResponse) throws MbedCloudException {
        checkNotNull(deviceId, TAG_DEVICE_ID);
        checkNotNull(resourcePath, TAG_RESOURCE_PATH);
        final String finalDeviceId = deviceId;
        final String finalResourcePath = resourcePath;
        // Body parameter value must not be null.
        final String finalFunctionName = (functionName == null) ? "" : functionName;
        final boolean finalNoResponse = noResponse;
        autostartDaemonIfNeeded();
        return cache.fetchAsyncResponse(threadPool, "executeResourceAsync()", new CloudCall() {

            @SuppressWarnings("boxing")
            @Override
            public Call call() {
                return endpoint.getResources().v2EndpointsDeviceIdResourcePathPost(finalDeviceId,
                        ApiUtils.normalisePath(finalResourcePath), finalFunctionName, finalNoResponse);
            }
        });
    }

    /**
     * Executes a function on a resource.
     * 

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String functionName = null;
     * 
     *     Future resultObject = connectApi.executeResourceAsync(resource, functionName, false);
     *     String resultValue = (String)resultObject.get();
     *     System.out.println("Result from the function executed: " + resultValue);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param resource
     *            The resource to execute the function on.
     * @param functionName
     *            The function to trigger.
     * @param noResponse
     *            If true, mbed Device Connector will not wait for a response.
     * @return A Future from which it is possible to get the value returned from the function executed on the resource.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public @Nullable Future executeResourceAsync(@NonNull Resource resource, @Nullable String functionName,
            @DefaultValue(value = FALSE) boolean noResponse) throws MbedCloudException {
        checkNotNull(resource, TAG_RESOURCE);
        return executeResourceAsync(resource.getDeviceId(), resource.getPath(), functionName, noResponse);
    }

    /**
     * Executes a function on a resource.
     * 

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     String functionName = null;
     * 
     *     Object resultObject = connectApi.executeResource(deviceId, resourcePath, functionName, false, new TimePeriod(5));
     *     System.out.println("Result from the function executed: " + (String)resultObject);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param deviceId * The name/id of the device. * @param resourcePath * The resource path to get. * @param functionName * The function to trigger. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return the value returned from the function executed on the resource. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object executeResource(@NonNull String deviceId, @NonNull String resourcePath, @Nullable String functionName, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { final String id = deviceId; final String path = resourcePath; final String function = functionName; final boolean waitForResponse = noResponse; try { return SynchronousMethod.waitForCompletion(new AsynchronousMethod() { @Override public Future submit() throws MbedCloudException { return executeResourceAsync(id, path, function, waitForResponse); } }, timeout); } catch (MbedCloudException exception) { logger.throwSdkException(exception); } return null; } /** * Executes a function on a resource. *

* Note: Waits if necessary for the computation to complete, and then retrieves its result. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3201/0/5853";
     *     Resource resource = new Resource(deviceId, resourcePath);
     *     String functionName = null;
     * 
     *     Object resultObject = connectApi.executeResource(resource, functionName, false, new TimePeriod(5));
     *     System.out.println("Result from the function executed: " + (String)resultObject);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * The resource path to execute the function on. * @param functionName * The function to trigger. * @param noResponse * If true, mbed Device Connector will not wait for a response. * @param timeout * Timeout for the request. * @return the value returned from the function executed on the resource. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Object executeResource(@NonNull Resource resource, @Nullable String functionName, @DefaultValue(value = FALSE) boolean noResponse, @Nullable TimePeriod timeout) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); return executeResource(resource.getDeviceId(), resource.getPath(), functionName, noResponse, timeout); } /** * Lists pre-subscription data. *

* Example: * *

     * {@code
     * try {
     *     List presubscriptions = connectApi.listPresubscriptions();
     *     for (Presubscription presub : presubscriptions) {
     *         System.out.println("Device (" + presub.getDeviceId() + ") has subscriptions to resources: ");
     *         for (String resource : presub.getResourcePaths()) {
     *             System.out.println(resource);
     *         }
     *     }
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @return the list of pre-subscription data. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable List listPresubscriptions() throws MbedCloudException { return CloudCaller.call(this, "listPresubscriptions()", PresubscriptionAdapter.getListMapper(), new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsGet(); } }); } /** * Updates pre-subscription data. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *
     *     Presubscription presub1 = new Presubscription();
     *     presub1.setDeviceType("default");
     *     List resourceList1 = Arrays.asList("/3201/0/5850", "/3201/0/5853");
     *     presub1.setResourcePaths(resourceList1);
     *
     *     Presubscription presub2 = new Presubscription();
     *     presub2.setDeviceId(deviceId);
     *     List resourceList2 = Arrays.asList("/3200/0/5501");
     *     presub2.setResourcePaths(resourceList2);
     *
     *     List presubscriptions = Arrays.asList(presub1, presub2);
     *     connectApi.updatePresubscriptions(presubscriptions);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param presubscriptions * The pre-subscription list to update. *

* If you send an empty/null array, the pre-subscription data will be removed @see * {@link #deletePresubscriptions()} for similar action. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void updatePresubscriptions(@Nullable List presubscriptions) throws MbedCloudException { final List finalList = presubscriptions; final PresubscriptionArray array = PresubscriptionAdapter.reverseMapList(finalList); CloudCaller.call(this, "updatePresubscriptions()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsPut(array); } }); } /** * Deletes pre-subscription data. *

* Note: this method will deregister all subscription callbacks or observers if any. *

* Example: * *

     * {@code
     * try {
     *     connectApi.deletePresubscriptions();
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void deletePresubscriptions() throws MbedCloudException { CloudCaller.call(this, "deletePresubscriptions()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsPut(PresubscriptionAdapter.reverseMapList(null)); } }); deregisterAllResourceSubscriptionObserversOrCallbacks(); } /** * Removes all subscriptions. *

* Note: this method will deregister all subscription callbacks or observers if any. *

* Example: * *

     * {@code
     * try {
     *     connectApi.deleteSubscribers();
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void deleteSubscribers() throws MbedCloudException { CloudCaller.call(this, "deleteSubscriptions()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDelete(); } }); deregisterAllResourceSubscriptionObserversOrCallbacks(); } /** * Deletes a device's subscriptions. *

* Note: this method will deregister all subscription callbacks or observers for this device if any. *

* Example: * *

     * {@code
     * try {
     *     Device device = new Device();
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     device.setId(deviceId);
     *     connectApi.deleteDeviceSubscriptions(device);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param device * Device to consider. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void deleteDeviceSubscriptions(@NonNull Device device) throws MbedCloudException { checkNotNull(device, TAG_DEVICE); checkNotNull(device.getId(), TAG_DEVICE_ID); final String finalDeviceId = device.getId(); CloudCaller.call(this, "deletePresubscriptions()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDeviceIdDelete(finalDeviceId); } }); deregisterAllResourceSubscriptionObserversOrCallbacks(device); } /** * Gets the status of a resource's subscription. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3200/0/5501";
     *     Resource buttonResource = new Resource(deviceId, resourcePath);
     * 
     *     boolean subscribed = connectApi.getResourceSubscription(buttonResource);
     *     System.out.println("Is " + deviceId + " subscribed to: " + resourcePath + "? " + (subscribed ? "yes" : "no"));
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * resource * @return true if resource is subscribed. false otherwise. * @throws MbedCloudException * if a parameter is incorrect * * */ @API public boolean getResourceSubscription(@NonNull Resource resource) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); final Resource finalResource = resource; try { CloudCaller.call(this, "getResourceSubscription()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDeviceIdResourcePathGet( finalResource.getDeviceId(), ApiUtils.normalisePath(finalResource.getPath())); } }); return true; } catch (MbedCloudException exception) { return false; } } /** * Allows notifications (received from a Webhook) to be injected into the notifications system. *

* Example: * *

     * {@code
     * try {
     *       String deviceId = "015f4ac587f500000000000100100249";
     *       String resourcePath = "/3200/0/5501";         
     *       String payload ="Q2hhbmdlIG1lIQ==";
     *       
     *       NotificationData notification = new NotificationData();
     *       notification.setEp(deviceId);
     *       notification.setPath(resourcePath);
     *       notification.setPayload(payload);
     *       NotificationMessage notifications = new NotificationMessage();
     *       notifications.addNotificationsItem(notification);
     *       Resource resource = new Resource(deviceId, resourcePath);
     *       api.createResourceSubscriptionObserver(resource, BackpressureStrategy.BUFFER)
     *               .subscribe(new Consumer() {
     * 
     *                   @Override
     *                   public void accept(Object t) throws Exception {
     *                       log("Received notification value", t);
     *                   }
     *               });    
     *       api.notify(notifications);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param data
     *            The notification data to inject
     */
    @API
    public void notify(@Nullable NotificationMessage data) {
        cache.notify(data);
    }

    /**
     * Allows notifications expressed as a JSON string to be injected into the notifications system.
     * 

* Example: * *

     * {@code
     * try {
     *       String deviceId = "015f4ac587f500000000000100100249";
     *       String resourcePath = "/3200/0/5501";         
     *       String payload ="Q2hhbmdlIG1lIQ==";
     *       String notifications = "{\"async-responses\":[{\"payload\":\"MQ\u003d\u003d\",\"id\":\"sfjasldfjl\"}],\"notifications\""
     *       +":[{\"path\":\"/3200/0/5501\",\"payload\":\"MQ\u003d\u003d\",\"ep\":\"015f4ac587f500000000000100100249\"}]}";
     *       
     *       Resource resource = new Resource(deviceId, resourcePath);
     *       api.createResourceSubscriptionObserver(resource, BackpressureStrategy.BUFFER)
     *               .subscribe(new Consumer() {
     * 
     *                   @Override
     *                   public void accept(Object t) throws Exception {
     *                       log("Received notification value", t);
     *                   }
     *               });    
     *       api.notify(notifications);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param dataAsJson
     *            The notification data to inject as JSON String.
     */
    @API
    public void notify(@Nullable String dataAsJson) {
        notify(null, dataAsJson);
    }

    /**
     * Allows a notification to be injected into the notifications system.
     * 

* Example: * *

     * {@code
     * try {
     *       String deviceId = "015f4ac587f500000000000100100249";
     *       String resourcePath = "/3200/0/5501";         
     *       String payload ="Q2hhbmdlIG1lIQ==";
     *       JSONSerialiser jsonSerialiser = new JSONSerialiser();
     *       String notifications = "{\"async-responses\":[{\"payload\":\"MQ\u003d\u003d\",\"id\":\"sfjasldfjl\"}],\"notifications\""
     *       +":[{\"path\":\"/3200/0/5501\",\"payload\":\"MQ\u003d\u003d\",\"ep\":\"015f4ac587f500000000000100100249\"}]}";
     *       
     *       Resource resource = new Resource(deviceId, resourcePath);
     *       api.createResourceSubscriptionObserver(resource, BackpressureStrategy.BUFFER)
     *               .subscribe(new Consumer() {
     * 
     *                   @Override
     *                   public void accept(Object t) throws Exception {
     *                       log("Received notification value", t);
     *                   }
     *               });    
     *       api.notify(jsonSerialiser, notifications);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     * 
     * @param deserialiser
     *            JSON deserialiser to use.
     * @param dataAsJson
     *            The notification data to inject as JSON String.
     */
    @API
    public void notify(@Nullable JsonSerialiser deserialiser, @Nullable String dataAsJson) {
        final JsonSerialiser jsonEngine = (deserialiser == null) ? new JsonSerialiser() : deserialiser;
        notify(jsonEngine.fromJson(dataAsJson, NotificationMessage.class));
    }

    /**
     * Subscribes to a resource.
     * 

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3200/0/5501";
     *     Resource buttonResource = new Resource(deviceId, resourcePath);
     * 
     *     connectApi.addResourceSubscription(buttonResource);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * resource to subscribe to. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void addResourceSubscription(@NonNull Resource resource) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); final Resource finalResource = resource; CloudCaller.call(this, "addResourceSubscription()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDeviceIdResourcePathPut(finalResource.getDeviceId(), ApiUtils.normalisePath(finalResource.getPath())); } }); } /** * Subscribes to a resource and associates callbacks. *

* Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3200/0/5501";
     *     Resource buttonResource = new Resource(deviceId, resourcePath);
     *     Callback callback = new Callback() {
     * 
     *         @Override
     *         public void execute(Object arg) {
     *             System.out.println("Just received a notification from " + device.getId() + " regarding " + path
     *                     + ": " + String.valueOf(arg));
     * 
     *         }
     *     };
     *     connectApi.addResourceSubscription(resource,callback, null);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
     *
     * @param resource
     *            resource to subscribe to.
     * @param onNotification
     *            callback to execute on notification.
     * @param onFailure
     *            callback to execute on error.
     * @throws MbedCloudException
     *             if a problem occurred during request processing.
     */
    @API
    public void addResourceSubscription(@NonNull Resource resource, @NonNull Callback onNotification,
            @Nullable Callback onFailure) throws MbedCloudException {
        registerResourceSubscriptionCallback(resource, onNotification, onFailure);
        addResourceSubscription(resource);
    }

    /**
     * Subscribes to a resource and creates a related observer.
     * 

* Note: for more information about observers @see Reactive X or * RxJava * *

* Example: * *

     * {
     * {@code String resourcePath = "/3200/0/5501";
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     Resource resource = new Resource(deviceId, path);
     *     connectApi.addResourceSubscription(resource, BackpressureStrategy.BUFFER)
     *                   .subscribe(System.out::println);
     * }
     * 
* * @param resource * resource to subscribe to. * @param strategy * backpressure strategy to apply @see {@link BackpressureStrategy} * @return Observable which can be subscribed to. @see {@link Flowable} * @throws MbedCloudException * if a problem occurred during request processing. */ @API public @Nullable Flowable addResourceSubscription(@NonNull Resource resource, @Nullable @DefaultValue("BUFFER") BackpressureStrategy strategy) throws MbedCloudException { final Flowable observer = createResourceSubscriptionObserver(resource, strategy); addResourceSubscription(resource); return observer; } /** * Registers a subscription callback for a resource. *

* Example: * *

     * {
     * {@code String resourcePath = "/3200/0/5501";
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     Resource resource = new Resource(deviceId, path);
     *     Callback callback = new Callback() {
     * 
     *         @Override
     *         public void execute(Object arg) {
     *             System.out.println("Just received a notification from " + device.getId() + " regarding " + path
     *                     + ": " + String.valueOf(arg));
     * 
     *         }
     *     };
     *     connectApi.registerResourceSubscriptionCallback(resource, callback, null);
     *     connectApi.addResourceSubscription(resource);
     * }
     * 
     * 
     * @param resource
     *            resource to register the callback for.
     * @param onNotification
     *            callback to execute on notification.
     * @param onFailure
     *            callback to execute on error.
     * @throws MbedCloudException
     *             if an error occurred in the process.
     */
    @API
    public void registerResourceSubscriptionCallback(@NonNull Resource resource,
            @NonNull Callback onNotification, @Nullable Callback onFailure)
            throws MbedCloudException {
        checkNotNull(resource, TAG_RESOURCE);
        checkModelValidity(resource, TAG_RESOURCE);
        checkNotNull(onNotification, TAG_ON_NOTIFICATION_CALLBACK);
        cache.registerSubscriptionCallback(resource, onNotification, onFailure);
    }

    /**
     * Deregisters the subscription callback of a resource.
     * 

* Example: * *

     * 
     * {
     *     @code
     *     String resourcePath = "/3200/0/5501";
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     Resource resource = new Resource(deviceId, path);
     *     connectApi.deregisterResourceSubscriptionCallback(resource);
     * }
     * 
* * @param resource * resource to consider. * @throws MbedCloudException * if an error occurred in the process. */ @API public void deregisterResourceSubscriptionCallback(@NonNull Resource resource) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); cache.deregisterNotificationSubscriptionCallback(resource); } /** * Creates an observer for resource subscriptions. *

* Note: for more information about observers @see Reactive X or * RxJava * *

* Example: * *

     * {
     * {@code String resourcePath = "/3200/0/5501";
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     Resource resource = new Resource(deviceId, path);
     *     connectApi.createResourceSubscriptionObserver(resource, BackpressureStrategy.BUFFER)
     *                   .subscribe(System.out::println);
     *     connectApi.addResourceSubscription(resource);
     * }
     * 
* * @param resource * resource to subscribe to. * @param strategy * backpressure strategy to apply @see {@link BackpressureStrategy} * @return Observable which can be subscribed to. @see {@link Flowable} * @throws MbedCloudException * if an error occurred in the process. */ @API public @Nullable Flowable createResourceSubscriptionObserver(@NonNull Resource resource, @Nullable @DefaultValue("BUFFER") BackpressureStrategy strategy) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); final BackpressureStrategy finalStrategy = (strategy == null) ? BackpressureStrategy.BUFFER : strategy; return cache.createResourceSubscriptionObserver(resource, finalStrategy); } /** * Removes the subscription observer of a resource. *

* Example: * *

     *      
     * {@code String resourcePath = "/3200/0/5501"
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     Resource resource = new Resource(deviceId, path);
     *     connectApi.removeResourceSubscriptionObserver(resource);
     * }
     * 
* * @param resource * resource to consider. * @throws MbedCloudException * if an error occurred in the process. */ @API public void removeResourceSubscriptionObserver(@NonNull Resource resource) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); cache.removeResourceSubscriptionObserver(resource); } /** * Deregisters all subscription observers or callbacks for a device. *

* Example: * *

     *      
     * {@code String deviceId = "015f4ac587f500000000000100100249"
     *     Device device = new Device();
     *     device.setId(deviceId);
     *     connectApi.deregisterAllResourceSubscriptionObserversOrCallbacks(device);
     * }
     * 
* * @param device * device to consider. * @throws MbedCloudException * if an error occurred in the process. */ @API public void deregisterAllResourceSubscriptionObserversOrCallbacks(@NonNull Device device) throws MbedCloudException { checkNotNull(device, TAG_DEVICE); checkNotNull(device.getId(), TAG_DEVICE_ID); cache.deregisterAllResourceSubscriptionObserversOrCallbacks(device.getId()); } /** * Deregisters all subscription observers or callbacks. *

* Example: * *

     *      
     * {@code 
     *     connectApi.deregisterAllResourceSubscriptionObserversOrCallbacks();
     * }
     * 
*/ @API public void deregisterAllResourceSubscriptionObserversOrCallbacks() { cache.deregisterAllResourceSubscriptionObserversOrCallbacks(); } /** * Deletes a resource's subscription. *

* Note: this method will deregister all subscription callbacks or observers for this resource if any. *

* * Example: * *

     * {@code
     * try {
     *     String deviceId = "015f4ac587f500000000000100100249";
     *     String resourcePath = "/3200/0/5501";
     *     Resource buttonResource = new Resource(deviceId, resourcePath);
     * 
     *     connectApi.deleteResourceSubscription(buttonResource);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param resource * resource to subscribe to. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void deleteResourceSubscription(@NonNull Resource resource) throws MbedCloudException { checkNotNull(resource, TAG_RESOURCE); checkModelValidity(resource, TAG_RESOURCE); final Resource finalResource = resource; CloudCaller.call(this, "deleteResourceSubscription()", null, new CloudCall() { @Override public Call call() { return endpoint.getSubscriptions().v2SubscriptionsDeviceIdResourcePathDelete( finalResource.getDeviceId(), ApiUtils.normalisePath(finalResource.getPath())); } }); deregisterResourceSubscriptionCallback(finalResource); removeResourceSubscriptionObserver(finalResource); } /** * Gets the current callback URL if it exists. *

* Example: * *

     * {@code
     * try {
     *     Webhook webhook = connectApi.getWebhook();
     *     System.out.println("Webhook URL: " + webhook.getUrl());
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @return the webhook. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public Webhook getWebhook() throws MbedCloudException { return CloudCaller.call(this, "getWebhook()", WebhookAdapter.getMapper(), new CloudCall() { @Override public Call call() { return endpoint.getWebhooks().v2NotificationCallbackGet(); } }); } /** * Registers new webhook for incoming subscriptions. *

* Example: * *

     * {@code
     * try {
     *     Webhook webhook = new Webhook();
     *     webhook.setUrl("https://goo.gl/testwh");
     *     webhook.addHeader("Auth","token");
     *     connectApi.updateWebhook(webhook);
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @param webhook * Webhook to set. * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void updateWebhook(@NonNull Webhook webhook) throws MbedCloudException { checkNotNull(webhook, TAG_WEBHOOK); checkModelValidity(webhook, TAG_WEBHOOK); if (isForceClear()) { stopNotifications(); } final Webhook finalWebhook = webhook; CloudCaller.call(this, "updateWebhook()", null, new CloudCall() { @Override public Call call() { return endpoint.getNotifications().v2NotificationCallbackPut(WebhookAdapter.reverseMap(finalWebhook)); } }); } /** * Deletes the callback data (effectively stopping Arm Mbed Cloud Connect from putting notifications). *

* If no webhook is registered, an exception (404) will be raised. *

* Note that every registered subscription will be deleted as part of deregistering a webhook. *

* Example: * *

     * {@code
     * try {
     *     connectApi.deleteWebhook();
     * } catch (MbedCloudException e) {
     *     e.printStackTrace();
     * }
     * }
     * 
* * @throws MbedCloudException * if a problem occurred during request processing. */ @API public void deleteWebhook() throws MbedCloudException { CloudCaller.call(this, "deleteWebhook()", null, new CloudCall() { @Override public Call call() { return endpoint.getWebhooks().v2NotificationCallbackDelete(); } }); } /** * States whether any existing notification channel should be cleared before a new one is created. * * @return True if the channel will be cleared. False otherwise. */ public boolean isForceClear() { return endpoint.isForceClear(); } /** * Sets whether any existing notification channel should be cleared before a new one is created. * * @param forceClear * True if the channel will be cleared. False otherwise. */ public void setForceClear(boolean forceClear) { endpoint.setForceClear(forceClear); } /** * States whether notification daemon will start automatically when needed. *

* Note: to change this behaviour, use {@link ConnectionOptions#setAutostartDaemon(boolean)} when initialising this * API. * * @return true if daemon will be started automatically. False otherwise. */ public boolean isAutostartDaemon() { return endpoint.isAutostartDaemon(); } /** * Retrieves module name. * * @return module name. */ @Override public String getModuleName() { return "Connect"; } }