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

joynr.messaging.mqtt.SharedMqttClient.js Maven / Gradle / Ivy

/*jslint es5: true */

/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2016 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.
 * #L%
 */

define(
        "joynr/messaging/mqtt/SharedMqttClient",
        [
            "global/Promise",
            "global/Mqtt",
            "joynr/messaging/JoynrMessage",
            "joynr/messaging/MessagingQosEffort",
            "joynr/util/JSONSerializer",
            "joynr/util/LongTimer",
            "joynr/util/Typing",
            "joynr/system/LoggerFactory"
        ],
        function(Promise, Mqtt, JoynrMessage, MessagingQosEffort, JSONSerializer, LongTimer, Typing, LoggerFactory) {
            var log = LoggerFactory.getLogger("joynr.messaging.mqtt.SharedMqttClient");

            /**
             * @param {mqtt}
             *            client to use for sending messages
             * @param {Array}
             *            queuedMessages
             */
            function sendQueuedMessages(client, queuedMessages) {
                var queued, topic;
                while (queuedMessages.length) {
                    queued = queuedMessages.shift();
                    try {
                        client.publish(queued.topic, JSONSerializer.stringify(queued.message), queued.options);
                        queued.resolve();
                        // Error is thrown if the connection is no longer open
                    } catch (e) {
                        // so add the message back to the front of the queue
                        queuedMessages.unshift(queued);
                        throw e;
                    }
                }
            }

            function sendQueuedUnsubscriptions(client, queuedUnsubscriptions) {
                var i, topic;
                for (i = 0; i < queuedUnsubscriptions.length; i++) {
                    client.unsubscribe(queuedUnsubscriptions[i]);
                }
                queuedUnsubscriptions = [];
            }

            function sendQueuedSubscriptions(client, queuedSubscriptions, qosLevel) {
                return new Promise(function(resolve, reject) {
                    var i, topic, subscribeObject = {};
                    for (i = 0; i < queuedSubscriptions.length; i++) {
                        topic = queuedSubscriptions[i];
                        subscribeObject[topic] = qosLevel;
                    }
                    client.subscribe(subscribeObject, undefined, function(err, granted) {
                        //TODO error handling
                        queuedSubscriptions = [];
                        resolve();
                    });
                });
            }

            function sendMessage(client, topic, joynrMessage, sendQosLevel, queuedMessages) {
                return new Promise(function(resolve, reject){
                    try {
                        client.publish(topic, JSONSerializer.stringify(joynrMessage), { qos : sendQosLevel });
                        resolve();
                        // Error is thrown if the socket is no longer open, so requeue to the front
                    } catch (e) {
                        // add the message back to the front of the queue
                        queuedMessages.unshift({
                            message : joynrMessage,
                            resolve : resolve,
                            options : {
                                qos : sendQosLevel,
                            },
                            topic : topic
                        });
                        throw e;
                    }
                });
            }

            /**
             * @name SharedMqttClient
             * @constructor
             * @param {Object}
             *            settings
             * @param {MqttAddress}
             *            settings.address to be used to connect to mqtt broker
             */
            var SharedMqttClient =
                    function SharedMqttClient(settings) {
                        Typing.checkProperty(settings, "Object", "settings");
                        Typing.checkProperty(
                                settings.address,
                                "MqttAddress",
                                "settings.address");

                        var client = null;
                        var address = settings.address;
                        var onmessageCallback = null;
                        var queuedMessages = [];
                        var onOpen;
                        var resolve;
                        var closed = false;
                        var connected = false;
                        var onConnectedPromise = new Promise(function(resolveTrigger) {
                            resolve = resolveTrigger;
                        });
                        var qosLevel = SharedMqttClient.DEFAULT_QOS_LEVEL;

                        if (settings.provisioning.qosLevel !== undefined) {
                            qosLevel = settings.provisioning.qosLevel;
                        }

                        var queuedSubscriptions = [];
                        var queuedUnsubscriptions = [];

                        var onMessage = function(topic, payload) {
                            if (onmessageCallback !== undefined) {
                                onmessageCallback(topic, new JoynrMessage(JSON.parse(payload.toString())));
                            }
                        };

                        var resetConnection = function resetConnection() {
                            if (closed) {
                                return;
                            }
                            client = new Mqtt.connect(address.brokerUri);
                            client.on('connect', onOpen);
                            client.on('message', onMessage);
                        };

                        this.onConnected = function() {
                            return onConnectedPromise;
                        };

                        // send all queued messages, requeuing to the front in case of a problem
                        onOpen = function onOpen() {
                            try {
                                connected = true;
                                sendQueuedMessages(client, queuedMessages);
                                sendQueuedUnsubscriptions(client, queuedUnsubscriptions);
                                sendQueuedSubscriptions(client, queuedSubscriptions, qosLevel).then(resolve);
                            } catch (e) {
                                resetConnection();
                            }
                        };

                        resetConnection();

                        /**
                         * @name SharedMqttClient#send
                         * @function
                         * @param {String}
                         *            topic the topic to publish the message
                         * @param {JoynrMessage}
                         *            joynrMessage the joynr message to transmit
                         */
                        this.send = function send(topic, joynrMessage) {
                            var sendQosLevel = qosLevel;
                            if (MessagingQosEffort.BEST_EFFORT === joynrMessage.effort) {
                                sendQosLevel = SharedMqttClient.BEST_EFFORT_QOS_LEVEL;
                            }

                            return sendMessage(client, topic, joynrMessage, sendQosLevel, queuedMessages).catch(function(e1) {
                                    resetConnection();
                                });
                        };

                        /**
                         * @name SharedMqttClient#close
                         * @function
                         */
                        this.close = function close() {
                            closed = true;
                            if (client !== null && client.end) {
                                client.end();
                            }
                        };

                        this.subscribe = function(topic) {
                            if (connected) {
                                client.subscribe(topic, { qos : qosLevel} );
                            } else {
                                queuedSubscriptions.push(topic);
                            }
                        };

                        this.unsubscribe = function(topic) {
                            if (connected) {
                                client.unsubscribe(topic);
                            } else {
                                queuedUnsubscriptions.push(topic);
                            }
                        };

                        // using the defineProperty syntax for onmessage to be able to keep
                        // the same API as WebSocket but have a setter function called when
                        // the attribute is set.
                        Object.defineProperty(this, "onmessage", {
                            set : function(newCallback) {
                                onmessageCallback = newCallback;
                                if (typeof newCallback !== "function") {
                                    throw new Error(
                                            "onmessage callback must be a function, but instead was of type "
                                                + typeof newCallback);
                                }
                            },
                            get : function() {
                                return onmessageCallback;
                            },
                            enumerable : false,
                            configurable : false
                        });

                    };

            SharedMqttClient.DEFAULT_QOS_LEVEL = 1;
            SharedMqttClient.BEST_EFFORT_QOS_LEVEL = 0;

            return SharedMqttClient;
        });




© 2015 - 2025 Weber Informatics LLC | Privacy Policy