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

joynr.capabilities.discovery.CapabilityDiscovery.js Maven / Gradle / Ivy

/*jslint es5: true, continue: 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%
 */

/**
 * The CapabilityDiscovery is a joynr internal interface. When the Arbitrator does a lookup for capabilities, this module is
 * queried. If a provider needs to be registered, this module selects the places to register at.
 */
define(
        "joynr/capabilities/discovery/CapabilityDiscovery",
        [
            "global/Promise",
            "joynr/types/GlobalDiscoveryEntry",
            "joynr/system/RoutingTypes/ChannelAddress",
            "joynr/types/DiscoveryEntry",
            "joynr/proxy/DiscoveryQos",
            "joynr/types/DiscoveryScope",
            "joynr/types/ProviderScope",
            "joynr/types/Version",
            "joynr/infrastructure/GlobalCapabilitiesDirectoryProxy",
            "joynr/types/TypeRegistrySingleton",
            "joynr/util/Typing",
            "joynr/system/LoggerFactory",
            "joynr/util/UtilInternal",
            "joynr/exceptions/ProviderRuntimeException"
        ],
        function(
                Promise,
                GlobalDiscoveryEntry,
                ChannelAddress,
                DiscoveryEntry,
                DiscoveryQos,
                DiscoveryScope,
                ProviderScope,
                Version,
                GlobalCapabilitiesDirectoryProxy,
                TypeRegistrySingleton,
                Typing,
                LoggerFactory,
                Util,
                ProviderRuntimeException) {

            /**
             * The CapabilitiesDiscovery looks up the local and global capabilities directory
             *
             * @constructor
             * @name CapabilityDiscovery
             *
             * @param {CapabilitiesStore}
             *            localCapabilitiesStore the local capabilities store
             * @param {CapabilitiesStore}
             *            globalCapabilitiesCache the cache for the global capabilities directory
             * @param {MessageRouter}
             *            messageRouter the message router
             * @param {ProxyBuilder}
             *            proxyBuilder the proxy builder used to create the GlobalCapabilitiesDirectoryProxy
             * @param {String}
             *            globalCapabilitiesDomain the domain to communicate with the GlobalCapablitiesDirectory
             *                                     GlobalCapab
             *
             */
            function CapabilityDiscovery(
                    localCapabilitiesStore,
                    globalCapabilitiesCache,
                    messageRouter,
                    proxyBuilder,
                    globalCapabilitiesDomain) {
                var log =
                        LoggerFactory.getLogger("joynr/capabilities/discovery/CapabilityDiscovery");
                var TTL_30DAYS_IN_MS = 30*24*60*60*1000;
                var globalAddress, globalAddressSerialized;
                var typeRegistry = TypeRegistrySingleton.getInstance();
                var queuedGlobalDiscoveryEntries = [];
                var queuedGlobalLookups = [];

                if (!localCapabilitiesStore
                    || !localCapabilitiesStore.lookup
                    || !localCapabilitiesStore.add
                    || !localCapabilitiesStore.remove) {
                    throw new Error(
                            "constructor of CapabilityDiscovery requires the localCapabilitiesStore as argument");
                }

                if (!globalCapabilitiesCache
                    || !globalCapabilitiesCache.lookup
                    || !globalCapabilitiesCache.add
                    || !globalCapabilitiesCache.remove) {
                    throw new Error(
                            "constructor of CapabilityDiscovery requires the globalCapabilitiesCache as argument");
                }

                if (!messageRouter || !(messageRouter.addNextHop)) {
                    throw new Error(
                            "constructor of CapabilityDiscovery requires the messageRouter as argument");
                }

                if (proxyBuilder === undefined) {
                    throw new Error(
                            "constructor of CapabilityDiscovery requires the proxyBuilder as argument");
                }

                if (globalCapabilitiesDomain === undefined) {
                    throw new Error(
                            "constructor of CapabilityDiscovery requires the globalCapabilitiesDomain");
                }

                /**
                 * This method create a new global capabilities proxy with the provided ttl as messaging QoS
                 *
                 * @function
                 * @name CapabilityDiscovery#getGlobalCapabilitiesDirectoryProxy
                 *
                 * @param {Number}
                 *            ttl time to live of joynr messages triggered by the returning proxy
                 *
                 * @returns {GlobalCapabilitiesDirectoryProxy} the newly created proxy
                 *
                 */
                function getGlobalCapabilitiesDirectoryProxy(ttl) {
                    return proxyBuilder.build(GlobalCapabilitiesDirectoryProxy, {
                        domain : globalCapabilitiesDomain,
                        messagingQos : {
                            ttl: ttl
                        },
                        discoveryQos : new DiscoveryQos({
                            discoveryScope : DiscoveryScope.GLOBAL_ONLY,
                            cacheMaxAgeMs : Util.getMaxLongValue()
                        })
                    }).catch(function(error) {
                        throw new Error("Failed to create global capabilities directory proxy: " + error);
                    });
                }

                function lookupGlobal(domains, interfaceName, ttl, capabilities) {

                    return getGlobalCapabilitiesDirectoryProxy(ttl).then(function(globalCapabilitiesDirectoryProxy){
                        return globalCapabilitiesDirectoryProxy.lookup({
                            domains : domains,
                            interfaceName : interfaceName
                        }).then(function(opArgs) {
                            var i, messageRouterPromises = [], globalCapabilities = opArgs.result;
                            var globalAddress;
                            if (globalCapabilities === undefined) {
                                log.error("globalCapabilitiesDirectoryProxy.lookup() returns with missing result");
                            } else {
                                for (i = globalCapabilities.length - 1; i >= 0; i--) {
                                    var globalDiscoveryEntry = globalCapabilities[i];
                                    if (globalDiscoveryEntry.address === globalAddressSerialized) {
                                        globalCapabilities.splice(i, 1);
                                    } else {
                                        try {
                                            globalAddress = Typing.augmentTypes(JSON.parse(globalDiscoveryEntry.address), typeRegistry);
                                        } catch (e) {
                                            log.error("unable to use global discoveryEntry with unknown address type: " + globalDiscoveryEntry.address);
                                            continue;
                                        }
                                        // Update routing table
                                        messageRouterPromises.push(messageRouter.addNextHop(
                                                globalDiscoveryEntry.participantId,
                                                globalAddress));
                                        capabilities.push(globalDiscoveryEntry);
                                    }
                                }
                            }
                            return Promise.all(messageRouterPromises).then(function() {
                                return capabilities;
                            });
                        });
                    });
                }
                /**
                 * expects a capabilities array which is then filled with any that are found from the proxy
                 *
                 * @function
                 * @name CapabilityDiscovery#lookupGlobalCapabilities
                 *
                 * @param {String} domains - the domains
                 * @param {String} interfaceName - the interface name
                 * @param {Number} ttl - time to live of joynr messages triggered by the returning proxy
                 * @param {Array} capabilities - the capabilities array to be filled
                 *
                 * @returns {Array} - the capabilities array filled with the capabilities found in the global capabilities directory
                 */
                function lookupGlobalCapabilities(domains, interfaceName, ttl, capabilities) {
                    var promise;
                    if (! globalAddressSerialized) {
                        promise = new Promise(function (resolve, reject) {
                            queuedGlobalLookups.push({
                                domains: domains,
                                interfaceName: interfaceName,
                                ttl: ttl,
                                capabilities: capabilities,
                                resolve: resolve,
                                reject: reject
                            });
                        });
                    } else {
                        promise = lookupGlobal(domains,interfaceName, ttl, capabilities);
                    }
                    return promise;
                }

                /**
                 *
                 * @param discoveryEntry to be added to the global discovery directory
                 * @returns {Object} an A+ promise
                 */
                function addGlobal(discoveryEntry) {
                    return getGlobalCapabilitiesDirectoryProxy(TTL_30DAYS_IN_MS).then(function(globalCapabilitiesDirectoryProxy){
                        discoveryEntry.address = globalAddressSerialized;
                        return globalCapabilitiesDirectoryProxy.add({
                            globalDiscoveryEntry : new GlobalDiscoveryEntry(discoveryEntry)
                        }).catch(function(error) {
                            throw new Error("Error calling operation \"add\" of GlobalCapabilitiesDirectory because: "
                                    + error);
                        });
                    });
                }

                /**
                 *
                 * @param {Object} queuedDiscoveryEntry contains a discoveryEntry and the resolve
                 * and reject functions from the original Promise created on add().
                 */
                function addGlobalQueued(queuedDiscoveryEntry) {
                    addGlobal(queuedDiscoveryEntry.discoveryEntry).then(queuedDiscoveryEntry.resolve).catch(queuedDiscoveryEntry.reject);
                }

                /**
                 * This method is called when the global address has been created
                 *
                 * @function
                 * @name CapabilityDiscovery#globalAddressReady
                 *
                 * @param {Address}
                 *            globalAddress the address used to register discovery entries globally
                 */
                this.globalAddressReady = function globalAddressReady(newGlobalAddress) {
                    var i, parameters;
                    globalAddress = newGlobalAddress;
                    globalAddressSerialized = JSON.stringify(newGlobalAddress);
                    for (i=0;i 0) {
                                        return Promise.resolve(localCapabilities);
                                    }
                                    globalCapabilities = globalCapabilitiesCache.lookup({
                                        domains : domains,
                                        interfaceName : interfaceName,
                                        cacheMaxAge : discoveryQos.cacheMaxAge
                                    });
                                    if (globalCapabilities.length > 0) {
                                        return Promise.resolve(globalCapabilities);
                                    }
                                    return lookupGlobalCapabilities(
                                            domains,
                                            interfaceName,
                                            TTL_30DAYS_IN_MS,
                                            localCapabilities);

                                    // Use local results, but then lookup global
                                case DiscoveryScope.LOCAL_AND_GLOBAL.value:
                                    localCapabilities = localCapabilitiesStore.lookup({
                                        domains : domains,
                                        interfaceName : interfaceName
                                    });
                                    globalCapabilities = globalCapabilitiesCache.lookup({
                                        domains : domains,
                                        interfaceName : interfaceName,
                                        cacheMaxAge : discoveryQos.cacheMaxAge
                                    });
                                    if (globalCapabilities.length === 0) {
                                        return lookupGlobalCapabilities(
                                                domains,
                                                interfaceName,
                                                TTL_30DAYS_IN_MS,
                                                localCapabilities);
                                    }
                                    return Promise.resolve(localCapabilities
                                            .concat(globalCapabilities));

                                case DiscoveryScope.GLOBAL_ONLY.value:
                                    globalCapabilities = globalCapabilitiesCache.lookup({
                                        domains : domains,
                                        interfaceName : interfaceName,
                                        cacheMaxAge : discoveryQos.cacheMaxAge
                                    });
                                    if (globalCapabilities.length > 0) {
                                        return Promise.resolve(globalCapabilities);
                                    }
                                    return lookupGlobalCapabilities(
                                            domains,
                                            interfaceName,
                                            TTL_30DAYS_IN_MS,
                                            globalCapabilities);
                            }
                        };

                /**
                 * This method adds a capability in the local and/or global capabilities directory according to the given registration
                 * strategy.
                 *
                 * @function
                 * @name CapabilityDiscovery#add
                 *
                 * @param {String}
                 *            domain of the capability
                 * @param {String}
                 *            interfaceName of the capability
                 * @param {String}
                 *            participantId of the capability
                 * @param {ProviderQos}
                 *            providerQos of the capability
                 * @param {Array}
                 *            array of communication middlewares
                 *
                 * @returns {Object} an A+ promise
                 */
                this.add =
                        function add(discoveryEntry) {
                            localCapabilitiesStore.add({
                                discoveryEntry : discoveryEntry,
                                remote : false
                            });
                            var promise;
                            discoveryEntry.lastSeenDateMs = Date.now();
                            if (discoveryEntry.qos.scope === ProviderScope.LOCAL) {
                                promise = Promise.resolve();
                            } else if (discoveryEntry.qos.scope === ProviderScope.GLOBAL) {
                                if (! globalAddressSerialized) {
                                    promise = new Promise(function (resolve, reject) {
                                        queuedGlobalDiscoveryEntries.push({
                                            discoveryEntry: discoveryEntry,
                                            resolve: resolve,
                                            reject: reject
                                        });
                                    });
                                } else {
                                    promise = addGlobal(discoveryEntry);
                                }
                            } else {
                                promise = Promise.reject(new Error("Encountered unknown ProviderQos scope \""
                                        + discoveryEntry.qos.scope + "\""));
                            }
                            return promise;
                        };

                /**
                 * This method sends a freshness update to the global capabilities directory to update
                 * the lastSeenDateMs of the entries registered via the local cluster controller.
                 *
                 * @param clusterControllerId the channelId of the local cluster controller
                 * @param ttlMs the time to live of the freshness update message
                 *
                 * @returns {Object} an A+ promise
                 */
                this.touch = function touch(clusterControllerId, ttlMs) {
                    return getGlobalCapabilitiesDirectoryProxy(ttlMs).then(function(globalCapabilitiesDirectoryProxy){
                        return globalCapabilitiesDirectoryProxy.touch({clusterControllerId: clusterControllerId});
                    }).catch(function(error) {
                        throw new Error("Error calling operation \"touch\" of GlobalCapabilitiesDirectory because: "
                                    + error);
                    });
                };

                /**
                 * This method removes a capability from the global capabilities directory.
                 *
                 * @function
                 * @name CapabilityDiscovery#removeParticipantIdFromGlobalCapabilitiesDirectory
                 *
                 * @param {String}
                 *            participantId to remove
                 *
                 * @returns {Object} an A+ promise
                 */
                function removeParticipantIdFromGlobalCapabilitiesDirectory(participantId) {
                    return getGlobalCapabilitiesDirectoryProxy(TTL_30DAYS_IN_MS).then(function(globalCapabilitiesDirectoryProxy){
                        return globalCapabilitiesDirectoryProxy.remove({
                            participantId : participantId
                        }).catch(function(error) {
                            throw new Error("Error calling operation \"remove\" of GlobalCapabilitiesDirectory because: "
                                    + error);
                        });
                    });
                }

                /**
                 * This method removes a capability from the local and/or global capabilities directory according to the given registration
                 * strategy.
                 *
                 * @function
                 * @name CapabilityDiscovery#remove
                 *
                 * @param {String}
                 *            participantId to remove
                 *
                 * @returns {Object} an A+ promise
                 */
                this.remove =
                        function remove(participantId) {
                            var discoveryEntries = localCapabilitiesStore.lookup({
                                participantId : participantId
                            });
                            var promise;

                            localCapabilitiesStore.remove({
                                participantId : participantId
                            });
                            if (discoveryEntries === undefined
                                    || discoveryEntries.length !== 1) {
                                log
                                .warn("remove(): no capability entry found in local capabilities store for participantId "
                                        + participantId
                                        + ". Trying to remove the capability from global directory");
                                promise = removeParticipantIdFromGlobalCapabilitiesDirectory(
                                        participantId);
                            } else {
                                if (discoveryEntries[0].qos.scope === ProviderScope.LOCAL
                                        || discoveryEntries.length < 1) {
                                    promise = Promise.resolve();
                                } else if (discoveryEntries[0].qos.scope === ProviderScope.GLOBAL) {
                                    promise = removeParticipantIdFromGlobalCapabilitiesDirectory(
                                            participantId);
                                } else {
                                    promise = Promise.reject(new Error(
                                            "Encountered unknown ProviderQos scope \""
                                            + discoveryEntries[0].qos.scope
                                            + "\""));
                                }
                            }
                            return promise;
                        };
            }

            return CapabilityDiscovery;

        });




© 2015 - 2025 Weber Informatics LLC | Privacy Policy