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

com.amazonaws.services.iot.client.shadow.AbstractAwsIotDevice Maven / Gradle / Ivy

// Generated by delombok at Tue Jun 13 16:52:50 PDT 2017
/*
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package com.amazonaws.services.iot.client.shadow;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.amazonaws.services.iot.client.AWSIotConfig;
import com.amazonaws.services.iot.client.AWSIotDevice;
import com.amazonaws.services.iot.client.AWSIotDeviceProperty;
import com.amazonaws.services.iot.client.AWSIotException;
import com.amazonaws.services.iot.client.AWSIotMessage;
import com.amazonaws.services.iot.client.AWSIotQos;
import com.amazonaws.services.iot.client.AWSIotTimeoutException;
import com.amazonaws.services.iot.client.AWSIotTopic;
import com.amazonaws.services.iot.client.core.AbstractAwsIotClient;
import com.amazonaws.services.iot.client.shadow.AwsIotDeviceCommandManager.Command;
import com.amazonaws.services.iot.client.shadow.AwsIotDeviceCommandManager.CommandAck;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

/**
 * The actual implementation of {@link AWSIotDevice}.
 */
public abstract class AbstractAwsIotDevice {
    private static final Logger LOGGER = Logger.getLogger(AbstractAwsIotDevice.class.getName());
    protected final String thingName;
    protected long reportInterval = AWSIotConfig.DEVICE_REPORT_INTERVAL;
    protected boolean enableVersioning = AWSIotConfig.DEVICE_ENABLE_VERSIONING;
    protected AWSIotQos deviceReportQos = AWSIotQos.valueOf(AWSIotConfig.DEVICE_REPORT_QOS);
    protected AWSIotQos shadowUpdateQos = AWSIotQos.valueOf(AWSIotConfig.DEVICE_SHADOW_UPDATE_QOS);
    protected AWSIotQos methodQos = AWSIotQos.valueOf(AWSIotConfig.DEVICE_METHOD_QOS);
    protected AWSIotQos methodAckQos = AWSIotQos.valueOf(AWSIotConfig.DEVICE_METHOD_ACK_QOS);
    private final Map reportedProperties;
    private final Map updatableProperties;
    private final AwsIotDeviceCommandManager commandManager;
    private final ConcurrentMap deviceSubscriptions;
    private final ObjectMapper jsonObjectMapper;
    private AbstractAwsIotClient client;
    private Future syncTask;
    private AtomicLong localVersion;

    protected AbstractAwsIotDevice(String thingName) {
        this.thingName = thingName;
        reportedProperties = getDeviceProperties(true, false);
        updatableProperties = getDeviceProperties(false, true);
        commandManager = new AwsIotDeviceCommandManager(this);
        deviceSubscriptions = new ConcurrentHashMap<>();
        for (String topic : getDeviceTopics()) {
            deviceSubscriptions.put(topic, false);
        }
        jsonObjectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(AbstractAwsIotDevice.class, new AwsIotJsonSerializer());
        jsonObjectMapper.registerModule(module);
        localVersion = new AtomicLong(-1);
    }

    protected AbstractAwsIotDevice getDevice() {
        return this;
    }

