com.whizzosoftware.hobson.mqtt.MQTTMessageHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.whizzosoftware.hobson.mqtt.MQTTMessageHandler.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Whizzo Software, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package com.whizzosoftware.hobson.mqtt;

import com.whizzosoftware.hobson.api.device.DeviceContext;
import com.whizzosoftware.hobson.api.device.DevicePassport;
import com.whizzosoftware.hobson.api.device.DevicePassportAlreadyActivatedException;
import com.whizzosoftware.hobson.api.device.DevicePassportNotFoundException;
import com.whizzosoftware.hobson.api.plugin.PluginContext;
import com.whizzosoftware.hobson.api.variable.VariableContext;
import com.whizzosoftware.hobson.api.variable.VariableUpdate;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Class responsible for processing incoming MQTT messages. Examples are device registration and device data
 * submission messages.
 *
 * @author Dan Noguerol
 */
public class MQTTMessageHandler {
    private static final Logger logger = LoggerFactory.getLogger(MQTTMessageHandler.class);

    private PluginContext ctx;
    private MQTTMessageSink sink;
    private MQTTEventDelegate delegate;
    private Pattern deviceDataTopicPattern = Pattern.compile("^device/.*/data$");

    /**
     * Constructor.
     *
     * @param sink the transport to use for sending response messages
     * @param delegate the listener to notify of events generated by incoming messages
     */
    public MQTTMessageHandler(PluginContext ctx, MQTTMessageSink sink, MQTTEventDelegate delegate) {
        this.ctx = ctx;
        this.sink = sink;
        this.delegate = delegate;
    }

    /**
     * Called when an MQTT message is received.
     *
     * @param topic the topic the message was received on
     * @param json the JSON payload of the message
     */
    public void onMessage(String topic, JSONObject json) {
        logger.trace("Received MQTT message on topic {}: {}", topic, json);

        if ("bootstrap".equals(topic)) {
            if (json.has("deviceId") && json.has("nonce")) {
                String deviceId = json.getString("deviceId");
                String nonce = json.getString("nonce");

                JSONObject res = new JSONObject();

                try {
                    DevicePassport passport = delegate.activateDevicePassport(deviceId);

                    // alert listener of the device registration
                    delegate.onPassportRegistration(deviceId,
                            json.has("name") ? json.getString("name") : "Unknown MQTT Device",
                            json.has("data")
                                    ? createVariableUpdates(passport.getDeviceId(), json.getJSONObject("data"))
                                    : null);

                    // create JSON response message
                    res.put("id", passport.getId());
                    res.put("secret", passport.getSecret());
                    res.put("topics", createTopicJson(passport.getId()));
                } catch (DevicePassportAlreadyActivatedException e) {
                    res.put("topics", createTopicJson(e.getId()));
                } catch (DevicePassportNotFoundException e) {
                    res.put("error", "Unable to bootstrap device");
                }

                // send response message
                String responseTopic = "bootstrap/" + deviceId + "/" + nonce;
                logger.debug("Registration message detected from {}; sending response on topic {}", deviceId,
                        responseTopic);
                sink.sendMessage(responseTopic, res);
            } else {
                logger.error("Device registration missing device ID or nonce");
            }
        } else if (deviceDataTopicPattern.matcher(topic).matches()) {
            try {
                // alert listener of received data
                int ix = topic.indexOf('/') + 1;
                DevicePassport passport = delegate.getDevicePassport(topic.substring(ix, topic.indexOf('/', ix)));
                if (passport != null) {
                    delegate.onDeviceData(passport.getDeviceId(),
                            createVariableUpdates(passport.getDeviceId(), json));
                } else {
                    logger.error("Received data from device with invalid bootstrap identifier");
                }
            } catch (JSONException e) {
                logger.error("Error parsing device data JSON", e);
            }
        }
    }

    protected Collection<VariableUpdate> createVariableUpdates(String deviceId, JSONObject json) {
        List<VariableUpdate> updates = new ArrayList<>();
        for (Object o : json.keySet()) {
            String key = o.toString();
            updates.add(new VariableUpdate(VariableContext.create(DeviceContext.create(ctx, deviceId), key),
                    json.get(key)));
        }
        return updates;
    }

    private JSONObject createTopicJson(String id) {
        JSONObject topics = new JSONObject();
        topics.put("data", "device/" + id + "/data");
        topics.put("command", "device/" + id + "/command");
        return topics;
    }
}