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

org.activecomponents.webservice.jadex.js Maven / Gradle / Ivy

There is a newer version: 4.0.267
Show newest version
// https://github.com/joewalnes/reconnecting-websocket/blob/master/reconnecting-websocket.js
(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof module !== 'undefined' && module.exports){
        module.exports = factory();
    } else {
        global.ReconnectingWebSocket = factory();
    }
})(this, function () {

    if (!('WebSocket' in window)) {
        return;
    }

    function ReconnectingWebSocket(url, protocols, options) {

        // Default settings
        var settings = {

            /** Whether this instance should log debug messages. */
            debug: false,

            /** Whether or not the websocket should attempt to connect immediately upon instantiation. */
            automaticOpen: true,

            /** The number of milliseconds to delay before attempting to reconnect. */
            reconnectInterval: 1000,
            /** The maximum number of milliseconds to delay a reconnection attempt. */
            maxReconnectInterval: 30000,
            /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
            reconnectDecay: 1.5,

            /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
            timeoutInterval: 2000,

            /** The maximum number of reconnection attempts to make. Unlimited if null. */
            maxReconnectAttempts: null,

            /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
            binaryType: 'blob'
        }
        if (!options) { options = {}; }

        // Overwrite and define settings with options if they exist.
        for (var key in settings) {
            if (typeof options[key] !== 'undefined') {
                this[key] = options[key];
            } else {
                this[key] = settings[key];
            }
        }

        // These should be treated as read-only properties

        /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
        this.url = url;

        /** The number of attempted reconnects since starting, or the last successful connection. Read only. */
        this.reconnectAttempts = 0;

        /**
         * The current state of the connection.
         * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
         * Read only.
         */
        this.readyState = WebSocket.CONNECTING;

        /**
         * A string indicating the name of the sub-protocol the server selected; this will be one of
         * the strings specified in the protocols parameter when creating the WebSocket object.
         * Read only.
         */
        this.protocol = null;

        // Private state variables

        var self = this;
        var ws;
        var forcedClose = false;
        var timedOut = false;
        var eventTarget = document.createElement('div');

        // Wire up "on*" properties as event handlers

        eventTarget.addEventListener('open',       function(event) { self.onopen(event); });
        eventTarget.addEventListener('close',      function(event) { self.onclose(event); });
        eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
        eventTarget.addEventListener('message',    function(event) { self.onmessage(event); });
        eventTarget.addEventListener('error',      function(event) { self.onerror(event); });

        // Expose the API required by EventTarget

        this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
        this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
        this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);

        /**
         * This function generates an event that is compatible with standard
         * compliant browsers and IE9 - IE11
         *
         * This will prevent the error:
         * Object doesn't support this action
         *
         * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
         * @param s String The name that the event should use
         * @param args Object an optional object that the event will use
         */
        function generateEvent(s, args) {
        	var evt = document.createEvent("CustomEvent");
        	evt.initCustomEvent(s, false, false, args);
        	return evt;
        };

        this.open = function (reconnectAttempt) {
            ws = new WebSocket(self.url, protocols || []);
            ws.binaryType = this.binaryType;

            if (reconnectAttempt) {
                if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
                    return;
                }
            } else {
                eventTarget.dispatchEvent(generateEvent('connecting'));
                this.reconnectAttempts = 0;
            }

            if (self.debug || ReconnectingWebSocket.debugAll) {
                console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
            }

            var localWs = ws;
            var timeout = setTimeout(function() {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
                }
                timedOut = true;
                localWs.close();
                timedOut = false;
            }, self.timeoutInterval);

            ws.onopen = function(event) {
                clearTimeout(timeout);
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onopen', self.url);
                }
                self.protocol = ws.protocol;
                self.readyState = WebSocket.OPEN;
                self.reconnectAttempts = 0;
                var e = generateEvent('open');
                e.isReconnect = reconnectAttempt;
                reconnectAttempt = false;
                eventTarget.dispatchEvent(e);
            };

            ws.onclose = function(event) {
                clearTimeout(timeout);
                ws = null;
                if (forcedClose) {
                    self.readyState = WebSocket.CLOSED;
                    eventTarget.dispatchEvent(generateEvent('close'));
                } else {
                    self.readyState = WebSocket.CONNECTING;
                    var e = generateEvent('connecting');
                    e.code = event.code;
                    e.reason = event.reason;
                    e.wasClean = event.wasClean;
                    eventTarget.dispatchEvent(e);
                    if (!reconnectAttempt && !timedOut) {
                        if (self.debug || ReconnectingWebSocket.debugAll) {
                            console.debug('ReconnectingWebSocket', 'onclose', self.url);
                        }
                        eventTarget.dispatchEvent(generateEvent('close'));
                    }

                    var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
                    setTimeout(function() {
                        self.reconnectAttempts++;
                        self.open(true);
                    }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
                }
            };
            ws.onmessage = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
                }
                var e = generateEvent('message');
                e.data = event.data;
                eventTarget.dispatchEvent(e);
            };
            ws.onerror = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
                }
                eventTarget.dispatchEvent(generateEvent('error'));
            };
        }

        // Whether or not to create a websocket upon instantiation
        if (this.automaticOpen == true) {
            this.open(false);
        }

        /**
         * Transmits data to the server over the WebSocket connection.
         *
         * @param data a text string, ArrayBuffer or Blob to send to the server.
         */
        this.send = function(data) {
            if (ws) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'send', self.url, data);
                }
                return ws.send(data);
            } else {
                throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
            }
        };

        /**
         * Closes the WebSocket connection or connection attempt, if any.
         * If the connection is already CLOSED, this method does nothing.
         */
        this.close = function(code, reason) {
            // Default CLOSE_NORMAL code
            if (typeof code == 'undefined') {
                code = 1000;
            }
            forcedClose = true;
            if (ws) {
                ws.close(code, reason);
            }
        };

        /**
         * Additional public API method to refresh the connection if still open (close, re-open).
         * For example, if the app suspects bad data / missed heart beats, it can try to refresh.
         */
        this.refresh = function() {
            if (ws) {
                ws.close();
            }
        };
    }

    /**
     * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
     * this indicates that the connection is ready to send and receive data.
     */
    ReconnectingWebSocket.prototype.onopen = function(event) {};
    /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
    ReconnectingWebSocket.prototype.onclose = function(event) {};
    /** An event listener to be called when a connection begins being attempted. */
    ReconnectingWebSocket.prototype.onconnecting = function(event) {};
    /** An event listener to be called when a message is received from the server. */
    ReconnectingWebSocket.prototype.onmessage = function(event) {};
    /** An event listener to be called when an error occurs. */
    ReconnectingWebSocket.prototype.onerror = function(event) {};

    /**
     * Whether all instances of ReconnectingWebSocket should log debug messages.
     * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.
     */
    ReconnectingWebSocket.debugAll = false;

    ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
    ReconnectingWebSocket.OPEN = WebSocket.OPEN;
    ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
    ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;

    return ReconnectingWebSocket;
});

var Scopes =
{
		/** None component scope (nothing will be searched). */
//		const SCOPE_NONE = "none";
	//
//		/** Local component scope. */
//		const SCOPE_LOCAL = "local";
	//
//		/** Component scope. */
//		const SCOPE_COMPONENT = "component";
	//
//		/** Application scope. */
//		const SCOPE_APPLICATION = "application";

	    /** Platform scope. */
	    SCOPE_PLATFORM: "platform",

	    /** Global scope. */
	    SCOPE_GLOBAL: "global",

//		/** Parent scope. */
//		SCOPE_PARENT:string = "parent";

	    /** Session scope. */
	    SCOPE_SESSION: "session"
};

var SUtil =
{
	wrappedConversionTypes:
	{
        java_lang_Integer: "number",
        java_lang_Byte: "number",
        java_lang_Short: "number",
        java_lang_Long: "number",
        java_lang_Float: "number",
        java_lang_Double: "number",
        java_lang_Character: "string",
        java_lang_Boolean: "boolean"
    },

    /**
     *  Test if an object is a basic type.
     *  @param obj The object.
     *  @return True, if is a basic type.
     */
    isBasicType: function(obj)
    {
        return typeof obj !== 'object' || !obj;
    },

    /**
     *  Test if an object is a java wrapped type.
     *  @param obj The object.
     *  @return False, if is not a wrapped primitive type, else returns the corresponding JS type.
     */
    isWrappedType: function(obj)
    {
        if("__classname" in obj) 
        {
            var searching = obj.__classname.replace(/\./g, '_')
            return this.wrappedConversionTypes[searching];
        } 
        else 
        {
            return false;
        }
    },

    /**
     *  Check of an obj is an enum.
     */
    isEnum: function(obj)
    {
        return ("enum" in obj)
    },

    /**
     *  Test if an object is an array.
     *  @param obj The object.
     *  @return True, if is an array.
     */
    isArray: function(obj)
    {
        return Object.prototype.toString.call(obj) == '[object Array]';
    },

    /**
     *  Compute the approx. size of an object.
     *  @param obj The object.
     */
    sizeOf: function(object) 
    {
        var objects = [object];
        var size = 0;

        for(var i = 0; i < objects.length; i++) 
        {
            switch(typeof objects[i]) 
            {
                case 'boolean':
                    size += 4;
                    break;
                case 'number':
                    size += 8;
                    break;
                case 'string':
                    size += 2 * objects[i].length;
                    break;
                case 'object':

                    if(Object.prototype.toString.call(objects[i]) != '[object Array]') 
                    {
                        for(var key in objects[i])
                            size += 2 * key.length;
                    }

                    var processed = false;
                    var key;
                    for(key in objects[i]) 
                    {
                        for(var search = 0; search < objects.length; search++) 
                        {
                            if(objects[search] === objects[i][key]) 
                            {
                                processed = true;
                                break;
                            }
                        }
                    }

                    if(!processed)
                        objects.push(objects[i][key]);
            }
        }
        return size;
    },

    /**
     *  Check if object is true by inspecting if it contains a true property.
     */
    isTrue: function(obj) 
    {
        return obj == true || (obj != null && obj.hasOwnProperty("value") && obj.value == true);
    },


    /**
     *  Assert that throws an error if not holds.
     */
    assert: function(condition, message) 
    {
        if(!condition) 
        {
            message = message || "Assertion failed";
            if(typeof Error !== "undefined") 
            {
                throw new Error(message);
            }
            throw message; // Fallback
        }
    },

    /**
     *  Get the service id as string.
     *  (otherwise it cannot be used as key in a map because
     *  no equals exists).
     */
    getServiceIdAsString: function(sid)
    {
        return sid.serviceName+"@"+sid.providerId;
    },

    /**
     *  Add a console out error handler to the promise.
     */
    addErrHandler: function(p)
    {
        p.oldcatch = p.catch;
        p.hasErrorhandler = false;
        p.catch = function(eh)
        {
            p.hasErrorHandler = true;
            return p.oldcatch(eh);
        };
        p.oldcatch(function(err)
        {
            if(!p.hasErrorHandler)
                console.log("Error occurred: "+err);
        });
        p.oldthen = p.then;
        p.then = function(t, e)
        {
            if(e)
                p.hasErrorHandler = true;
            return p.oldthen(t, e);
        };
        return p;
    },

    /**
     *  Test if a number is a float.
     *  @param n The number to test.
     *  @return True, if is float.
     */
    isFloat: function(n)
    {
        return n === +n && n !== (n | 0);
    },

    /**
     *  Test if a number is an integer.
     *  @param n The number to test.
     *  @return True, if is integer.
     */
    isInteger: function(n)
    {
        return n === +n && n === (n | 0);
    },

    /**
     *  Check if an object is contained in an array.
     *  Uses equal function to check equality of objects.
     *  If not provided uses reference test.
     *  @param object The object to check.
     *  @param objects The array.
     *  @param equals The equals method.
     *  @return True, if is contained.
     */
    containsObject: function(object, objects, equals)
	{
		var ret = false;
		
		for(var i=0; i 
        {
    //	    console.log(obj+" "+prop+" "+parent);

            if(!SUtil.isBasicType(obj)) 
            {
                // test if it is just a placeholder object that must be changed
    //			if(prop!=null)
    //			{
                for(var i = 0; i < replacemarker.length; i++) 
                {
                    if(replacemarker[i] in obj) 
                    {
                        obj = obj[replacemarker[i]];
                        break;
                    }
                }
    //		    }

                // instantiate classes
                if("__classname" in obj) 
                {
                    var className = obj["__classname"];
                    //if(className == "jadex.bridge.service.IService") 
                    if(className == "org.activecomponents.webservice.ServiceInfo") 
                    {
                        obj = new Jadex.ServiceProxy(obj.serviceIdentifier, recurse(obj.methodNames, "methodNames", obj), url)
                    } 
                    else if(className in JsonParser.registeredClasses)
                    {
                        var func = JsonParser.registeredClasses[className];
                        
                        if(func.create)
                        {
                            obj = func.create(obj);
                        }
                        else
                        {
                            // iterate members:
                            var instance = new func();
                            for(var prop in obj) 
                            {
                                instance[prop] = recurse(obj[prop], prop, obj);
                            }
                            obj = instance;
                        }
                    }
                } 
                else 
                {
                    // recreate arrays
                    if(SUtil.isArray(obj)) 
                    {
                        for(var i = 0; i < obj.length; i++) 
                        {
                            if(!SUtil.isBasicType(obj[i])) 
                            {
                                if(refmarker in obj[i]) 
                                {
                                    obj[i] = recurse(obj[i], i, obj);
                                }
                                else 
                                {
                                    obj[i] = recurse(obj[i], prop, obj);
                                }
                            }
                        }
                    }
                }

                if(refmarker in obj) 
                {
                    var ref = obj[refmarker];
                    if(ref in os) 
                    {
                        obj = os[ref];
                    }
                    else 
                    {
                        refs.push([parent, prop, ref]); // lazy evaluation necessary
                    }
                }
                else 
                {
                    var id = null;
                    if(idmarker in obj) 
                    {
                        id = obj[idmarker];
                        delete obj[idmarker];
                    }
                    if("$values" in obj) // an array
                    {
                        obj = obj.$values.map(recurse);
                    }
                    else 
                    {
                        for(var prop in obj)  
                        {
                            obj[prop] = recurse(obj[prop], prop, obj);
                        }
                    }
                    if(id != null) 
                    {
                        os[id] = obj;
                    }
                }

                // unwrap boxed values for JS:
                var wrappedType = SUtil.isWrappedType(obj);
                if(wrappedType) 
                {
                    // console.log("found wrapped: " + isWrappedType(obj) + " for: " + obj.__classname + " with value: " + obj.value)
                    if(wrappedType == "boolean")
                    {
                        // this will not happen, because booleans are already native?
                        // obj = obj.value == "true"
                    }
                    else if(wrappedType == "string")
                    {
                        obj = obj.value
                    }
                    else
                    {
                        // everything else is a number in JS
                        obj = +obj.value
                    }
                }
                else if(SUtil.isEnum(obj))
                {
                    // convert enums to strings
                    obj = obj.value;
                }
            }
            return obj;
        };

        obj = recurse(obj, null, null);

        // resolve lazy references
        for(var i = 0; i < refs.length; i++) 
        {
            var ref = refs[i];
            ref[0][ref[1]] = os[ref[2]];
        }
        return obj;
    }
};
JsonParser.init(); 

var ConnectionHandler =  
{
    /** The websocket connections. */
    connections: [],

    baseurl: null,
    
    /** The map of open outcalls. */
    outcalls: [],
    
    /** The map of provided services (sid -> service invocation function). */
    providedServices: [],

    init: function() 
    {
        var scripts = document.getElementsByTagName('script');
        var script = scripts[scripts.length - 1];
        var prot = SUtil.isSecure()? "wss": "ws";
        
        if(script["src"]) 
        {
            this.baseurl = script["src"];
            this.baseurl = prot+this.baseurl.substring(this.baseurl.indexOf("://"));
        } 
        else if(script.hasAttributes())
        {
            //this.baseurl = "ws://" + window.location.hostname + ":" + window.location.port + "/wswebapi";
            this.baseurl = prot+"://" + window.location.hostname + ":" + window.location.port + script.attributes.getNamedItem("src").value;
        } 
        else 
        {
            // fail?
            throw new Error("Could not find websocket url");
        }

        this.baseurl = this.baseurl.substring(0, this.baseurl.lastIndexOf("jadex.js")-1);

        this.connections[""] = this.addConnection(this.baseurl);
        //this.connections[undefined] = this.connections[null];
    },
    
    WebsocketCall: function(type, resolve, reject, cb)
    {
    	var self = this;
    	this.type = type;
        this.finished = false;
        this.resolve = resolve;
        this.reject = reject;
        this.cb = cb;

        /**
         *  Resume the listeners of promise.
         */
        this.resume = function(result, exception) 
        {
            if(self.cb != null && (exception == null || exception == undefined) && !SUtil.isTrue(self.finished)) 
            {
                self.cb(result);
            }
            else if(SUtil.isTrue(self.finished)) 
            {
                exception == null || exception == undefined? self.resolve(result): self.reject(exception);
            }
        }
    },

    /**
     *  Internal function to get a web socket for a url.
     */
    getConnection: function(url)
    {
        if(url == null)
            url = "";
        
        var ret = this.connections[url];
        if(ret!=null)
        {
            return ret;
        }
        else
        {
            return this.addConnection(url);
        }
    },

    /**
     *  Add a new server connection.
     *  @param url The url.
     */
    addConnection: function(url)
    {
    	var self = this;
    	
        this.connections[url] = new Promise(function(resolve, reject)
        {
            try
            {
                //var ws = new WebSocket(url);
                var ws = new ReconnectingWebSocket(url);

                ws.onopen = () =>
                {
                    resolve(ws);
                };

                ws.onmessage = message =>
                {
                    self.onMessage(message, url);
                };
            }
            catch(e)
            {
                reject(e);
            }
        });

        return this.connections[url];
    },

    /**
     *  Send a message to the server and create a callid for the answer message.
     */
    sendData: function(url, data)
    {
        this.getConnection(url).then(ws =>
        {
            ws.send(data);
        });
    },

    /**
     *  Send a message to the server in an ongoing conversation.
     */
    sendConversationMessage: function(url, cmd)
    {
        this.getConnection(url).then((ws) =>
        {
            ws.send(JSON.stringify(cmd));
        });
    },

    /**
     *  Send a message to the server and create a callid for the answer message.
     */
    sendMessage: function(url, cmd, type, resolve, reject, callback)
    {
        // todo: use Jadex binary to serialize message and send
        var callid = this.randomString(-1);

        this.outcalls[callid] = new this.WebsocketCall(type, resolve, reject, callback);

        cmd.callid = callid;

        this.sendRawMessage(url, cmd);

        return callid;
    },

    /**
     *  Send a raw message without callid management.
     */
    sendRawMessage: function(url, cmd) 
    {
        if(!cmd.callid)
            console.log("Sending message without callid: "+cmd);

        var data = this.objectToJson(cmd);
        //console.log(data);
        
        //var size = sizeOf(cmd);
        var size = data.length;
        var limit = 7000; // 8192

        // If message is larger than limit slice the message via partial messages
        if(size>limit)
        {
            var cnt = Math.ceil(size/limit);

            for(var i=0; i
        {
            if(value instanceof ArrayBuffer)
            {
                //var ret = window.btoa(value);
                var ret = btoa(String.fromCharCode.apply(null, new Uint8Array(value)));
                return ret;
            }
            else
            {
                return value;
            }
            //return value instanceof ArrayBuffer? window.btoa(value): value;
        };

        return JSON.stringify(object, replacer);
    },
    
    /**
     *  Send a result.
     */
    sendResult: function(url, result, finished, callid)
    {
        var cmd =
        {
            __classname: "org.activecomponents.webservice.messages.ResultMessage",
            callid: callid,
            result: result,
            finished: finished
        };

        this.sendRawMessage(url, cmd);
    },

    /**
     *  Send an exception.
     */
    sendException: function(url, err, finished, callid)
    {
        var exception =
        {
            __classname: "java.lang.RuntimeException",
            message: ""+err
        }

        var cmd =
        {
            __classname: "org.activecomponents.webservice.messages.ResultMessage",
            callid: callid,
            exception: exception,
            finished: finished
        };

        this.sendRawMessage(url, cmd);
    },

    /**
     *  Called when a message arrives.
     */
    onMessage: function(message, url) 
    {
        if(message.type == "message")
        {
            var msg = JsonParser.parse(message.data, url);
            var call = this.outcalls[msg.callid];

//		    console.log("outcalls: "+outcalls);

            if(call!=null)
            {
                if(SUtil.isTrue(msg.finished))
                {
                    delete this.outcalls[msg.callid];
//					console.log("outCall deleted: "+msg.callid);
                    call.finished = true;
                }

                if(call.type=="search")
                {
                    if(msg.result!=null)
                    {
                        if(msg.result.hasOwnProperty("__array"))
                            msg.result = msg.result.__array;
                        if(msg.result.hasOwnProperty("__collection"))
                            msg.result[1] = msg.result[1].__collection;
                    }

                    var serproxy;

                    if(msg.exception==null && msg.result!=null)
                    {
                        serproxy = msg.result;//createServiceProxy(msg.result[0], msg.result[1]);
                    }

                    call.resume(serproxy, msg.exception);
                }
                else if(call.type=="invoke")
                {
                    call.resume(msg.result, msg.exception);
                }
                else if(outCall.type=="provide")
                {
                    if(msg.exception!=null)
                    {
                        call.reject(msg.exception);
                    }
                    else
                    {
                        // Save the service functionality in the inca
                        this.providedServices[SUtil.getServiceIdAsString(msg.result)] = call.cb;
                        
                        call.resolve(msg.result);
                    }
                }
                else if(call.type=="unprovide")
                {
                    if(msg.exception!=null)
                    {
                        call.reject(msg.exception);
                    }
                    else
                    {
                    	// removeProperty?!
                        this.providedServices[SUtil.getServiceIdAsString(msg.result)] = null;
                        call.resolve(msg.result);
                    }
                }
            }
            else // incoming call
            {
                if(msg.__classname==="org.activecomponents.webservice.messages.ServiceInvocationMessage")
                {
                    var service = this.providedServices[SUtil.getServiceIdAsString(msg.serviceId)];

                    if(service)
                    {
                        var res;

                        // If it a service object with functions or just a function
                        if(service[msg.methodName])
                        {
                            //res = service[msg.methodName](msg.parameterValues);
                            res = service[msg.methodName].apply(undefined, msg.parameterValues);
                        }
                        // If it is just a function (assume functional interface as in Java)
                        else if(typeof res==="function")
                        {
                        	//res = service(msg.parameterValues);
                            res = service.apply(undefined, msg.parameterValues);
                        }
                        // If it is an invocation handler with a generic invoke method
                        else if(service.invoke)
                        {
                            res = service.invoke(msg.methodName, msg.parameterValues);
                        }
                        else
                        {
                            console.log("Cannot invoke service method (not found): "+msg.methodName);
                        }
	
						// Hack, seems to loose this in callback :-( 
//						var fthis = this;

                        // Make anything that comes back to a promise
//                        Promise.resolve(res).then(function(res)
//                        {
//                            fthis.sendResult(url, res, true, msg.callid);
//                        })
//                        .catch(function(e)
//                        {
//                            fthis.sendException(url, e, true, msg.callid);
//                        });
                        Promise.resolve(res).then(res =>
                        {
                            this.sendResult(url, res, true, msg.callid);
                        })
                        .catch(e =>
                        {
                            this.sendException(url, e, true, msg.callid);
                        });
                    }
                    else
                    {
                        console.log("Provided service not found: "+[msg.serviceId]);
                        this.sendException(url, "Provided service not found: "+[msg.serviceId], true, msg.callid);
                    }
                }
                else
                {
                    console.log("Received message without request: "+msg);
                }
            }
        }
        else if(message.type == "binary")
        {
            console.log("Binary messages currently not supported");
        }
        // else: do not handle pong messages
    },

    /**
     *  Create a random string.
     *  @param length The length of the string.
     *  @returns The random string.
     */
    randomString: function(length)
    {
        if(length<1)
            length = 10;
        return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1);
    }
};
ConnectionHandler.init();

var Jadex =
{
	/**
	 *  Service Proxy constructor function.
	 */
	ServiceProxy: function(serviceid, methodnames, url)
	{
	    var outer = this;
		this.serviceid = serviceid;
		//this.methodnames = methodnames;
		//this.url = url;
		
		/**
		 *  Create method function (needed to preserve the name).
		 *
		 *  Creates an argument array and invokes generic invoke method.
		 *
		 *  TODO: callback function hack!
		 */
		createMethod = function(name) 
		{
		    return function () 
		    {
		        var params = [];
		        var callback;

		        for(var j = 0; j < arguments.length; j++) 
		        {
		            if(typeof arguments[j] === "function") 
		            {
		                callback = arguments[j];
		            }
		            else 
		            {
		                params.push(arguments[j]);
		            }
		        }
		        return outer.invoke(name, params, callback);
		    }
		}
		
		for(var i = 0; i < methodnames.length; i++) 
	    {
	        this[methodnames[i]] = createMethod(methodnames[i]);
	    }
		
		/**
		 *  Generic invoke method that sends a method call to the server side.
		 */
		this.invoke = function(name, params, callback) 
		{
		    var ret = new Jadex.JadexPromise(this.url);
		    
		    // Convert parameters seperately, one by one
		    var cparams = [];
		    for(var i=0; i 
		    {
		        // console.log("calling intermediate result with: " + intermediateResult);
		        ret.resolveIntermediate(res);
		        if(callback) 
		            callback(res);
		    };
		    ret.callid = ConnectionHandler.sendMessage(this.url, cmd, "invoke", res => ret.resolve(res), ex => ret.reject(ex), wrapcb);

		    return ret;
		}
	},
	
	JadexPromise: function(resolveFunc, rejectFunc, callid)
	{
		this.resolveFunc = resolveFunc;
		this.rejectFunc = rejectFunc;
		this.intermediateResolveCallbacks = [];
		this.callid = callid;
		
		this.promise = new Promise((resolve, reject) => 
		{
            this.resolveFunc = resolve;
            this.rejectFunc = reject;
        });
		
	    this.then = function(onfulfilled, onrejected) 
	    {
	        return this.promise.then(onfulfilled, onrejected);
	    };

	    this.resolve = function(value)
	    {
	        // return this.promise.resolve(value);
	        return this.resolveFunc(value);
	    };

	    this.reject = function(error) 
	    {
	        // return this.promise.reject(error);
	        return this.rejectFunc(error);
	    };

	    this.resolveIntermediate = function(value)
	    {
	        for(var cb of this.intermediateResolveCallbacks)
	        {
	            cb(value);
	        }
	    };

	    this.thenIntermediate = function(onfulfilled, onrejected)//?: (value: T)=>(PromiseLike|TResult), onrejected?: (reason: any)=>(PromiseLike|TResult))
	    {
	        this.intermediateResolveCallbacks.push(onfulfilled);
	        // this.intermediateRejectCallbacks.push(onrejected);
	    };

	    this.terminate = function()
	    {
	        var cmd =
	        {
	            __classname: "org.activecomponents.webservice.messages.ServiceTerminateInvocationMessage",
	            callid: this.callid
	        };

	        ConnectionHandler.sendConversationMessage(this.url, cmd);
	    };

	    this.pull = function()
	    {
	        var cmd =
	        {
	            __classname: "org.activecomponents.webservice.messages.PullResultMessage",
	            callid: this.callid
	        };

	        ConnectionHandler.sendConversationMessage(this.url, cmd);
	    };
	},
	
	ServiceSearchMessage: function(type, multiple, scope)
	{
		this.__classname = "org.activecomponents.webservice.messages.ServiceSearchMessage"; 
		//this.callid = callid;
		this.type = type;
		this.multiple = multiple;
		this.scope = scope;
	},
	
	ServiceInvocationMessage: function(serviceid, methodname, parametervalues)
	{
		this.__classname = "org.activecomponents.webservice.messages.ServiceInvocationMessage"; 
		this.serviceId = serviceid;
		this.methodName = methodname;
		this.parameterValues = parametervalues;
	},
	
	ServiceUnprovideMessage: function(serviceid) 
    {
        this.__classname = "org.activecomponents.webservice.messages.ServiceUnprovideMessage";
		this.serviceId = serviceid;
    },
    
    PartialMessage: function(callid, data, number, count) 
    {
    	this.__classname = "org.activecomponents.webservice.messages.PartialMessage";
    	this.callid = callid;
    	this.data = data;
    	this.number = number;
    	this.count = count;
    },
    
    ServiceProvideMessage: function(type, scope, tags) 
    {
    	this.__classname = "org.activecomponents.webservice.messages.ServiceProvideMessage";
    	this.type = type;
    	this.scope = scope;
    	this.tags = tags;
    },
	
	/**
     *  Get a service from a publication point.
     *  @param type The service type.
     *  @param scope The scope.
     *  @param url The url of the websocket.
     */
    getService: function(type, scope, url) 
    {
    	var self = this;
        var prom = SUtil.addErrHandler(new Promise((resolve, reject) => 
        {
            var cmd = new self.ServiceSearchMessage(type, false, scope != null ? scope : Scopes.SCOPE_PLATFORM);
            ConnectionHandler.sendMessage(url, cmd, "search", resolve, reject, null);
        }));
        return prom;
    },

    /**
     *  Get services from a publication point.
     *  @param type The service type.
     *  @param callback The callback function for the intermediate results.
     *  @param scope The search scope.
     *  @param url The url of the websocket.
     */
    getServices: function(type, callback, scope, url) 
    {
    	var self = this;
        SUtil.assert(callback instanceof Function && callback != null);
        var prom = SUtil.addErrHandler(new Promise((resolve, reject) => 
        {
            var cmd = new self.ServiceSearchMessage(type, true, scope != null ? scope : Scopes.SCOPE_PLATFORM);
            ConnectionHandler.sendMessage(url, cmd, "search", resolve, reject, callback);
        }));
        return prom;
    },

    /**
     *  Provide a new (client) service.
     *  @param type The service type.
     *  @param scope The provision scope.
     *  @param url The url of the websocket.
     */
    provideService: function(type, scope, tags, callback, url)
    {
    	var self = this;
        return SUtil.addErrHandler(new Promise(function(resolve, reject)
        {
            var cmd = new self.ServiceProvideMessage(type, scope!=null? scope: "global", typeof tags === "string"? [tags]: tags);
            ConnectionHandler.sendMessage(url, cmd, "provide", resolve, reject, callback);
        }));
    },

    /**
     *  Unprovide a (client) service.
     *  @param type The service type.
     *  @param scope The provision scope.
     *  @param url The url of the websocket.
     */
    unprovideService: function(sid, url)
    {
    	var self = this;
        return SUtil.addErrHandler(new Promise(function(resolve, reject)
        {
            var cmd = new self.ServiceUnprovideMessage(sid);
            ConnectionHandler.sendMessage(url, cmd, "unprovide", resolve, reject, null);
        }));
    },

    /**
     *  Register a class for json (de)serialization.
     */
    registerClass: function(clazz) 
    {
        JsonParser.registerClass(clazz);
    }
};
var jadex = Jadex;

	




© 2015 - 2024 Weber Informatics LLC | Privacy Policy