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

joynr.dispatching.subscription.SubscriptionManager.js Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/*jslint es5: true, node: true, node: true */
/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 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%
 */
var Promise = require("../../../global/Promise");
var MessagingQos = require("../../messaging/MessagingQos");
var MulticastWildcardRegexFactory = require("../../messaging/util/MulticastWildcardRegexFactory");
var defaultMessagingSettings = require("../../start/settings/defaultMessagingSettings");
var SubscriptionQos = require("../../proxy/SubscriptionQos");
var SubscriptionStop = require("../types/SubscriptionStop");
var SubscriptionRequest = require("../types/SubscriptionRequest");
var MulticastSubscriptionRequest = require("../types/MulticastSubscriptionRequest");
var BroadcastSubscriptionRequest = require("../types/BroadcastSubscriptionRequest");
var SubscriptionListener = require("./SubscriptionListener");
var SubscriptionUtil = require("./util/SubscriptionUtil");
var LongTimer = require("../../util/LongTimer");
var LoggerFactory = require("../../system/LoggerFactory");
var uuid = require("../../../lib/uuid-annotated");
var Util = require("../../util/UtilInternal");
var Typing = require("../../util/Typing");
var TypeRegistrySingleton = require("../../../joynr/types/TypeRegistrySingleton");
var PublicationMissedException = require("../../exceptions/PublicationMissedException");
var JSONSerializer = require("../../util/JSONSerializer");
/**
 * @name SubscriptionManager
 * @constructor
 * @param {Dispatcher}
 *            dispatcher
 */
function SubscriptionManager(dispatcher) {
    var multicastWildcardRegexFactory = new MulticastWildcardRegexFactory();
    var log = LoggerFactory.getLogger("joynr.dispatching.subscription.SubscriptionManager");
    var typeRegistry = TypeRegistrySingleton.getInstance();
    if (!(this instanceof SubscriptionManager)) {
        // in case someone calls constructor without new keyword (e.g. var c =
        // Constructor({..}))
        return new SubscriptionManager(dispatcher);
    }

    // stores subscriptionId - SubscriptionInfo pairs
    var subscriptionInfos = {};
    // stores subscriptionId - SubscriptionListener
    var subscriptionListeners = {};
    // stores the object which is returned by setTimeout mapped to the subscriptionId
    var publicationCheckTimerIds = {};
    var subscriptionReplyCallers = {};
    var started = true;

    var multicastSubscribers = {};

    function isReady() {
        return started;
    }

    /**
     * @param {String}
     *            subscriptionId Id of the subscription to check
     * @returns {Number} time of last received publication
     */
    function getLastPublicationTime(subscriptionId) {
        return subscriptionInfos[subscriptionId].lastPublicationTime_ms;
    }

    function setLastPublicationTime(subscriptionId, time_ms) {
        subscriptionInfos[subscriptionId].lastPublicationTime_ms = time_ms;
    }

    /**
     * @param {String}
     *            subscriptionId Id of the subscription to check
     * @param {Number}
     *            delay_ms Delay to the next publication check.
     * @returns {Boolean} true if subscription is expired, false if end date is not reached.
     */
    function subscriptionEnds(subscriptionId, delay_ms) {
        if (subscriptionInfos[subscriptionId] === undefined) {
            log.warn('subscriptionEnds has been called with unresolved subscriptionId "' + subscriptionId + '"');
            return true;
        }
        var expiryDateMs = subscriptionInfos[subscriptionId].qos.expiryDateMs;
        // log.debug("Checking subscription end for subscriptionId: " + subscriptionId + "
        // expiryDateMs: " + expiryDateMs + "
        // current time: " + Date.now());
        var ends = expiryDateMs <= Date.now() + delay_ms;
        // if (ends === true) {
        // log.info("Subscription end date reached for id: " + subscriptionId);
        // }
        return ends;
    }

    /**
     * @param {String}
     *            subscriptionId Id of the subscription to check
     * @param {Number}
     *            alertAfterIntervalMs maximum delay between two incoming publications
     */
    function checkPublication(subscriptionId, alertAfterIntervalMs) {
        var subscriptionListener = subscriptionListeners[subscriptionId];
        var timeSinceLastPublication = Date.now() - getLastPublicationTime(subscriptionId);
        // log.debug("timeSinceLastPublication : " + timeSinceLastPublication + "
        // alertAfterIntervalMs: " + alertAfterIntervalMs);
        if (alertAfterIntervalMs > 0 && timeSinceLastPublication >= alertAfterIntervalMs) {
            // log.warn("publication missed for subscription id: " + subscriptionId);
            if (subscriptionListener.onError) {
                var publicationMissedException = new PublicationMissedException({
                    detailMessage: "alertAfterIntervalMs period exceeded without receiving publication",
                    subscriptionId: subscriptionId
                });
                subscriptionListener.onError(publicationMissedException);
            }
        }

        var delay_ms;
        if (timeSinceLastPublication > alertAfterIntervalMs) {
            delay_ms = alertAfterIntervalMs;
        } else {
            delay_ms = alertAfterIntervalMs - timeSinceLastPublication;
        }

        function checkPublicationDelay() {
            checkPublication(subscriptionId, alertAfterIntervalMs);
        }

        if (!subscriptionEnds(subscriptionId, delay_ms)) {
            // log.debug("Rescheduling checkPublication with delay: " + delay_ms);
            publicationCheckTimerIds[subscriptionId] = LongTimer.setTimeout(checkPublicationDelay, delay_ms);
        }
    }

    function calculateTtl(subscriptionQos) {
        var ttl;
        if (subscriptionQos.expiryDateMs === SubscriptionQos.NO_EXPIRY_DATE) {
            return defaultMessagingSettings.MAX_MESSAGING_TTL_MS;
        }
        ttl = subscriptionQos.expiryDateMs - Date.now();
        if (ttl > defaultMessagingSettings.MAX_MESSAGING_TTL_MS) {
            return defaultMessagingSettings.MAX_MESSAGING_TTL_MS;
        }
        return ttl;
    }

    function storeSubscriptionRequest(settings, subscriptionRequest) {
        var onReceiveWrapper;
        if (settings.attributeType !== undefined) {
            onReceiveWrapper = function(response) {
                settings.onReceive(Typing.augmentTypes(response[0], typeRegistry, settings.attributeType));
            };
        } else {
            onReceiveWrapper = function(response) {
                var responseKey;
                for (responseKey in response) {
                    if (response.hasOwnProperty(responseKey)) {
                        response[responseKey] = Typing.augmentTypes(
                            response[responseKey],
                            typeRegistry,
                            settings.broadcastParameter[responseKey].type
                        );
                    }
                }
                settings.onReceive(response);
            };
        }
        subscriptionListeners[subscriptionRequest.subscriptionId] = new SubscriptionListener({
            onReceive: onReceiveWrapper,
            onError: settings.onError,
            onSubscribed: settings.onSubscribed
        });
        var subscriptionInfo = Util.extend(
            {
                proxyId: settings.proxyId,
                providerDiscoveryEntry: settings.providerDiscoveryEntry,
                lastPublicationTime_ms: 0
            },
            subscriptionRequest
        );

        subscriptionInfos[subscriptionRequest.subscriptionId] = subscriptionInfo;

        var alertAfterIntervalMs = subscriptionRequest.qos.alertAfterIntervalMs;
        if (alertAfterIntervalMs !== undefined && alertAfterIntervalMs > 0) {
            publicationCheckTimerIds[subscriptionRequest.subscriptionId] = LongTimer.setTimeout(
                function checkPublicationAlertAfterInterval() {
                    checkPublication(subscriptionRequest.subscriptionId, alertAfterIntervalMs);
                },
                alertAfterIntervalMs
            );
        }
    }

    function removeRequestFromMulticastSubscribers(multicastId, subscriptionId) {
        var i, multicastIdPattern, subscribers;
        for (multicastIdPattern in multicastSubscribers) {
            if (multicastSubscribers.hasOwnProperty(multicastIdPattern)) {
                subscribers = multicastSubscribers[multicastIdPattern];
                for (i = 0; i < subscribers.length; i++) {
                    if (subscribers[i] === subscriptionId) {
                        subscribers.splice(i, 1);
                        if (subscribers.length === 0) {
                            delete multicastSubscribers[multicastIdPattern];
                        }
                    }
                }
            }
        }
    }

    function cleanupSubscription(subscriptionId) {
        if (subscriptionInfos[subscriptionId] !== undefined) {
            var subscriptionInfo = subscriptionInfos[subscriptionId];
            if (subscriptionInfo.multicastId !== undefined) {
                removeRequestFromMulticastSubscribers(subscriptionInfo.multicastId, subscriptionId);
            }
            delete subscriptionInfos[subscriptionId];
        }
        if (subscriptionListeners[subscriptionId] !== undefined) {
            delete subscriptionListeners[subscriptionId];
        }
        if (subscriptionReplyCallers[subscriptionId] !== undefined) {
            delete subscriptionReplyCallers[subscriptionId];
        }
    }

    /**
     * This callback is called when a publication is received
     * @callback SubscriptionManager~onReceive
     * @param {Object} publication received
     */
    /**
     * This callback is called if there was an error with the subscription
     * @callback SubscriptionManager~onError
     * @param {Error} error
     */

    /**
     * @name SubscriptionManager#registerSubscription
     * @function
     * @param {Object}
     *            settings
     * @param {String}
     *            settings.proxyId participantId of the sender
     * @param {DiscoveryEntryWithMetaInfo}
     *            settings.providerDiscoveryEntry DiscoveryEntry of the receiver
     * @param {String}
     *            settings.attributeType the type of the subscribing attribute
     * @param {String}
     *            settings.attributeName the attribute name to subscribe to
     * @param {SubscriptionQos}
     *            [settings.qos] the subscriptionQos
     * @param {String}
     *            settings.subscriptionId optional parameter subscriptionId to reuse a
     *            preexisting identifier for this concrete subscription request
     * @param {SubscriptionManager~onReceive}
     *            settings.onReceive the callback for received publications.
     * @param {SubscriptionManager~onError}
     *            settings.onError the callback for missing publication alerts or when an
     *            error occurs.
     * @param {SubscriptionManager~onSubscribed}
     *            settings.onSubscribed the callback to inform once the subscription request has
     *            been delivered successfully
     * @returns an A promise object which provides the subscription token upon success and
     *          an error upon failure
     */
    this.registerSubscription = function registerSubscription(settings) {
        if (!isReady()) {
            return Promise.reject(new Error("SubscriptionManager is already shut down"));
        }
        var subscriptionId = settings.subscriptionId || uuid();
        // log.debug("Registering Subscription Id " + subscriptionId);

        if (settings.attributeName === undefined) {
            return Promise.reject(
                new Error(
                    "Error: attributeName not provided in call to registerSubscription, settings = " +
                        JSON.stringify(settings)
                )
            );
        }
        if (settings.attributeType === undefined) {
            return Promise.reject(
                new Error(
                    "Error: attributeType not provided in call to registerSubscription, settings = " +
                        JSON.stringify(settings)
                )
            );
        }

        if (settings.onError === undefined) {
            log.warn(
                'Warning: subscription for attribute "' +
                    settings.attributeName +
                    '" has been done without error callback function. You will not be informed about missed publications. Please specify the "onError" parameter while subscribing!'
            );
        }
        if (settings.onReceive === undefined) {
            log.warn(
                'Warning: subscription for attribute "' +
                    settings.attributeName +
                    '" has been done without receive callback function. You will not be informed about incoming publications. Please specify the "onReceive" parameter while subscribing!'
            );
        }
        var subscriptionRequest = new SubscriptionRequest({
            subscriptionId: subscriptionId,
            subscribedToName: settings.attributeName,
            qos: settings.qos
        });

        var messagingQos = new MessagingQos({
            ttl: calculateTtl(subscriptionRequest.qos)
        });

        var deferred = Util.createDeferred();

        subscriptionReplyCallers[subscriptionId] = {
            resolve: deferred.resolve,
            reject: deferred.reject
        };

        storeSubscriptionRequest(settings, subscriptionRequest);

        function sendSubscriptionRequestCatcher(error) {
            cleanupSubscription(subscriptionId);
            if (settings.onError) {
                settings.onError(error);
            }
            deferred.reject(error);
            return deferred.promise;
        }

        dispatcher
            .sendSubscriptionRequest({
                from: settings.proxyId,
                toDiscoveryEntry: settings.providerDiscoveryEntry,
                messagingQos: messagingQos,
                subscriptionRequest: subscriptionRequest
            })
            .catch(sendSubscriptionRequestCatcher);

        return deferred.promise;
    };

    function addRequestToMulticastSubscribers(multicastId, subscriptionId) {
        var i, subscribers;
        var multicastIdPattern = multicastWildcardRegexFactory.createIdPattern(multicastId);
        if (multicastSubscribers[multicastIdPattern] === undefined) {
            multicastSubscribers[multicastIdPattern] = [];
        }
        subscribers = multicastSubscribers[multicastIdPattern];
        for (i = 0; i < subscribers.length; i++) {
            if (subscribers[i] === subscriptionId) {
                return;
            }
        }
        subscribers.push(subscriptionId);
    }

    function createBroadcastSubscriptionRequest(parameters) {
        var i, request;
        if (parameters.selective) {
            request = new BroadcastSubscriptionRequest({
                subscriptionId: parameters.subscriptionId || uuid(),
                subscribedToName: parameters.broadcastName,
                qos: parameters.subscriptionQos,
                filterParameters: parameters.filterParameters
            });
        } else {
            request = new MulticastSubscriptionRequest({
                multicastId: SubscriptionUtil.createMulticastId(
                    parameters.providerDiscoveryEntry.participantId,
                    parameters.broadcastName,
                    parameters.partitions
                ),
                subscriptionId: parameters.subscriptionId || uuid(),
                subscribedToName: parameters.broadcastName,
                qos: parameters.subscriptionQos
            });
            addRequestToMulticastSubscribers(request.multicastId, request.subscriptionId);
        }
        return request;
    }

    /**
     * @name SubscriptionManager#registerBroadcastSubscription
     * @function
     * @param {Object}
     *            parameters
     * @param {String}
     *            parameters.proxyId participantId of the sender
     * @param {DiscoveryEntryWithMetaInfo}
     *            parameters.providerDiscoveryEntry DiscoveryEntry of the receiver
     * @param {String}
     *            parameters.broadcastName the name of the broadcast being subscribed to
     * @param {String[]}
     *            parameters.broadcastParameter the parameter meta information of the broadcast being subscribed to
     * @param {SubscriptionQos}
     *            [parameters.subscriptionQos] the subscriptionQos
     * @param {BroadcastFilterParameters}
     *            [parameters.filterParameters] filter parameters used to indicate interest in
     *            only a subset of broadcasts that might be sent.
     * @param {Boolean}
     *            parameters.selective true if broadcast is selective
     * @param {String[]}
     *            [parameters.partitions] partitions for multicast requests
     * @param {String}
     *            parameters.subscriptionId optional parameter subscriptionId to reuse a
     *            pre-existing identifier for this concrete subscription request
     * @param {SubscriptionManager~onReceive}
     *            parameters.onReceive is called when a broadcast is received.
     * @param {SubscriptionManager~onError}
     *            parameters.onError is called when an error occurs with the broadcast
     * @param {SubscriptionManager~onSubscribed}
     *            parameters.onSubscribed the callback to inform once the subscription request has
     *            been delivered successfully
     * @returns a promise object which provides the subscription token upon success and an error
     *          upon failure
     */
    this.registerBroadcastSubscription = function(parameters) {
        var messagingQos;

        if (!isReady()) {
            return Promise.reject(new Error("SubscriptionManager is already shut down"));
        }

        var deferred = Util.createDeferred();
        var subscriptionRequest = createBroadcastSubscriptionRequest(parameters);

        messagingQos = new MessagingQos({
            ttl: calculateTtl(subscriptionRequest.qos)
        });

        subscriptionReplyCallers[subscriptionRequest.subscriptionId] = {
            resolve: deferred.resolve,
            reject: deferred.reject
        };

        storeSubscriptionRequest(parameters, subscriptionRequest);

        function sendBroadcastSubscriptionRequestOnError(error) {
            cleanupSubscription(subscriptionRequest.subscriptionId);
            if (parameters.onError) {
                parameters.onError(error);
            }
            deferred.reject(error);
            return deferred.promise;
        }

        dispatcher
            .sendBroadcastSubscriptionRequest({
                from: parameters.proxyId,
                toDiscoveryEntry: parameters.providerDiscoveryEntry,
                messagingQos: messagingQos,
                subscriptionRequest: subscriptionRequest
            })
            .catch(sendBroadcastSubscriptionRequestOnError);

        return deferred.promise;
    };

    /**
     * @name SubscriptionManager#handleSubscriptionReply
     * @function
     * @param subscriptionReply
     *            {SubscriptionReply} incoming subscriptionReply
     */
    this.handleSubscriptionReply = function handleSubscriptionReply(subscriptionReply) {
        var subscriptionReplyCaller = subscriptionReplyCallers[subscriptionReply.subscriptionId];
        var subscriptionListener = subscriptionListeners[subscriptionReply.subscriptionId];

        if (subscriptionReplyCaller === undefined && subscriptionListener === undefined) {
            log.error(
                "error handling subscription reply, because subscriptionReplyCaller and subscriptionListener could not be found: " +
                    JSONSerializer.stringify(subscriptionReply, undefined, 4)
            );
            return;
        }

        try {
            if (subscriptionReply.error) {
                if (!(subscriptionReply.error instanceof Error)) {
                    subscriptionReply.error = Typing.augmentTypes(subscriptionReply.error, typeRegistry);
                }
                if (subscriptionReplyCaller !== undefined) {
                    subscriptionReplyCaller.reject(subscriptionReply.error);
                }
                if (subscriptionListener !== undefined && subscriptionListener.onError !== undefined) {
                    subscriptionListener.onError(subscriptionReply.error);
                }
                cleanupSubscription(subscriptionReply.subscriptionId);
            } else {
                if (subscriptionReplyCaller !== undefined) {
                    subscriptionReplyCaller.resolve(subscriptionReply.subscriptionId);
                }
                if (subscriptionListener !== undefined && subscriptionListener.onSubscribed !== undefined) {
                    subscriptionListener.onSubscribed(subscriptionReply.subscriptionId);
                }
                delete subscriptionReplyCallers[subscriptionReply.subscriptionId];
            }
        } catch (e) {
            log.error(
                "exception thrown during handling subscription reply " +
                    JSONSerializer.stringify(subscriptionReply, undefined, 4) +
                    ":\n" +
                    e.stack
            );
            delete subscriptionReplyCallers[subscriptionReply.subscriptionId];
        }
    };

    /**
     * @name SubscriptionManager#handleMulticastPublication
     * @function
     * @param publication
     *            {MulticastPublication} incoming multicast publication
     */
    this.handleMulticastPublication = function handleMulticastPublication(publication) {
        var i,
            multicastIdPattern,
            subscribers,
            subscribersFound = false;
        for (multicastIdPattern in multicastSubscribers) {
            if (multicastSubscribers.hasOwnProperty(multicastIdPattern)) {
                if (publication.multicastId.match(new RegExp(multicastIdPattern)) !== null) {
                    subscribers = multicastSubscribers[multicastIdPattern];
                    if (subscribers !== undefined) {
                        subscribersFound = true;
                        for (i = 0; i < subscribers.length; i++) {
                            var subscriptionListener = subscriptionListeners[subscribers[i]];
                            if (publication.error) {
                                if (subscriptionListener.onError) {
                                    subscriptionListener.onError(publication.error);
                                } else {
                                    log.debug(
                                        'subscriptionListener with Id "' +
                                            subscribers[i] +
                                            '" has no onError callback. Skipping error publication'
                                    );
                                }
                            } else if (publication.response) {
                                if (subscriptionListener.onReceive) {
                                    subscriptionListener.onReceive(publication.response);
                                } else {
                                    log.debug(
                                        'subscriptionListener with Id "' +
                                            subscribers[i] +
                                            '" has no onReceive callback. Skipping multicast publication'
                                    );
                                }
                            }
                        }
                    }
                }
            }
        }
        if (!subscribersFound) {
            throw new Error(
                "Publication cannot be handled, as no subscription with " +
                    "multicastId " +
                    publication.multicastId +
                    " is known."
            );
        }
    };

    /**
     * @name SubscriptionManager#handlePublication
     * @function
     * @param publication
     *            {SubscriptionPublication} incoming publication
     */
    this.handlePublication = function handlePublication(publication) {
        if (subscriptionInfos[publication.subscriptionId] === undefined) {
            throw new Error(
                "Publication cannot be handled, as no subscription with " +
                    "subscriptionId " +
                    publication.subscriptionId +
                    " is known."
            );
        }
        setLastPublicationTime(publication.subscriptionId, Date.now());
        var subscriptionListener = subscriptionListeners[publication.subscriptionId];
        if (publication.error) {
            if (subscriptionListener.onError) {
                subscriptionListener.onError(publication.error);
            } else {
                log.debug(
                    'subscriptionListener with Id "' +
                        publication.subscriptionId +
                        '" has no onError callback. Skipping error publication'
                );
            }
        } else if (publication.response) {
            if (subscriptionListener.onReceive) {
                subscriptionListener.onReceive(publication.response);
            } else {
                log.debug(
                    'subscriptionListener with Id "' +
                        publication.subscriptionId +
                        '" has no onReceive callback. Skipping publication'
                );
            }
        }
    };

    /**
     * @name SubscriptionManager#unregisterSubscription
     * @function
     * @param {Object}
     *            settings
     * @param {MessagingQos}
     *            settings.messagingQos the messaging Qos object for the ttl
     * @param {String}
     *            settings.subscriptionId of the subscriptionId to stop
     * @returns {Object} A promise object
     */
    this.unregisterSubscription = function unregisterSubscription(settings) {
        if (!isReady()) {
            throw new Error("SubscriptionManager is already shut down");
        }

        var subscriptionInfo = subscriptionInfos[settings.subscriptionId];
        var errorMessage;
        if (subscriptionInfo === undefined) {
            errorMessage = "Cannot find subscription with id: " + settings.subscriptionId;
            log.error(errorMessage);
            return Promise.reject(new Error(errorMessage));
        }

        var subscriptionStop = new SubscriptionStop({
            subscriptionId: settings.subscriptionId
        });

        var promise;
        if (subscriptionInfo.multicastId !== undefined) {
            promise = dispatcher.sendMulticastSubscriptionStop({
                from: subscriptionInfo.proxyId,
                toDiscoveryEntry: subscriptionInfo.providerDiscoveryEntry,
                messagingQos: settings.messagingQos,
                multicastId: subscriptionInfo.multicastId,
                subscriptionStop: subscriptionStop
            });
        } else {
            promise = dispatcher.sendSubscriptionStop({
                from: subscriptionInfo.proxyId,
                toDiscoveryEntry: subscriptionInfo.providerDiscoveryEntry,
                messagingQos: settings.messagingQos,
                subscriptionStop: subscriptionStop
            });
        }

        if (publicationCheckTimerIds[settings.subscriptionId] !== undefined) {
            LongTimer.clearTimeout(publicationCheckTimerIds[settings.subscriptionId]);
            delete publicationCheckTimerIds[settings.subscriptionId];
        }

        cleanupSubscription(settings.subscriptionId);

        return promise;
    };

    this.hasMulticastSubscriptions = function() {
        return Object.keys(multicastSubscribers).length > 0;
    };

    this.hasOpenSubscriptions = function() {
        var hasSubscriptionInfos = Object.keys(subscriptionInfos).length > 0;
        var hasSubscriptionListeners = Object.keys(subscriptionListeners).length > 0;
        var hasPublicationCheckTimerIds = Object.keys(publicationCheckTimerIds).length > 0;
        var hasSubscriptionReplyCallers = Object.keys(subscriptionReplyCallers).length > 0;
        return (
            hasSubscriptionInfos ||
            hasSubscriptionListeners ||
            hasPublicationCheckTimerIds ||
            hasSubscriptionReplyCallers ||
            this.hasMulticastSubscriptions()
        );
    };

    /**
     * This method is meant to be called by the runtime before shutdown is called.
     * It turns out that there is a necessary shutdown order and Subscriptionmanager can't be shutdown first.
     *
     * @function
     * @param {number} clearSubscriptionsTimeoutMs
     */
    this.terminateSubscriptions = function(clearSubscriptionsTimeoutMs) {
        var cleanUpPromises = [];
        var activeSubscriptionId;
        for (activeSubscriptionId in subscriptionInfos) {
            if (Object.prototype.hasOwnProperty.call(subscriptionInfos, activeSubscriptionId)) {
                var promise = this.unregisterSubscription({ subscriptionId: activeSubscriptionId });
                cleanUpPromises.push(promise);
            }
        }
        return Util.timeoutPromise(Promise.all(cleanUpPromises), clearSubscriptionsTimeoutMs);
    };

    /**
     * Shutdown the subscription manager
     *
     * @function
     */
    this.shutdown = function shutdown() {
        var subscriptionId;
        for (subscriptionId in publicationCheckTimerIds) {
            if (publicationCheckTimerIds.hasOwnProperty(subscriptionId)) {
                var timerId = publicationCheckTimerIds[subscriptionId];
                if (timerId !== undefined) {
                    LongTimer.clearTimeout(timerId);
                }
            }
        }
        publicationCheckTimerIds = {};
        for (subscriptionId in subscriptionReplyCallers) {
            if (subscriptionReplyCallers.hasOwnProperty(subscriptionId)) {
                var subscriptionReplyCaller = subscriptionReplyCallers[subscriptionId];
                if (subscriptionReplyCaller) {
                    subscriptionReplyCaller.reject(new Error("Subscription Manager is already shut down"));
                }
            }
        }
        subscriptionReplyCallers = {};
        started = false;
    };
}

module.exports = SubscriptionManager;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy