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

joynr.proxy.ProxyOperation.js Maven / Gradle / Ivy

/*jslint es5: true */

/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2015 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/proxy/ProxyOperation",
        [
            "global/Promise",
            "joynr/util/UtilInternal",
            "joynr/util/JSONSerializer",
            "joynr/util/Typing",
            "joynr/util/MethodUtil",
            "joynr/types/TypeRegistrySingleton",
            "joynr/dispatching/types/Request",
            "joynr/dispatching/types/OneWayRequest",
            "joynr/messaging/MessagingQos"
        ],
        function(
                Promise,
                Util,
                JSONSerializer,
                Typing,
                MethodUtil,
                TypeRegistrySingleton,
                Request,
                OneWayRequest,
                MessagingQos) {
            var typeRegistry = TypeRegistrySingleton.getInstance();
            /**
             * Checks if the given operationSignature is valid to be called for the given operation
             * arguments. valid means, that for all operationArguments there is a matching
             * (name and type) parameter found in the operationSignature and there are no parameters
             * in the operationSignature that miss the corresponding argument.
             *
             * @name ProxyOperation#checkSignatureMatch
             * @function
             * @private
             *
             * @param {Object}
             *            operationSignature an object with the argument name as key and an object
             *            as value defining the type
             * @param {Object}
             *            operationSignature.PARAMETERNAME an object describing the single parameter
             * @param {String}
             *            operationSignature.PARAMETERNAME.type the type of the parameter
             * @param {Object}
             *            operationArguments: this object contains all parameters
             * @param {?}
             *            operationArguments.OPERATIONARGUMENTNAME: CUSTOM DOC FROM IDL GOES HERE,
             *            contains the argument value
             *
             * @returns {Object} if there's a match, a combined object with the name, type and value
             *            is returned, e.g. {"argumentName1":
             *            {type: "string", value: "asdf"},
             *            "argumentName2": {type: "number", value: 1234}}
             *
             * @throws {Error}
             *             if an argument value is nullable
             */
            function checkSignatureMatch(operationSignature, operationArguments) {

                // if for all operationArguments there is a matching (name and type) parameter found
                // in the operationSignature, this object will hold name, type and value and is
                // qualified to be used for serialization and will be returned
                var result = {};

                try {
                    result.signature =
                            {
                                inputParameter : MethodUtil.transformParameterMapToArray(
                                        operationArguments,
                                        operationSignature.inputParameter),
                                outputParameter : operationSignature.outputParameter || [],
                                fireAndForget : operationSignature.fireAndForget
                            };
                } catch (error) {
                    result.errorMessage = error.message;
                }
                return result;
            }

            function checkArguments(operationArguments) {
                var errors = [];
                var argumentName;
                var argumentValue;
                for (argumentName in operationArguments) {
                    if (operationArguments.hasOwnProperty(argumentName)) {
                        argumentValue = operationArguments[argumentName];
                        // make sure types of complex type members are also ok
                        if (argumentValue
                            && argumentValue.checkMembers
                            && typeof argumentValue.checkMembers === "function") {
                            try {
                                argumentValue.checkMembers(Util.checkPropertyIfDefined);
                            } catch (error) {
                                errors.push(error.message);
                            }
                        }
                    }
                }
                return errors;
            }

            /**
             * Constructor of ProxyOperation object that is used in the generation of proxy objects
             *
             * @constructor
             * @name ProxyOperation
             *
             * @param {Object}
             *            parent is the proxy object that contains this attribute
             * @param {String}
             *            parent.fromParticipantId of the proxy itself
             * @param {String}
             *            parent.toParticipantId of the provider being addressed
             * @param {Object}
             *            settings the settings object for this function call
             * @param {DiscoveryQos}
             *            settings.discoveryQos the Quality of Service parameters for arbitration
             * @param {MessagingQos}
             *            settings.messagingQos the Quality of Service parameters for messaging
             * @param {Object}
             *            settings.dependencies the dependencies object for this function call
             * @param {RequestReplyManager}
             *            settings.dependencies.requestReplyManager
             * @param {String}
             *            operationName the name of the operation
             * @param {Array}
             *            operationSignatures an array of possible signatures for this operation
             * @param {Array}
             *            operationSignatures.array.inputParameter an array of supported arguments for
             *            one specific signature
             * @param {String}
             *            operationSignatures.array.inputParameter.name the name of the input parameter
             * @param {String}
             *            operationSignatures.array.inputParameter.type the type of the input parameter
             * @param {Array}
             *            operationSignatures.array.outputParameter an array of output parameters for
             *            one specific signature
             * @param {String}
             *            operationSignatures.array.outputParameter.name the name of the output parameter
             * @param {String}
             *            operationSignatures.array.outputParameter.type the type of the output parameter
             */
            function ProxyOperation(parent, settings, operationName, operationSignatures) {
                if (!(this instanceof ProxyOperation)) {
                    // in case someone calls constructor without new keyword (e.g. var c = Constructor({..}))
                    return new ProxyOperation(parent, settings, operationName, operationSignatures);
                }

                /**
                 * Generic operation implementation
                 *
                 * @name ProxyOperation#operationFunction
                 * @function
                 * @private
                 *
                 * @param {Object}
                 *            operationArguments: this object contains all parameters
                 * @param {?}
                 *            operationArguments.OPERATIONARGUMENTNAME: CUSTOM DOC FROM IDL GOES
                 *            HERE, contains the argument value
                 * @param {DiscoveryQos}
                 *            proxyOperation.settings.discoveryQos the Quality of Service parameters
                 *            for arbitration
                 * @param {MessagingQos}
                 *            proxyOperation.settings.messagingQos the Quality of Service parameters
                 *            for messaging
                 *
                 * @param {ProxyOperation}
                 *            proxyOperation the ProxyOperation object in which context the
                 *            operation is called
                 * @param {Array}
                 *            operationSignatures a list holding multiple versions of the signature
                 *            for the overloaded operation
                 * @param {Object}
                 *            operationSignatures.array an object with the argument name as key and
                 *            an object as value defining the type
                 * @param {Object}
                 *            operationSignatures.array.PARAMETERNAME an object describing the
                 *            single parameter
                 * @param {String}
                 *            operationSignatures.array.PARAMETERNAME.type the type of the parameter
                 *
                 * @returns {Object} returns an A+ promise object that will alternatively accept the
                 *            callback functions through its
                 *            functions "then(function (){..}).catch(function ({string}error){..})"
                 *            in A+ promise style instead of using the function parameters
                 */
                function operationFunction(operationArguments, proxyOperation, operationSignatures) {
                    var i;

                    // ensure operationArguments variable holds a valid object and initialize promise object
                    var argumentErrors = checkArguments(operationArguments);
                    if (argumentErrors.length > 0) {
                        return Promise.reject(new Error("error calling operation: "
                            + operationName
                            + ": "
                            + argumentErrors.toString()));
                    }

                    try {
                        var foundValidOperationSignature, checkResult, caughtErrors = [];

                        // cycle through multiple available operation signatures
                        for (i = 0; i < proxyOperation.operationSignatures.length
                            && foundValidOperationSignature === undefined; ++i) {
                            // check if the parameters from the operation signature is valid for
                            // the provided arguments
                            checkResult =
                                    checkSignatureMatch(
                                            proxyOperation.operationSignatures[i],
                                            operationArguments || {});
                            if (checkResult !== undefined) {
                                if (checkResult.errorMessage !== undefined) {
                                    caughtErrors.push(checkResult.errorMessage);
                                } else {
                                    foundValidOperationSignature = checkResult.signature;
                                }
                            }
                        }

                        // operation was not called because there was no signature found that
                        // matches given arguments
                        if (foundValidOperationSignature === undefined) {
                            return Promise
                                    .reject(new Error(
                                            "Could not find a valid operation signature in '"
                                                + JSON
                                                        .stringify(proxyOperation.operationSignatures)
                                                + "' for a call to operation '"
                                                + proxyOperation.operationName
                                                + "' with the arguments: '"
                                                + JSON.stringify(operationArguments)
                                                + "'. The following errors occured during signature check: "
                                                + JSON.stringify(caughtErrors)));
                        }

                        // passed in (right-most) messagingQos have precedence; undefined values are
                        // ignored
                        var messagingQos =
                                new MessagingQos(Util.extend(
                                        {},
                                        proxyOperation.parent.messagingQos,
                                        settings.messagingQos));

                        // send it through request reply manager
                        if (foundValidOperationSignature.fireAndForget === true) {
                            // build outgoing request
                            var oneWayRequest =
                                    new OneWayRequest(
                                            {
                                                methodName : proxyOperation.operationName,
                                                paramDatatypes : foundValidOperationSignature.inputParameter.paramDatatypes,
                                                params : foundValidOperationSignature.inputParameter.params
                                            });

                            return settings.dependencies.requestReplyManager.sendOneWayRequest({
                                to : proxyOperation.parent.providerParticipantId,
                                from : proxyOperation.parent.proxyParticipantId,
                                messagingQos : messagingQos,
                                request : oneWayRequest
                            });
                        }
                        if (foundValidOperationSignature.fireAndForget !== true) {
                            // build outgoing request
                            var request =
                                    new Request(
                                            {
                                                methodName : proxyOperation.operationName,
                                                paramDatatypes : foundValidOperationSignature.inputParameter.paramDatatypes,
                                                params : foundValidOperationSignature.inputParameter.params
                                            });

                            return settings.dependencies.requestReplyManager
                                    .sendRequest({
                                        to : proxyOperation.parent.providerParticipantId,
                                        from : proxyOperation.parent.proxyParticipantId,
                                        messagingQos : messagingQos,
                                        request : request
                                    })
                                    .then(
                                            function(response) {
                                                var responseKey, argumentValue;
                                                if (foundValidOperationSignature.outputParameter
                                                    && foundValidOperationSignature.outputParameter.length > 0) {
                                                    argumentValue = {};
                                                    for (responseKey in response) {
                                                        if (response.hasOwnProperty(responseKey)) {
                                                            if (foundValidOperationSignature.outputParameter[responseKey] !== undefined) {
                                                                argumentValue[foundValidOperationSignature.outputParameter[responseKey].name] =
                                                                        Typing
                                                                                .augmentTypes(
                                                                                        response[responseKey],
                                                                                        typeRegistry,
                                                                                        foundValidOperationSignature.outputParameter[responseKey].type);
                                                            } else {
                                                                return Promise
                                                                        .reject(new Error(
                                                                                "Unexpected response: "
                                                                                    + JSONSerializer
                                                                                            .stringify(response[responseKey])));
                                                            }
                                                        }
                                                    }
                                                }

                                                return argumentValue;
                                            });
                        }
                    } catch (e) {
                        return Promise
                                .reject(new Error("error calling operation: " + e.toString()));
                    }
                }

                /**
                 * Operation Function builder
                 *
                 * @name ProxyOperation#buildFunction
                 * @function
                 *
                 * @returns {Function} returns the operation function that can be assigned to a
                 *            member of the proxy
                 */
                this.buildFunction = function buildFunction() {
                    var self = this;
                    return function(operationArguments) {
                        return operationFunction(operationArguments, self, operationSignatures);
                    };
                };

                /**
                 * The parent proxy object
                 *
                 * @name ProxyOperation#parent
                 * @type Proxy
                 */
                this.parent = parent;

                /**
                 * @name ProxyOperation#operationName
                 * @type String
                 */
                this.operationName = operationName;

                /**
                 * @name ProxyOperation#operationSignatures
                 * @type Array
                 */
                this.operationSignatures = operationSignatures;

                return Object.freeze(this);
            }

            return ProxyOperation;

        });




© 2015 - 2025 Weber Informatics LLC | Privacy Policy