    protected String get() throws AWSIotException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos);
        return commandManager.runCommandSync(Command.GET, message);
    }

    protected String get(long timeout) throws AWSIotException, AWSIotTimeoutException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos);
        return commandManager.runCommandSync(Command.GET, message, timeout);
    }

    protected void get(AWSIotMessage message, long timeout) throws AWSIotException {
        commandManager.runCommand(Command.GET, message, timeout);
    }

    protected void update(String jsonState) throws AWSIotException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos, jsonState);
        commandManager.runCommandSync(Command.UPDATE, message);
    }

    protected void update(String jsonState, long timeout) throws AWSIotException, AWSIotTimeoutException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos, jsonState);
        commandManager.runCommandSync(Command.UPDATE, message, timeout);
    }

    protected void update(AWSIotMessage message, long timeout) throws AWSIotException {
        commandManager.runCommand(Command.UPDATE, message, timeout);
    }

    protected void delete() throws AWSIotException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos);
        commandManager.runCommandSync(Command.DELETE, message);
    }

    protected void delete(long timeout) throws AWSIotException, AWSIotTimeoutException {
        AWSIotMessage message = new AWSIotMessage(null, methodQos);
        commandManager.runCommandSync(Command.DELETE, message, timeout);
    }

    protected void delete(AWSIotMessage message, long timeout) throws AWSIotException {
        commandManager.runCommand(Command.DELETE, message, timeout);
    }

    protected void onShadowUpdate(String jsonState) {
        // synchronized block to serialize device accesses
        synchronized (this) {
            try {
                AwsIotJsonDeserializer.deserialize(this, jsonState);
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "Failed to update device", e);
            }
        }
    }

    protected String onDeviceReport() {
        // synchronized block to serialize device accesses
        synchronized (this) {
            try {
                return jsonObjectMapper.writeValueAsString(this);
            } catch (JsonProcessingException e) {
                LOGGER.log(Level.WARNING, "Failed to generate device report", e);
                return null;
            }
        }
    }

    public void activate() throws AWSIotException {
        stopSync();
        for (String topic : getDeviceTopics()) {
            AWSIotTopic awsIotTopic;
            if (commandManager.isDeltaTopic(topic)) {
                awsIotTopic = new AwsIotDeviceDeltaListener(topic, shadowUpdateQos, this);
            } else {
                awsIotTopic = new AwsIotDeviceCommandAckListener(topic, methodAckQos, this);
            }
            client.subscribe(awsIotTopic, client.getServerAckTimeout());
        }
        startSync();
    }

    public void deactivate() throws AWSIotException {
        stopSync();
        commandManager.onDeactivate();
        for (String topic : getDeviceTopics()) {
            deviceSubscriptions.put(topic, false);
            AWSIotTopic awsIotTopic = new AWSIotTopic(topic);
            client.unsubscribe(awsIotTopic, client.getServerAckTimeout());
        }
    }

    public boolean isTopicReady(String topic) {
        Boolean status = deviceSubscriptions.get(topic);
        return Boolean.TRUE.equals(status);
    }

    public boolean isCommandReady(Command command) {
        Boolean accepted = deviceSubscriptions.get(commandManager.getTopic(command, CommandAck.ACCEPTED));
        Boolean rejected = deviceSubscriptions.get(commandManager.getTopic(command, CommandAck.REJECTED));
        return (Boolean.TRUE.equals(accepted) && Boolean.TRUE.equals(rejected));
    }

    public void onSubscriptionAck(String topic, boolean success) {
        deviceSubscriptions.put(topic, success);
        commandManager.onSubscriptionAck(topic, success);
    }

    public void onCommandAck(AWSIotMessage message) {
        commandManager.onCommandAck(message);
    }

    protected void startSync() {
        // don't start the publish task if no properties are to be published
        if (reportedProperties.isEmpty() || reportInterval <= 0) {
            return;
        }
        syncTask = client.scheduleRoutineTask(new Runnable() {
            @Override
            public void run() {
                if (!isCommandReady(Command.UPDATE)) {
                    LOGGER.fine("Device not ready for reporting");
                    return;
                }
                long reportVersion = localVersion.get();
                if (enableVersioning && reportVersion < 0) {
                    // if versioning is enabled, synchronize the version first
                    LOGGER.fine("Starting version sync");
                    startVersionSync();
                    return;
                }
                String jsonState = onDeviceReport();
                if (jsonState != null) {
                    LOGGER.fine("Sending device report");
                    sendDeviceReport(reportVersion, jsonState);
                }
            }
        }, 0L, reportInterval);
    }

    protected void stopSync() {
        if (syncTask != null) {
            syncTask.cancel(false);
            syncTask = null;
        }
        localVersion.set(-1);
    }

    protected void startVersionSync() {
        localVersion.set(-1);
        AwsIotDeviceSyncMessage message = new AwsIotDeviceSyncMessage(null, shadowUpdateQos, this);
        try {
            commandManager.runCommand(Command.GET, message, client.getServerAckTimeout(), true);
        } catch (AWSIotTimeoutException e) {
        } catch (
        // async command, shouldn't receive timeout exception
        AWSIotException e) {
            LOGGER.log(Level.WARNING, "Failed to publish version update message", e);
        }
    }

    private void sendDeviceReport(long reportVersion, String jsonState) {
        StringBuilder payload = new StringBuilder("{");
        if (enableVersioning) {
            payload.append("\"version\":").append(reportVersion).append(",");
        }
        payload.append("\"state\":{\"reported\":").append(jsonState).append("}}");
        AwsIotDeviceReportMessage message = new AwsIotDeviceReportMessage(null, shadowUpdateQos, reportVersion, payload.toString(), this);
        if (enableVersioning && reportVersion != localVersion.get()) {
            LOGGER.warning("Local version number has changed, skip reporting for this round");
            return;
        }
        try {
            commandManager.runCommand(Command.UPDATE, message, client.getServerAckTimeout(), true);
        } catch (AWSIotTimeoutException e) {
        } catch (
        // async command, shouldn't receive timeout exception
        AWSIotException e) {
            LOGGER.log(Level.WARNING, "Failed to publish device report message", e);
        }
    }

    private Map getDeviceProperties(boolean enableReport, boolean allowUpdate) {
        Map properties = new HashMap<>();
        for (Field field : this.getClass().getDeclaredFields()) {
            AWSIotDeviceProperty annotation = field.getAnnotation(AWSIotDeviceProperty.class);
            if (annotation == null) {
                continue;
            }
            String propertyName = annotation.name().length() > 0 ? annotation.name() : field.getName();
            if ((enableReport && annotation.enableReport()) || (allowUpdate && annotation.allowUpdate())) {
                properties.put(propertyName, field);
            }
        }
        return properties;
    }

    private List getDeviceTopics() {
        List topics = new ArrayList<>();
        topics.add(commandManager.getTopic(Command.DELTA, null));
        topics.add(commandManager.getTopic(Command.GET, CommandAck.ACCEPTED));
        topics.add(commandManager.getTopic(Command.GET, CommandAck.REJECTED));
        topics.add(commandManager.getTopic(Command.UPDATE, CommandAck.ACCEPTED));
        topics.add(commandManager.getTopic(Command.UPDATE, CommandAck.REJECTED));
        topics.add(commandManager.getTopic(Command.DELETE, CommandAck.ACCEPTED));
        topics.add(commandManager.getTopic(Command.DELETE, CommandAck.REJECTED));
        return topics;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public String getThingName() {
        return this.thingName;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public long getReportInterval() {
        return this.reportInterval;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public boolean isEnableVersioning() {
        return this.enableVersioning;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AWSIotQos getDeviceReportQos() {
        return this.deviceReportQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AWSIotQos getShadowUpdateQos() {
        return this.shadowUpdateQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AWSIotQos getMethodQos() {
        return this.methodQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AWSIotQos getMethodAckQos() {
        return this.methodAckQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public Map getReportedProperties() {
        return this.reportedProperties;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public Map getUpdatableProperties() {
        return this.updatableProperties;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AwsIotDeviceCommandManager getCommandManager() {
        return this.commandManager;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public ConcurrentMap getDeviceSubscriptions() {
        return this.deviceSubscriptions;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public ObjectMapper getJsonObjectMapper() {
        return this.jsonObjectMapper;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AbstractAwsIotClient getClient() {
        return this.client;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public Future getSyncTask() {
        return this.syncTask;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public AtomicLong getLocalVersion() {
        return this.localVersion;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setReportInterval(final long reportInterval) {
        this.reportInterval = reportInterval;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setEnableVersioning(final boolean enableVersioning) {
        this.enableVersioning = enableVersioning;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setDeviceReportQos(final AWSIotQos deviceReportQos) {
        this.deviceReportQos = deviceReportQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setShadowUpdateQos(final AWSIotQos shadowUpdateQos) {
        this.shadowUpdateQos = shadowUpdateQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setMethodQos(final AWSIotQos methodQos) {
        this.methodQos = methodQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setMethodAckQos(final AWSIotQos methodAckQos) {
        this.methodAckQos = methodAckQos;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setClient(final AbstractAwsIotClient client) {
        this.client = client;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setSyncTask(final Future syncTask) {
        this.syncTask = syncTask;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setLocalVersion(final AtomicLong localVersion) {
        this.localVersion = localVersion;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy