diff options
author | Mark Spruiell <mes@zeroc.com> | 2014-03-19 12:45:55 -0700 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2014-03-19 12:45:55 -0700 |
commit | cdcffbcc3c3c052afdeb772ff0167e7a90b525bb (patch) | |
tree | 4f16ee41ef7d33394c44e9db81e4d6cd89908250 /js/src | |
parent | fixing testicedist.py for 5487 (diff) | |
download | ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.bz2 ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.xz ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.zip |
merging javascript branch
Diffstat (limited to 'js/src')
104 files changed, 25737 insertions, 0 deletions
diff --git a/js/src/Glacier2/.gitignore b/js/src/Glacier2/.gitignore new file mode 100644 index 00000000000..bf696523d2d --- /dev/null +++ b/js/src/Glacier2/.gitignore @@ -0,0 +1,7 @@ +PermissionsVerifierF.js +PermissionsVerifier.js +Router.js +RouterF.js +Session.js +SSLInfo.js +Metrics.js diff --git a/js/src/Glacier2/Glacier2.js b/js/src/Glacier2/Glacier2.js new file mode 100644 index 00000000000..a91f42e425a --- /dev/null +++ b/js/src/Glacier2/Glacier2.js @@ -0,0 +1,15 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(){ + require("Glacier2/PermissionsVerifier"); + require("Glacier2/Router"); + require("Glacier2/Session"); + require("Glacier2/SSLInfo"); +}()); diff --git a/js/src/Glacier2/Makefile b/js/src/Glacier2/Makefile new file mode 100644 index 00000000000..d61956f5b3a --- /dev/null +++ b/js/src/Glacier2/Makefile @@ -0,0 +1,40 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +LIBNAME = Glacier2 + +TARGETS = $(call mklibtargets,$(LIBNAME)) + +SLICES = $(SDIR)/PermissionsVerifierF.ice \ + $(SDIR)/PermissionsVerifier.ice \ + $(SDIR)/Router.ice \ + $(SDIR)/RouterF.ice \ + $(SDIR)/Session.ice \ + $(SDIR)/SSLInfo.ice \ + $(SDIR)/Metrics.ice + +SDIR = $(slicedir)/Glacier2 + +GEN_SRCS = $(patsubst $(SDIR)/%.ice, %.js, $(SLICES)) + +SRCS := $(GEN_SRCS) +INSTALL_SRCS := Glacier2.js $(GEN_SRCS) + +include $(top_srcdir)/config/Make.rules.js + +SLICE2JSFLAGS := $(SLICE2JSFLAGS) --ice -I$(slicedir) + +lint:: $(INSTALL_SRCS) + jshint $(LINTFLAGS) $(INSTALL_SRCS) + +install:: all + $(call installlib,$(DESTDIR)$(install_libdir),$(libdir),$(LIBNAME)) + $(call installmodule,$(DESTDIR)$(install_moduledir),$(INSTALL_SRCS),$(LIBNAME)) diff --git a/js/src/Glacier2/Makefile.mak b/js/src/Glacier2/Makefile.mak new file mode 100644 index 00000000000..482e18412ff --- /dev/null +++ b/js/src/Glacier2/Makefile.mak @@ -0,0 +1,39 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +LIBNAME = Glacier2 + +GEN_SRCS = PermissionsVerifierF.js \ + PermissionsVerifier.js \ + Router.js \ + RouterF.js \ + Session.js \ + SSLInfo.js \ + Metrics.js + +SDIR = $(slicedir)\Glacier2 + +SRCS = $(GEN_SRCS) +INSTALL_SRCS = Glacier2.js $(GEN_SRCS) + +!include $(top_srcdir)\config\Make.rules.mak.js + +SLICE2JSFLAGS = $(SLICE2JSFLAGS) --ice -I"$(slicedir)" + +MODULEDIR = $(install_moduledir)\$(LIBNAME) + +install:: all + @if not exist $(MODULEDIR) \ + @echo "Creating $(MODULEDIR)" && \ + mkdir "$(MODULEDIR)" + @for %i in ( $(INSTALL_SRCS) ) do \ + copy %i "$(MODULEDIR)" + copy package.json "$(MODULEDIR)" diff --git a/js/src/Glacier2/package.json b/js/src/Glacier2/package.json new file mode 100644 index 00000000000..f4d3062e38d --- /dev/null +++ b/js/src/Glacier2/package.json @@ -0,0 +1,5 @@ +{ + "name": "Glacier2", + "version": "0.1.0", + "main": "Glacier2.js" +} diff --git a/js/src/Ice/.gitignore b/js/src/Ice/.gitignore new file mode 100644 index 00000000000..77776474437 --- /dev/null +++ b/js/src/Ice/.gitignore @@ -0,0 +1,20 @@ +# Ignore generated files +BuiltinSequences.js +Connection.js +Current.js +Endpoint.js +EndpointTypes.js +Identity.js +LocalException.js +Locator.js +Metrics.js +Process.js +PropertiesAdmin.js +Router.js +ServantLocator.js +SliceChecksumDict.js +Version.js +ConnectionF.js +EndpointF.js +ObjectAdapterF.js +ProcessF.js diff --git a/js/src/Ice/Address.js b/js/src/Ice/Address.js new file mode 100644 index 00000000000..cfeeb2149c1 --- /dev/null +++ b/js/src/Ice/Address.js @@ -0,0 +1,22 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var Address = function(host, port) + { + this.host = host; + this.port = port; + }; + + Ice.Address = Address; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ArrayUtil.js b/js/src/Ice/ArrayUtil.js new file mode 100644 index 00000000000..6389845d69b --- /dev/null +++ b/js/src/Ice/ArrayUtil.js @@ -0,0 +1,130 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + var Slice = global.Slice || {}; + var ArrayUtil = {}; + + ArrayUtil.clone = function(arr) + { + if(arr === undefined) + { + return arr; + } + else if(arr === null) + { + return []; + } + else + { + return arr.slice(); + } + }; + + ArrayUtil.equals = function(v1, v2, equalFn) + { + var i, length; + + if(v1.length != v2.length) + { + return false; + } + + if(equalFn !== undefined && equalFn !== null) + { + for(i = 0, length = v1.length; i < length; ++i) + { + if(!equalFn.call(equalFn, v1[i], v2[i])) + { + return false; + } + } + } + else + { + for(i = 0, length = v1.length; i < length; ++i) + { + if(v1[i] != v2[i]) + { + return false; + } + } + } + + return true; + }; + + ArrayUtil.shuffle = function(arr) + { + for(var i = arr.length; i > 1; --i) + { + var e = arr[i - 1]; + var rand = Math.floor(Math.random() * i); + arr[i - 1] = arr[rand]; + arr[rand] = e; + } + }; + + ArrayUtil.indexOf = function(arr, elem, equalFn) + { + if(equalFn !== undefined && equalFn !== null) + { + for(var i = 0; i < arr.length; ++i) + { + if(equalFn.call(equalFn, arr[i], elem)) + { + return i; + } + } + } + else + { + return arr.indexOf(elem); + } + + return -1; + }; + + ArrayUtil.filter = function(arr, includeFn, obj) + { + obj = obj === undefined ? includeFn : obj; + var result = []; + for(var i = 0; i < arr.length; ++i) + { + if(includeFn.call(obj, arr[i], i, arr)) + { + result.push(arr[i]); + } + } + return result; + }; + + Slice.defineSequence = function(module, name, valueHelper, fixed, elementType) + { + var helper = null; + Object.defineProperty(module, name, + { + get: function() + { + if(helper === null) + { + /*jshint -W061 */ + helper = Ice.StreamHelpers.generateSeqHelper(eval(valueHelper), fixed, eval(elementType)); + /*jshint +W061 */ + } + return helper; + } + }); + }; + + Ice.ArrayUtil = ArrayUtil; + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/AsyncResult.js b/js/src/Ice/AsyncResult.js new file mode 100644 index 00000000000..1f2d5f8c6b2 --- /dev/null +++ b/js/src/Ice/AsyncResult.js @@ -0,0 +1,120 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResultBase"); + require("Ice/Debug"); + require("Ice/Promise"); + require("Ice/Protocol"); + require("Ice/Exception"); + require("Ice/BasicStream"); + + var Ice = global.Ice || {}; + + var AsyncResultBase = Ice.AsyncResultBase; + var Debug = Ice.Debug; + var Promise = Ice.Promise; + var Protocol = Ice.Protocol; + var UserException = Ice.UserException; + var BasicStream = Ice.BasicStream; + + var AsyncResult = Ice.Class(AsyncResultBase, { + __init__: function(communicator, op, connection, proxy, adapter, completedFn, sentFn) + { + // + // AsyncResult can be constructed by a sub-type's prototype, in which case the + // arguments are undefined. + // + AsyncResultBase.call(this, communicator, op, connection, proxy, adapter); + if(communicator !== undefined) + { + this._completed = completedFn; + this._sent = sentFn; + this._is = null; + this._os = communicator !== null ? + new BasicStream(this._instance, Protocol.currentProtocolEncoding, false) : null; + this._state = 0; + this._exception = null; + } + }, + __os: function() + { + return this._os; + }, + __is: function() + { + return this._is; + }, + __startReadParams: function() + { + this._is.startReadEncaps(); + return this._is; + }, + __endReadParams: function() + { + this._is.endReadEncaps(); + }, + __readEmptyParams: function() + { + this._is.skipEmptyEncaps(null); + }, + __readParamEncaps: function() + { + return this._is.readEncaps(null); + }, + __throwUserException: function() + { + Debug.assert((this._state & AsyncResult.Done) !== 0); + if((this._state & AsyncResult.OK) === 0) + { + try + { + this._is.startReadEncaps(); + this._is.throwException(); + } + catch(ex) + { + if(ex instanceof UserException) + { + this._is.endReadEncaps(); + } + throw ex; + } + } + }, + __exception: function(ex) + { + this._state |= AsyncResult.Done; + this._exception = ex; + this._os.resize(0); + this.fail(ex, this); + }, + __response: function() + { + // + // Note: no need to change the state here, specializations are responsible for + // changing the state. + // + + if(this.proxy !== null && this.proxy.ice_isTwoway()) + { + Debug.assert(this._completed !== null); + this._completed(this); + } + } + }); + + AsyncResult.OK = 0x1; + AsyncResult.Done = 0x2; + AsyncResult.Sent = 0x4; + + Ice.AsyncResult = AsyncResult; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/AsyncResultBase.js b/js/src/Ice/AsyncResultBase.js new file mode 100644 index 00000000000..348c715082e --- /dev/null +++ b/js/src/Ice/AsyncResultBase.js @@ -0,0 +1,70 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/Promise"); + require("Ice/Exception"); + + var Ice = global.Ice || {}; + + var Promise = Ice.Promise; + + var AsyncResultBase = Ice.Class(Promise, { + __init__: function(communicator, op, connection, proxy, adapter) + { + // + // AsyncResultBase can be constructed by a sub-type's prototype, in which case the + // arguments are undefined. + // + Promise.call(this); + if(communicator !== undefined) + { + this._communicator = communicator; + this._instance = communicator !== null ? communicator.instance : null; + this._operation = op; + this._connection = connection; + this._proxy = proxy; + this._adapter = adapter; + } + }, + __exception: function(ex) + { + this.fail(ex); + } + }); + + var prototype = AsyncResultBase.prototype; + var defineProperty = Object.defineProperty; + + defineProperty(prototype, "communicator", { + get: function() { return this._communicator; } + }); + + defineProperty(prototype, "connection", { + get: function() { return this._connection; } + }); + + defineProperty(prototype, "proxy", { + get: function() { return this._proxy; } + }); + + defineProperty(prototype, "adapter", { + get: function() { return this._adapter; } + }); + + defineProperty(prototype, "operation", { + get: function() { return this._operation; } + }); + + Ice.AsyncResultBase = AsyncResultBase; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/AsyncStatus.js b/js/src/Ice/AsyncStatus.js new file mode 100644 index 00000000000..e8d3142bcf9 --- /dev/null +++ b/js/src/Ice/AsyncStatus.js @@ -0,0 +1,15 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + var AsyncStatus = {Queued: 0, Sent: 1}; + Ice.AsyncStatus = AsyncStatus; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Base64.js b/js/src/Ice/Base64.js new file mode 100644 index 00000000000..f1f9ec47964 --- /dev/null +++ b/js/src/Ice/Base64.js @@ -0,0 +1,276 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Buffer"); + + var Ice = global.Ice || {}; + + var Buffer = Ice.Buffer; + + var Base64 = {}; + + var _codeA = "A".charCodeAt(0); + var _codea = "a".charCodeAt(0); + var _code0 = "0".charCodeAt(0); + + Base64.encode = function(buf) // Expects native Buffer + { + if(buf === null || buf.length === 0) + { + return ""; + } + + var base64Bytes = (((buf.length * 4) / 3) + 1); + var newlineBytes = (((base64Bytes * 2) / 76) + 1); + var totalBytes = base64Bytes + newlineBytes; + + var v = []; + + var by1; + var by2; + var by3; + var by4; + var by5; + var by6; + var by7; + + for(var i = 0; i < buf.length; i += 3) + { + by1 = buf[i] & 0xff; + by2 = 0; + by3 = 0; + + if((i + 1) < buf.length) + { + by2 = buf[i + 1] & 0xff; + } + + if((i + 2) < buf.length) + { + by3 = buf[i + 2] & 0xff; + } + + by4 = (by1 >> 2) & 0xff; + by5 = (((by1 & 0x3) << 4) | (by2 >> 4)) & 0xff; + by6 = (((by2 & 0xf) << 2) | (by3 >> 6)) & 0xff; + by7 = by3 & 0x3f; + + v.push(encodeChar(by4)); + v.push(encodeChar(by5)); + + if((i + 1) < buf.length) + { + v.push(encodeChar(by6)); + } + else + { + v.push("="); + } + + if((i + 2) < buf.length) + { + v.push(encodeChar(by7)); + } + else + { + v.push("="); + } + } + + var retval = v.join(""); + var outString = []; + var iter = 0; + + while((retval.length - iter) > 76) + { + outString.push(retval.substring(iter, iter + 76)); + outString.push("\r\n"); + iter += 76; + } + + outString.push(retval.substring(iter)); + + return outString.join(""); + }; + + Base64.decode = function(str) // Returns native Buffer + { + var newStr = []; + + for(var j = 0; j < str.length; j++) + { + var c = str.charAt(j); + if(Base64.isBase64(c)) + { + newStr.push(c); + } + } + + if(newStr.length === 0) + { + return null; + } + + // Note: This is how we were previously computing the size of the return + // sequence. The method below is more efficient (and correct). + // size_t lines = str.size() / 78; + // size_t totalBytes = (lines * 76) + (((str.size() - (lines * 78)) * 3) / 4); + + // Figure out how long the final sequence is going to be. + var totalBytes = (newStr.length * 3 / 4) + 1; + + var retval = new Buffer(); + retval.resize(totalBytes); + + var by1; + var by2; + var by3; + var by4; + + var c1; + var c2; + var c3; + var c4; + + var off = 0; + + for(var i = 0; i < newStr.length; i += 4) + { + c1 = "A"; + c2 = "A"; + c3 = "A"; + c4 = "A"; + + c1 = newStr[i]; + + if((i + 1) < newStr.length) + { + c2 = newStr[i + 1]; + } + + if((i + 2) < newStr.length) + { + c3 = newStr[i + 2]; + } + + if((i + 3) < newStr.length) + { + c4 = newStr[i + 3]; + } + + by1 = decodeChar(c1) & 0xff; + by2 = decodeChar(c2) & 0xff; + by3 = decodeChar(c3) & 0xff; + by4 = decodeChar(c4) & 0xff; + + retval.put((by1 << 2) | (by2 >> 4)); + + if(c3 != "=") + { + retval.put(((by2 & 0xf) << 4) | (by3 >> 2)); + } + + if(c4 != "=") + { + retval.put(((by3 & 0x3) << 6) | by4); + } + } + + return retval.remaining > 0 ? retval.getArrayAt(0, retval.position) : retval.getArrayAt(0); + }; + + Base64.isBase64 = function(c) + { + if(c >= 'A' && c <= 'Z') + { + return true; + } + + if(c >= 'a' && c <= 'z') + { + return true; + } + + if(c >= '0' && c <= '9') + { + return true; + } + + if(c == '+') + { + return true; + } + + if(c == '/') + { + return true; + } + + if(c == '=') + { + return true; + } + + return false; + }; + + function encodeChar(uc) + { + if(uc < 26) + { + return String.fromCharCode(_codeA + uc); + } + + if(uc < 52) + { + return String.fromCharCode(_codea + (uc - 26)); + } + + if(uc < 62) + { + return String.fromCharCode(_code0 + (uc - 52)); + } + + if(uc == 62) + { + return "+"; + } + + return "/"; + } + + function decodeChar(c) + { + if(c >= 'A' && c <= 'Z') + { + return c.charCodeAt(0) - _codeA; + } + + if(c >= 'a' && c <= 'z') + { + return c.charCodeAt(0) - _codea + 26; + } + + if(c >= '0' && c <= '9') + { + return c.charCodeAt(0) - _code0 + 52; + } + + if(c == '+') + { + return 62; + } + + return 63; + } + + Ice.Base64 = Base64; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/BasicStream.js b/js/src/Ice/BasicStream.js new file mode 100644 index 00000000000..db423be4c91 --- /dev/null +++ b/js/src/Ice/BasicStream.js @@ -0,0 +1,2961 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/FormatType"); + require("Ice/HashMap"); + require("Ice/Object"); + require("Ice/OptionalFormat"); + require("Ice/Protocol"); + require("Ice/TraceUtil"); + require("Ice/Buffer"); + require("Ice/Exception"); + require("Ice/LocalException"); + require("Ice/Version"); + require("Ice/CompactIdRegistry"); + require("Ice/ArrayUtil"); + require("Ice/UnknownSlicedObject"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var FormatType = Ice.FormatType; + var HashMap = Ice.HashMap; + var IceObject = Ice.Object; + var OptionalFormat = Ice.OptionalFormat; + var Protocol = Ice.Protocol; + var TraceUtil = Ice.TraceUtil; + var ArrayUtil = Ice.ArrayUtil; + var SlicedData = Ice.SlicedData; + + var SliceType = {}; + SliceType.NoSlice = 0; + SliceType.ObjectSlice = 1; + SliceType.ExceptionSlice = 2; + + var OPTIONAL_END_MARKER = 0xFF; + var FLAG_HAS_TYPE_ID_STRING = (1<<0); + var FLAG_HAS_TYPE_ID_INDEX = (1<<1); + var FLAG_HAS_TYPE_ID_COMPACT = (1<<1 | 1<<0); + var FLAG_HAS_OPTIONAL_MEMBERS = (1<<2); + var FLAG_HAS_INDIRECTION_TABLE = (1<<3); + var FLAG_HAS_SLICE_SIZE = (1<<4); + var FLAG_IS_LAST_SLICE = (1<<5); + + var IndirectPatchEntry = function(index, patcher) + { + this.index = index; + this.patcher = patcher; + }; + + var Class = Ice.Class; + + var EncapsDecoder = Class({ + __init__: function(stream, encaps, sliceObjects, f) + { + this._stream = stream; + this._encaps = encaps; + this._sliceObjects = sliceObjects; + this._servantFactoryManager = f; + this._patchMap = null; // Lazy initialized, HashMap<int, Patcher[] >() + this._unmarshaledMap = new HashMap(); // HashMap<int, Ice.Object>() + this._typeIdMap = null; // Lazy initialized, HashMap<int, String> + this._typeIdIndex = 0; + this._objectList = null; // Lazy initialized. Ice.Object[] + }, + readOpt: function() + { + return false; + }, + readPendingObjects: function() + { + }, + readTypeId: function(isIndex) + { + var typeId, index; + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new HashMap(); // Map<int, String>(); + } + + if(isIndex) + { + index = this._stream.readSize(); + typeId = this._typeIdMap.get(index); + if(typeId === undefined) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + typeId = this._stream.readString(); + this._typeIdMap.set(++this._typeIdIndex, typeId); + } + return typeId; + }, + newInstance: function(typeId) + { + // + // Try to find a factory registered for the specific type. + // + var userFactory = this._servantFactoryManager.find(typeId); + var v = null; + + if(userFactory !== undefined) + { + v = userFactory.create(typeId); + } + + // + // If that fails, invoke the default factory if one has been + // registered. + // + if(v === null || v === undefined) + { + userFactory = this._servantFactoryManager.find(""); + if(userFactory !== undefined) + { + v = userFactory.create(typeId); + } + } + + // + // Last chance: try to instantiate the class dynamically. + // + if(v === null || v === undefined) + { + v = this._stream.createObject(typeId); + } + + return v; + }, + addPatchEntry: function(index, patcher) + { + Debug.assert(index > 0); + // + // Check if already un-marshalled the object. If that's the case, + // just patch the object smart pointer and we're done. + // + var obj = this._unmarshaledMap.get(index); + if(obj !== undefined && obj !== null) + { + patcher.call(null, obj); + return; + } + + if(this._patchMap === null) // Lazy initialization + { + this._patchMap = new HashMap(); // HashMap<Integer, Patcher[] >(); + } + + // + // Add patch entry if the object isn't un-marshalled yet, + // the smart pointer will be patched when the instance is + // un-marshalled. + // + var l = this._patchMap.get(index); + if(l === undefined) + { + // + // We have no outstanding instances to be patched for this + // index, so make a new entry in the patch map. + // + l = []; // Patcher[]; + this._patchMap.set(index, l); + } + + // + // Append a patch entry for this instance. + // + l.push(patcher); + }, + unmarshal: function(index, v) + { + var i, length, l; + // + // Add the object to the map of un-marshalled objects, this must + // be done before reading the objects (for circular references). + // + this._unmarshaledMap.set(index, v); + + // + // Read the object. + // + v.__read(this._stream); + if(this._patchMap !== null) + { + // + // Patch all instances now that the object is un-marshalled. + // + l = this._patchMap.get(index); + if(l !== undefined) + { + Debug.assert(l.length > 0); + // + // Patch all pointers that refer to the instance. + // + for(i = 0, length = l.length; i < length; ++i) + { + l[i](v); + } + // + // Clear out the patch map for that index -- there is nothing left + // to patch for that index for the time being. + // + this._patchMap.delete(index); + } + } + + if((this._patchMap === null || this._patchMap.size === 0) && this._objectList === null) + { + try + { + v.ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_postUnmarshal:\n" + + ExUtil.toString(ex)); + } + } + else + { + if(this._objectList === null) // Lazy initialization + { + this._objectList = []; // Ice.Object[] + } + this._objectList.push(v); + + if(this._patchMap === null || this._patchMap.size === 0) + { + // + // Iterate over the object list and invoke ice_postUnmarshal on + // each object. We must do this after all objects have been + // unmarshaled in order to ensure that any object data members + // have been properly patched. + // + for(i = 0, length = this._objectList.length; i < length; i++) + { + try + { + this._objectList[i].ice_postUnmarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning( + "exception raised by ice_postUnmarshal:\n" + ExUtil.toString(ex)); + } + } + this._objectList = []; + } + } + } + }); + + var EncapsDecoder10 = Class(EncapsDecoder, { + __init__: function(stream, encaps, sliceObjects, f) + { + EncapsDecoder.call(this, stream, encaps, sliceObjects, f); + this._sliceType = SliceType.NoSlice; + }, + readObject: function(patcher) + { + Debug.assert(patcher !== null); + + // + // Object references are encoded as a negative integer in 1.0. + // + var index = this._stream.readInt(); + if(index > 0) + { + throw new Ice.MarshalException("invalid object id"); + } + index = -index; + + if(index === 0) + { + patcher.call(null, null); + } + else + { + this.addPatchEntry(index, patcher); + } + }, + throwException: function() + { + Debug.assert(this._sliceType === SliceType.NoSlice); + + // + // User exception with the 1.0 encoding start with a boolean flag + // that indicates whether or not the exception has classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + var usesClasses = this._stream.readBool(); + this._sliceType = SliceType.ExceptionSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + var mostDerivedId = this._typeId; + while(true) + { + var userEx = this._stream.createUserException(this._typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx.__read(this._stream); + if(usesClasses) + { + this.readPendingObjects(); + } + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + try + { + this.startSlice(); + } + catch(ex) + { + // + // An oversight in the 1.0 encoding means there is no marker to indicate + // the last slice of an exception. As a result, we just try to read the + // next type ID, which raises UnmarshalOutOfBoundsException when the + // input buffer underflows. + // + // Set the reason member to a more helpful message. + // + if(ex instanceof Ice.UnmarshalOutOfBoundsException) + { + ex.reason = "unknown exception type `" + mostDerivedId + "'"; + } + throw ex; + } + } + }, + startInstance: function(sliceType) + { + Debug.assert(this._sliceType === sliceType); + this._skipFirstSlice = true; + }, + endInstance: function(/*preserve*/) + { + var sz; + // + // Read the Ice::Object slice. + // + if(this._sliceType === SliceType.ObjectSlice) + { + this.startSlice(); + sz = this._stream.readSize(); // For compatibility with the old AFM. + if(sz !== 0) + { + throw new Ice.MarshalException("invalid Object slice"); + } + this.endSlice(); + } + + this._sliceType = SliceType.NoSlice; + return null; + }, + startSlice: function() + { + var isIndex; + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._skipFirstSlice) + { + this._skipFirstSlice = false; + return this._typeId; + } + + // + // For objects, first read the type ID boolean which indicates + // whether or not the type ID is encoded as a string or as an + // index. For exceptions, the type ID is always encoded as a + // string. + // + if(this._sliceType === SliceType.ObjectSlice) // For exceptions, the type ID is always encoded as a string + { + isIndex = this._stream.readBool(); + this._typeId = this.readTypeId(isIndex); + } + else + { + this._typeId = this._stream.readString(); + } + + this._sliceSize = this._stream.readInt(); + if(this._sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return this._typeId; + }, + endSlice: function() + { + }, + skipSlice: function() + { + if(this._stream.instance.traceLevels().slicing > 0) + { + var logger = this._stream.instance.initializationData().logger; + if(this._sliceType === SliceType.ObjectSlice) + { + TraceUtil.traceSlicing("object", this._typeId, this._stream.instance.traceLevels().slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("exception", this._typeId, this._stream.instance.traceLevels().slicingCat, logger); + } + } + Debug.assert(this._sliceSize >= 4); + this._stream.skip(this._sliceSize - 4); + }, + readPendingObjects: function() + { + var k, num; + do + { + num = this._stream.readSize(); + for(k = num; k > 0; --k) + { + this.readInstance(); + } + } + while(num > 0); + + if(this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + }, + readInstance: function() + { + var index = this._stream.readInt(), + mostDerivedId, + v = null; + + if(index <= 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + this._sliceType = SliceType.ObjectSlice; + this._skipFirstSlice = false; + + // + // Read the first slice header. + // + this.startSlice(); + mostDerivedId = this._typeId; + while(true) + { + // + // For the 1.0 encoding, the type ID for the base Object class + // marks the last slice. + // + if(this._typeId == IceObject.ice_staticId()) + { + throw new Ice.NoObjectFactoryException("", mostDerivedId); + } + + v = this.newInstance(this._typeId); + + // + // We found a factory, we get out of this loop. + // + if(v) + { + break; + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!this._sliceObjects) + { + throw new Ice.NoObjectFactoryException("object slicing is disabled", this._typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object and add-it to the map of un-marshaled objects. + // + this.unmarshal(index, v); + } + }); + + var EncapsDecoder11 = Class(EncapsDecoder, { + __init__: function(stream, encaps, sliceObjects, f) + { + EncapsDecoder.call(this, stream, encaps, sliceObjects, f); + this._current = null; + this._objectIdIndex = 1; + }, + readObject: function(patcher) + { + Debug.assert(patcher !== undefined); + var index = this._stream.readSize(); + + if(index < 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + if(index === 0) + { + if(patcher !== null) + { + patcher.call(null, null); + } + } + else if(this._current !== null && (this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + // + // When reading an object within a slice and there's an + // indirect object table, always read an indirect reference + // that points to an object from the indirect object table + // marshaled at the end of the Slice. + // + // Maintain a list of indirect references. Note that the + // indirect index starts at 1, so we decrement it by one to + // derive an index into the indirection table that we'll read + // at the end of the slice. + // + if(patcher !== null) + { + if(this._current.indirectPatchList === null) // Lazy initialization + { + this._current.indirectPatchList = []; // IndirectPatchEntry[] + } + var e = new IndirectPatchEntry(); + e.index = index - 1; + e.patcher = patcher; + this._current.indirectPatchList.push(e); + } + } + else + { + this.readInstance(index, patcher); + } + }, + throwException: function() + { + Debug.assert(this._current === null); + this.push(SliceType.ExceptionSlice); + + // + // Read the first slice header. + // + this.startSlice(); + var mostDerivedId = this._current.typeId; + while(true) + { + + var userEx = this._stream.createUserException(this._current.typeId); + + // + // We found the exception. + // + if(userEx !== null) + { + userEx.__read(this._stream); + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + + if((this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0) + { + if(mostDerivedId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(mostDerivedId.substr(2)); + } + throw new Ice.UnknownUserException(mostDerivedId); + } + + this.startSlice(); + } + }, + startInstance: function(sliceType) + { + Debug.assert(sliceType !== undefined); + Debug.assert(this._current.sliceType !== null && this._current.sliceType === sliceType); + this._current.skipFirstSlice = true; + }, + endInstance: function(preserve) + { + var slicedData = null; + if(preserve) + { + slicedData = this.readSlicedData(); + } + if(this._current.slices !== null) + { + this._current.slices.length = 0; // Clear the array. + this._current.indirectionTables.length = 0; // Clear the array. + } + this._current = this._current.previous; + return slicedData; + }, + startSlice: function() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(this._current.skipFirstSlice) + { + this._current.skipFirstSlice = false; + return this._current.typeId; + } + + this._current.sliceFlags = this._stream.readByte(); + + // + // Read the type ID, for object slices the type ID is encoded as a + // string or as an index, for exceptions it's always encoded as a + // string. + // + if(this._current.sliceType === SliceType.ObjectSlice) + { + if((this._current.sliceFlags & FLAG_HAS_TYPE_ID_COMPACT) === FLAG_HAS_TYPE_ID_COMPACT) // Must be checked 1st! + { + this._current.typeId = ""; + this._current.compactId = this._stream.readSize(); + } + else if((this._current.sliceFlags & (FLAG_HAS_TYPE_ID_INDEX | FLAG_HAS_TYPE_ID_STRING)) !== 0) + { + this._current.typeId = this.readTypeId((this._current.sliceFlags & FLAG_HAS_TYPE_ID_INDEX) !== 0); + this._current.compactId = -1; + } + else + { + // Only the most derived slice encodes the type ID for the compact format. + this._current.typeId = ""; + this._current.compactId = -1; + } + } + else + { + this._current.typeId = this._stream.readString(); + this._current.compactId = -1; + } + + // + // Read the slice size if necessary. + // + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + this._current.sliceSize = this._stream.readInt(); + if(this._current.sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + this._current.sliceSize = 0; + } + return this._current.typeId; + }, + endSlice: function() + { + var e, + i, + indirectionTable = [], + length; + + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.skipOpts(); + } + + // + // Read the indirection table if one is present and transform the + // indirect patch list into patch entries with direct references. + // + if((this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + // + // The table is written as a sequence<size> to conserve space. + // + length = this._stream.readAndCheckSeqSize(1); + for(i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + + // + // Sanity checks. If there are optional members, it's possible + // that not all object references were read if they are from + // unknown optional data members. + // + if(indirectionTable.length === 0) + { + throw new Ice.MarshalException("empty indirection table"); + } + if((this._current.indirectPatchList === null || this._current.indirectPatchList.length === 0) && + (this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) === 0) + { + throw new Ice.MarshalException("no references to indirection table"); + } + + // + // Convert indirect references into direct references. + // + if(this._current.indirectPatchList !== null) + { + for(i = 0, length = this._current.indirectPatchList.length; i < length; ++i) + { + e = this._current.indirectPatchList[i]; + Debug.assert(e.index >= 0); + if(e.index >= indirectionTable.length) + { + throw new Ice.MarshalException("indirection out of range"); + } + this.addPatchEntry(indirectionTable[e.index], e.patcher); + } + this._current.indirectPatchList.length = 0; + } + } + }, + skipSlice: function() + { + if(this._stream.instance.traceLevels().slicing > 0) + { + var logger = this._stream.instance.initializationData().logger; + var slicingCat = this._stream.instance.traceLevels().slicingCat; + if(this._current.sliceType === SliceType.ExceptionSlice) + { + TraceUtil.traceSlicing("exception", this._current.typeId, slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("object", this._current.typeId, slicingCat, logger); + } + } + + var start = this._stream.pos; + + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + Debug.assert(this._current.sliceSize >= 4); + this._stream.skip(this._current.sliceSize - 4); + } + else + { + if(this._current.sliceType === SliceType.ObjectSlice) + { + throw new Ice.NoObjectFactoryException( + "compact format prevents slicing (the sender should use the sliced format instead)", + this._current.typeId); + } + + if(this._current.typeId.indexOf("::") === 0) + { + throw new Ice.UnknownUserException(this._current.typeId.substring(2)); + } + + throw new Ice.UnknownUserException(this._current.typeId); + } + + // + // Preserve this slice. + // + var info = new Ice.SliceInfo(); + info.typeId = this._current.typeId; + info.compactId = this._current.compactId; + info.hasOptionalMembers = (this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0; + info.isLastSlice = (this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0; + + var b = this._stream._buf; + var end = b.position; + var dataEnd = end; + if(info.hasOptionalMembers) + { + // + // Don't include the optional member end marker. It will be re-written by + // endSlice when the sliced data is re-written. + // + --dataEnd; + } + + b.position = start; + info.bytes = b.getArray(dataEnd - start); + b.position = end; + + if(this._current.slices === null) // Lazy initialization + { + this._current.slices = []; // Ice.SliceInfo[] + this._current.indirectionTables = []; // int[] + } + + // + // Read the indirect object table. We read the instances or their + // IDs if the instance is a reference to an already un-marhsaled + // object. + // + // The SliceInfo object sequence is initialized only if + // readSlicedData is called. + // + + if((this._current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) !== 0) + { + var length = this._stream.readAndCheckSeqSize(1); + var indirectionTable = []; + for(var i = 0; i < length; ++i) + { + indirectionTable[i] = this.readInstance(this._stream.readSize(), null); + } + this._current.indirectionTables.push(indirectionTable); + } + else + { + this._current.indirectionTables.push(null); + } + this._current.slices.push(info); + }, + readOpt: function(readTag, expectedFormat) + { + if(this._current === null) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + return this._stream.readOptImpl(readTag, expectedFormat); + } + return false; + }, + readInstance: function(index, patcher) + { + Debug.assert(index > 0); + + var mostDerivedId, + v = null; + + if(index > 1) + { + if(patcher !== null) + { + this.addPatchEntry(index, patcher); + } + return index; + } + + this.push(SliceType.ObjectSlice); + + // + // Get the object ID before we start reading slices. If some + // slices are skiped, the indirect object table are still read and + // might read other objects. + // + index = ++this._objectIdIndex; + + // + // Read the first slice header. + // + this.startSlice(); + mostDerivedId = this._current.typeId; + while(true) + { + if(this._current.compactId >= 0) + { + // + // Translate a compact (numeric) type ID into a string type ID. + // + this._current.typeId = ""; + if(this._current.typeId.length === 0) + { + this._current.typeId = this._stream.getTypeId(this._current.compactId); + } + } + + if(this._current.typeId.length > 0) + { + v = this.newInstance(this._current.typeId); + // + // We found a factory, we get out of this loop. + // + if(v !== null && v !== undefined) + { + break; + } + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!this._sliceObjects) + { + throw new Ice.NoObjectFactoryException("object slicing is disabled", this._current.typeId); + } + + // + // Slice off what we don't understand. + // + this.skipSlice(); + // + // If this is the last slice, keep the object as an opaque + // UnknownSlicedData object. + // + if((this._current.sliceFlags & FLAG_IS_LAST_SLICE) !== 0) + { + v = new Ice.UnknownSlicedObject(mostDerivedId); + break; + } + + this.startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object + // + this.unmarshal(index, v); + if(this._current === null && this._patchMap !== null && this._patchMap.size !== 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + + if(patcher !== null) + { + patcher.call(null, v); + } + return index; + }, + readSlicedData: function() + { + var i, ii, table, info, j, jj; + + if(this._current.slices === null) // No preserved slices. + { + return null; + } + // + // The _indirectionTables member holds the indirection table for each slice + // in _slices. + // + Debug.assert(this._current.slices.length === this._current.indirectionTables.length); + for(i = 0, ii = this._current.slices.length; i < ii; ++i) + { + // + // We use the "objects" list in SliceInfo to hold references + // to the target objects. Note that the objects might not have + // been read yet in the case of a circular reference to an + // enclosing object. + // + table = this._current.indirectionTables[i]; + info = this._current.slices[i]; + info.objects = []; + jj = table ? table.length : 0; + for(j = 0; j < jj; ++j) + { + this.addPatchEntry(table[j], sequencePatcher(info.objects, j, IceObject)); + } + } + return new SlicedData(ArrayUtil.clone(this._current.slices)); + }, + push: function(sliceType) + { + if(this._current === null) + { + this._current = new EncapsDecoder11.InstanceData(null); + } + else + { + this._current = !this._current.next ? new EncapsDecoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.skipFirstSlice = false; + } + }); + + EncapsDecoder11.InstanceData = function(previous) + { + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.skipFirstSlice = false; + this.slices = null; // Preserved slices. Ice.SliceInfo[] + this.indirectionTables = null; // int[] + + // Slice attributes + this.sliceFlags = 0; + this.sliceSize = 0; + this.typeId = null; + this.compactId = 0; + this.indirectPatchList = null; // Lazy initialized, IndirectPatchEntry[] + }; + + var sequencePatcher = function(seq, index, T){ + return function(v) + { + if(v !== null && !(v instanceof T)) + { + ExUtil.throwUOE(T.ice_staticId(), v); + } + seq[index] = v; + }; + }; + + var EncapsEncoder = Class({ + __init__: function(stream, encaps) + { + this._stream = stream; + this._encaps = encaps; + this._marshaledMap = new HashMap(); // HashMap<Ice.Object, int>; + this._typeIdMap = null; // Lazy initialized. HashMap<String, int> + this._typeIdIndex = 0; + }, + writeOpt: function() + { + return false; + }, + writePendingObjects: function() + { + return undefined; + }, + registerTypeId: function(typeId) + { + if(this._typeIdMap === null) // Lazy initialization + { + this._typeIdMap = new HashMap(); // HashMap<String, int> + } + + var p = this._typeIdMap.get(typeId); + if(p !== undefined) + { + return p; + } + this._typeIdMap.set(typeId, ++this._typeIdIndex); + return -1; + } + }); + + var EncapsEncoder10 = Class(EncapsEncoder, { + __init__: function(stream, encaps) + { + EncapsEncoder.call(this, stream, encaps); + // Instance attributes + this._sliceType = SliceType.NoSlice; + this._writeSlice = 0; // Position of the slice data members + // Encapsulation attributes for object marshalling. + this._objectIdIndex = 0; + this._toBeMarshaledMap = new HashMap(); // HashMap<Ice.Object, Integer>(); + }, + writeObject: function(v) + { + Debug.assert(v !== undefined); + // + // Object references are encoded as a negative integer in 1.0. + // + if(v !== null) + { + this._stream.writeInt(-this.registerObject(v)); + } + else + { + this._stream.writeInt(0); + } + }, + writeUserException: function(v) + { + Debug.assert(v !== null && v !== undefined); + // + // User exception with the 1.0 encoding start with a boolean + // flag that indicates whether or not the exception uses + // classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + var usesClasses = v.__usesClasses(); + this._stream.writeBool(usesClasses); + v.__write(this._stream); + if(usesClasses) + { + this.writePendingObjects(); + } + }, + startInstance: function(sliceType) + { + this._sliceType = sliceType; + }, + endInstance: function() + { + if(this._sliceType === SliceType.ObjectSlice) + { + // + // Write the Object slice. + // + this.startSlice(IceObject.ice_staticId(), -1, true); + this._stream.writeSize(0); // For compatibility with the old AFM. + this.endSlice(); + } + this._sliceType = SliceType.NoSlice; + }, + startSlice: function(typeId) + { + // + // For object slices, encode a boolean to indicate how the type ID + // is encoded and the type ID either as a string or index. For + // exception slices, always encode the type ID as a string. + // + if(this._sliceType === SliceType.ObjectSlice) + { + var index = this.registerTypeId(typeId); + if(index < 0) + { + this._stream.writeBool(false); + this._stream.writeString(typeId); + } + else + { + this._stream.writeBool(true); + this._stream.writeSize(index); + } + } + else + { + this._stream.writeString(typeId); + } + + this._stream.writeInt(0); // Placeholder for the slice length. + + this._writeSlice = this._stream.pos; + }, + endSlice: function() + { + // + // Write the slice length. + // + var sz = this._stream.pos - this._writeSlice + 4; + this._stream.rewriteInt(sz, this._writeSlice - 4); + }, + writePendingObjects: function() + { + var self = this, + writeCB = function(key, value) + { + // + // Ask the instance to marshal itself. Any new class + // instances that are triggered by the classes marshaled + // are added to toBeMarshaledMap. + // + self._stream.writeInt(value); + + try + { + key.ice_preMarshal(); + } + catch(ex) + { + self._stream.instance.initializationData().logger.warning( + "exception raised by ice_preMarshal:\n" + ExUtil.toString(ex)); + } + + key.__write(self._stream); + }, + savedMap; + + while(this._toBeMarshaledMap.size > 0) + { + // + // Consider the to be marshalled objects as marshalled now, + // this is necessary to avoid adding again the "to be + // marshalled objects" into _toBeMarshaledMap while writing + // objects. + // + this._marshaledMap.merge(this._toBeMarshaledMap); + + savedMap = this._toBeMarshaledMap; + this._toBeMarshaledMap = new HashMap(); // HashMap<Ice.Object, int>(); + this._stream.writeSize(savedMap.size); + savedMap.forEach(writeCB); + } + this._stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances. + }, + registerObject: function(v) + { + Debug.assert(v !== null); + + // + // Look for this instance in the to-be-marshaled map. + // + var p = this._toBeMarshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // Didn't find it, try the marshaled map next. + // + p = this._marshaledMap.get(v); + if(p !== undefined) + { + return p; + } + + // + // We haven't seen this instance previously, create a new + // index, and insert it into the to-be-marshaled map. + // + this._toBeMarshaledMap.set(v, ++this._objectIdIndex); + return this._objectIdIndex; + } + }); + + var EncapsEncoder11 = Class(EncapsEncoder, { + __init__: function(stream, encaps) + { + EncapsEncoder.call(this, stream, encaps); + this._current = null; + this._objectIdIndex = 1; + }, + writeObject: function(v) + { + Debug.assert(v !== undefined); + var index, idx; + if(v === null) + { + this._stream.writeSize(0); + } + else if(this._current !== null && this._encaps.format === FormatType.SlicedFormat) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Object[] + this._current.indirectionMap = new HashMap(); // HashMap<Ice.Object, int> + } + + // + // If writting an object within a slice and using the sliced + // format, write an index from the object indirection + // table. The indirect object table is encoded at the end of + // each slice and is always read (even if the Slice is + // unknown). + // + index = this._current.indirectionMap.get(v); + if(index === undefined) + { + this._current.indirectionTable.push(v); + idx = this._current.indirectionTable.length; // Position + 1 (0 is reserved for nil) + this._current.indirectionMap.set(v, idx); + this._stream.writeSize(idx); + } + else + { + this._stream.writeSize(index); + } + } + else + { + this.writeInstance(v); // Write the instance or a reference if already marshaled. + } + }, + writePendingObjects: function() + { + return undefined; + }, + writeUserException: function(v) + { + Debug.assert(v !== null && v !== undefined); + v.__write(this._stream); + }, + startInstance: function(sliceType, data) + { + if(this._current === null) + { + this._current = new EncapsEncoder11.InstanceData(null); + } + else + { + this._current = (this._current.next === null) ? new EncapsEncoder11.InstanceData(this._current) : this._current.next; + } + this._current.sliceType = sliceType; + this._current.firstSlice = true; + + if(data !== null && data !== undefined) + { + this.writeSlicedData(data); + } + }, + endInstance: function() + { + this._current = this._current.previous; + }, + startSlice: function(typeId, compactId, last) + { + Debug.assert((this._current.indirectionTable === null || this._current.indirectionTable.length === 0) && + (this._current.indirectionMap === null || this._current.indirectionMap.size === 0)); + + this._current.sliceFlagsPos = this._stream.pos; + + this._current.sliceFlags = 0; + if(this._encaps.format === FormatType.SlicedFormat) + { + this._current.sliceFlags |= FLAG_HAS_SLICE_SIZE; // Encode the slice size if using the sliced format. + } + if(last) + { + this._current.sliceFlags |= FLAG_IS_LAST_SLICE; // This is the last slice. + } + + this._stream.writeByte(0); // Placeholder for the slice flags + + // + // For object slices, encode the flag and the type ID either as a + // string or index. For exception slices, always encode the type + // ID a string. + // + if(this._current.sliceType === SliceType.ObjectSlice) + { + // + // Encode the type ID (only in the first slice for the compact + // encoding). + // + if(this._encaps.format === FormatType.SlicedFormat || this._current.firstSlice) + { + if(compactId >= 0) + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_COMPACT; + this._stream.writeSize(compactId); + } + else + { + var index = this.registerTypeId(typeId); + if(index < 0) + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_STRING; + this._stream.writeString(typeId); + } + else + { + this._current.sliceFlags |= FLAG_HAS_TYPE_ID_INDEX; + this._stream.writeSize(index); + } + } + } + } + else + { + this._stream.writeString(typeId); + } + + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + this._stream.writeInt(0); // Placeholder for the slice length. + } + + this._current.writeSlice = this._stream.pos; + this._current.firstSlice = false; + }, + endSlice: function() + { + var sz, i, length; + + // + // Write the optional member end marker if some optional members + // were encoded. Note that the optional members are encoded before + // the indirection table and are included in the slice size. + // + if((this._current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) !== 0) + { + this._stream.writeByte(OPTIONAL_END_MARKER); + } + + // + // Write the slice length if necessary. + // + if((this._current.sliceFlags & FLAG_HAS_SLICE_SIZE) !== 0) + { + sz = this._stream.pos - this._current.writeSlice + 4; + this._stream.rewriteInt(sz, this._current.writeSlice - 4); + } + + // + // Only write the indirection table if it contains entries. + // + if(this._current.indirectionTable !== null && this._current.indirectionTable.length !== 0) + { + Debug.assert(this._encaps.format === FormatType.SlicedFormat); + this._current.sliceFlags |= FLAG_HAS_INDIRECTION_TABLE; + + // + // Write the indirection object table. + // + this._stream.writeSize(this._current.indirectionTable.length); + for(i = 0, length = this._current.indirectionTable.length; i < length; ++i) + { + this.writeInstance(this._current.indirectionTable[i]); + } + this._current.indirectionTable.length = 0; // Faster way to clean array in JavaScript + this._current.indirectionMap.clear(); + } + + // + // Finally, update the slice flags. + // + this._stream.rewriteByte(this._current.sliceFlags, this._current.sliceFlagsPos); + }, + writeOpt: function(tag, format) + { + if(this._current === null) + { + return this._stream.writeOptImpl(tag, format); + } + + if(this._stream.writeOptImpl(tag, format)) + { + this._current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + return true; + } + + return false; + }, + writeSlicedData: function(slicedData) + { + Debug.assert(slicedData !== null && slicedData !== undefined); + + // + // We only remarshal preserved slices if we are using the sliced + // format. Otherwise, we ignore the preserved slices, which + // essentially "slices" the object into the most-derived type + // known by the sender. + // + if(this._encaps.format !== FormatType.SlicedFormat) + { + return; + } + + var i, ii, info, + j, jj; + + for(i = 0, ii = slicedData.slices.length; i < ii; ++i) + { + info = slicedData.slices[i]; + this.startSlice(info.typeId, info.compactId, info.isLastSlice); + + // + // Write the bytes associated with this slice. + // + this._stream.writeBlob(info.bytes); + + if(info.hasOptionalMembers) + { + this._current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + } + + // + // Make sure to also re-write the object indirection table. + // + if(info.objects !== null && info.objects.length > 0) + { + if(this._current.indirectionTable === null) // Lazy initialization + { + this._current.indirectionTable = []; // Ice.Object[] + this._current.indirectionMap = new HashMap(); // HashMap<Ice.Object, int> + } + + for(j = 0, jj = info.objects.length; j < jj; ++j) + { + this._current.indirectionTable.push(info.objects[j]); + } + } + + this.endSlice(); + } + }, + writeInstance: function(v) + { + Debug.assert(v !== null && v !== undefined); + + // + // If the instance was already marshaled, just write it's ID. + // + var p = this._marshaledMap.get(v); + if(p !== undefined) + { + this._stream.writeSize(p); + return; + } + + // + // We haven't seen this instance previously, create a new ID, + // insert it into the marshaled map, and write the instance. + // + this._marshaledMap.set(v, ++this._objectIdIndex); + + try + { + v.ice_preMarshal(); + } + catch(ex) + { + this._stream.instance.initializationData().logger.warning("exception raised by ice_preMarshal:\n" + + ExUtil.toString(ex)); + } + + this._stream.writeSize(1); // Object instance marker. + v.__write(this._stream); + } + }); + + EncapsEncoder11.InstanceData = function(previous) + { + Debug.assert(previous !== undefined); + if(previous !== null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + + // Instance attributes + this.sliceType = null; + this.firstSlice = false; + + // Slice attributes + this.sliceFlags = 0; + this.writeSlice = 0; // Position of the slice data members + this.sliceFlagsPos = 0; // Position of the slice flags + this.indirectionTable = null; // Ice.Object[] + this.indirectionMap = null; // HashMap<Ice.Object, int> + }; + + var ReadEncaps = Class({ + __init__: function() + { + this.start = 0; + this.sz = 0; + this.encoding = null; + this.encoding_1_0 = false; + this.decoder = null; + this.next = null; + }, + reset: function() + { + this.decoder = null; + }, + setEncoding: function(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } + }); + + var WriteEncaps = Class({ + __init__: function() + { + this.start = 0; + this.format = FormatType.DefaultFormat; + this.encoding = null; + this.encoding_1_0 = false; + this.encoder = null; + this.next = null; + }, + reset: function() + { + this.encoder = null; + }, + setEncoding: function(encoding) + { + this.encoding = encoding; + this.encoding_1_0 = encoding.equals(Ice.Encoding_1_0); + } + }); + + var BasicStream = Class({ + __init__: function(instance, encoding, unlimited, data) + { + this._instance = instance; + this._closure = null; + this._encoding = encoding; + + this._readEncapsStack = null; + this._writeEncapsStack = null; + this._readEncapsCache = null; + this._writeEncapsCache = null; + + this._sliceObjects = true; + + this._messageSizeMax = this._instance.messageSizeMax(); // Cached for efficiency. + this._unlimited = unlimited !== undefined ? unlimited : false; + + this._startSeq = -1; + this._sizePos = -1; + + if(data !== undefined) + { + this._buf = new Ice.Buffer(data); + } + else + { + this._buf = new Ice.Buffer(); + } + }, + // + // This function allows this object to be reused, rather than + // reallocated. + // + reset: function() + { + this._buf.reset(); + this.clear(); + }, + clear: function() + { + if(this._readEncapsStack !== null) + { + Debug.assert(this._readEncapsStack.next); + this._readEncapsStack.next = this._readEncapsCache; + this._readEncapsCache = this._readEncapsStack; + this._readEncapsCache.reset(); + this._readEncapsStack = null; + } + + if(this._writeEncapsStack !== null) + { + Debug.assert(this._writeEncapsStack.next); + this._writeEncapsStack.next = this._writeEncapsCache; + this._writeEncapsCache = this._writeEncapsStack; + this._writeEncapsCache.reset(); + this._writeEncapsStack = null; + } + this._startSeq = -1; + this._sliceObjects = true; + }, + swap: function(other) + { + Debug.assert(this._instance === other._instance); + + var tmpBuf, tmpClosure, tmpUnlimited, tmpStartSeq, tmpMinSeqSize, tmpSizePos; + + tmpBuf = other._buf; + other._buf = this._buf; + this._buf = tmpBuf; + + tmpClosure = other._closure; + other._closure = this._closure; + this._closure = tmpClosure; + + // + // Swap is never called for BasicStreams that have encapsulations being read/write. However, + // encapsulations might still be set in case marshalling or un-marshalling failed. We just + // reset the encapsulations if there are still some set. + // + this.resetEncaps(); + other.resetEncaps(); + + tmpUnlimited = other._unlimited; + other._unlimited = this._unlimited; + this._unlimited = tmpUnlimited; + + tmpStartSeq = other._startSeq; + other._startSeq = this._startSeq; + this._startSeq = tmpStartSeq; + + tmpMinSeqSize = other._minSeqSize; + other._minSeqSize = this._minSeqSize; + this._minSeqSize = tmpMinSeqSize; + + tmpSizePos = other._sizePos; + other._sizePos = this._sizePos; + this._sizePos = tmpSizePos; + }, + resetEncaps: function() + { + this._readEncapsStack = null; + this._writeEncapsStack = null; + }, + resize: function(sz) + { + // + // Check memory limit if stream is not unlimited. + // + if(!this._unlimited && sz > this._messageSizeMax) + { + ExUtil.throwMemoryLimitException(sz, this._messageSizeMax); + } + + this._buf.resize(sz); + this._buf.position = sz; + }, + prepareWrite: function() + { + this._buf.position = 0; + return this._buf; + }, + startWriteObject: function(data) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startInstance(SliceType.ObjectSlice, data); + }, + endWriteObject: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endInstance(); + }, + startReadObject: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.startInstance(SliceType.ObjectSlice); + }, + endReadObject: function(preserve) + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.endInstance(preserve); + }, + startWriteException: function(data) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startInstance(SliceType.ExceptionSlice, data); + }, + endWriteException: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endInstance(); + }, + startReadException: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.startInstance(SliceType.ExceptionSlice); + }, + endReadException: function(preserve) + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.endInstance(preserve); + }, + startWriteEncaps: function(encoding, format) + { + // + // If no encoding version is specified, use the current write + // encapsulation encoding version if there's a current write + // encapsulation, otherwise, use the stream encoding version. + // + + if(encoding === undefined) + { + if(this._writeEncapsStack !== null) + { + encoding = this._writeEncapsStack.encoding; + format = this._writeEncapsStack.format; + } + else + { + encoding = this._encoding; + format = FormatType.DefaultFormat; + } + } + + Protocol.checkSupportedEncoding(encoding); + + var curr = this._writeEncapsCache; + if(curr !== null) + { + curr.reset(); + this._writeEncapsCache = this._writeEncapsCache.next; + } + else + { + curr = new WriteEncaps(); + } + curr.next = this._writeEncapsStack; + this._writeEncapsStack = curr; + + this._writeEncapsStack.format = format; + this._writeEncapsStack.setEncoding(encoding); + this._writeEncapsStack.start = this._buf.limit; + + this.writeInt(0); // Placeholder for the encapsulation length. + this._writeEncapsStack.encoding.__write(this); + }, + endWriteEncaps: function() + { + Debug.assert(this._writeEncapsStack); + + // Size includes size and version. + var start = this._writeEncapsStack.start; + + var sz = this._buf.limit - start; + this._buf.putIntAt(start, sz); + + var curr = this._writeEncapsStack; + this._writeEncapsStack = curr.next; + curr.next = this._writeEncapsCache; + this._writeEncapsCache = curr; + this._writeEncapsCache.reset(); + }, + endWriteEncapsChecked: function() // Used by public stream API. + { + if(this._writeEncapsStack === null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + this.endWriteEncaps(); + }, + writeEmptyEncaps: function(encoding) + { + Protocol.checkSupportedEncoding(encoding); + this.writeInt(6); // Size + encoding.__write(this); + }, + writeEncaps: function(v) + { + if(v.length < 6) + { + throw new Ice.EncapsulationException(); + } + this.expand(v.length); + this._buf.putArray(v); + }, + getWriteEncoding: function() + { + return this._writeEncapsStack !== null ? this._writeEncapsStack.encoding : this._encoding; + }, + startReadEncaps: function() + { + var curr = this._readEncapsCache; + if(curr !== null) + { + curr.reset(); + this._readEncapsCache = this._readEncapsCache.next; + } + else + { + curr = new ReadEncaps(); + } + curr.next = this._readEncapsStack; + this._readEncapsStack = curr; + + this._readEncapsStack.start = this._buf.position; + + // + // I don't use readSize() and writeSize() for encapsulations, + // because when creating an encapsulation, I must know in advance + // how many bytes the size information will require in the data + // stream. If I use an Int, it is always 4 bytes. For + // readSize()/writeSize(), it could be 1 or 5 bytes. + // + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._readEncapsStack.sz = sz; + + var encoding = new Ice.EncodingVersion(); + encoding.__read(this); + Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported. + this._readEncapsStack.setEncoding(encoding); + + return encoding; + }, + endReadEncaps: function() + { + Debug.assert(this._readEncapsStack !== null); + + if(!this._readEncapsStack.encoding_1_0) + { + this.skipOpts(); + if(this._buf.position !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + } + else if(this._buf.position !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + if(this._buf.position + 1 !== this._readEncapsStack.start + this._readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + + // + // Ice version < 3.3 had a bug where user exceptions with + // class members could be encoded with a trailing byte + // when dispatched with AMD. So we tolerate an extra byte + // in the encapsulation. + // + + try + { + this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + var curr = this._readEncapsStack; + this._readEncapsStack = curr.next; + curr.next = this._readEncapsCache; + this._readEncapsCache = curr; + this._readEncapsCache.reset(); + }, + skipEmptyEncaps: function(encoding) + { + Debug.assert(encoding !== undefined); + var sz = this.readInt(); + if(sz !== 6) + { + throw new Ice.EncapsulationException(); + } + + var pos = this._buf.position; + if(pos + 2 > this._buf.limit) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(encoding !== null) + { + encoding.__read(this); + } + else + { + this._buf.position = pos + 2; + } + }, + endReadEncapsChecked: function() // Used by public stream API. + { + if(this._readEncapsStack === null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + this.endReadEncaps(); + }, + readEncaps: function(encoding) + { + Debug.assert(encoding !== undefined); + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(sz - 4 > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(encoding !== null) + { + encoding.__read(this); + this._buf.position = this._buf.position - 6; + } + else + { + this._buf.position = this._buf.position - 4; + } + + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + getReadEncoding: function() + { + return this._readEncapsStack !== null ? this._readEncapsStack.encoding : this._encoding; + }, + getReadEncapsSize: function() + { + Debug.assert(this._readEncapsStack !== null); + return this._readEncapsStack.sz - 6; + }, + skipEncaps: function() + { + var sz = this.readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + var encoding = new Ice.EncodingVersion(); + encoding.__read(this); + try + { + this._buf.position = this._buf.position + sz - 6; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return encoding; + }, + startWriteSlice: function(typeId, compactId, last) + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.startSlice(typeId, compactId, last); + }, + endWriteSlice: function() + { + Debug.assert(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null); + this._writeEncapsStack.encoder.endSlice(); + }, + startReadSlice: function() // Returns type ID of next slice + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + return this._readEncapsStack.decoder.startSlice(); + }, + endReadSlice: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.endSlice(); + }, + skipSlice: function() + { + Debug.assert(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null); + this._readEncapsStack.decoder.skipSlice(); + }, + readPendingObjects: function() + { + if(this._readEncapsStack !== null && this._readEncapsStack.decoder !== null) + { + this._readEncapsStack.decoder.readPendingObjects(); + } + else if((this._readEncapsStack !== null && this._readEncapsStack.encoding_1_0) || + (this._readEncapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no objects were read, we + // still read an empty sequence of pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.skipSize(); + } + }, + writePendingObjects: function() + { + if(this._writeEncapsStack !== null && this._writeEncapsStack.encoder !== null) + { + this._writeEncapsStack.encoder.writePendingObjects(); + } + else if((this._writeEncapsStack !== null && this._writeEncapsStack.encoding_1_0) || + (this._writeEncapsStack === null && this._encoding.equals(Ice.Encoding_1_0))) + { + // + // If using the 1.0 encoding and no objects were written, we + // still write an empty sequence for pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + this.writeSize(0); + } + }, + writeSize: function(v) + { + if(v > 254) + { + this.expand(5); + this._buf.put(255); + this._buf.putInt(v); + } + else + { + this.expand(1); + this._buf.put(v); + } + }, + readSize: function() + { + try + { + var b = this._buf.get(); + if(b === 255) + { + var v = this._buf.getInt(); + if(v < 0) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return v; + } + return b; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + readAndCheckSeqSize: function(minSize) + { + var sz = this.readSize(); + + if(sz === 0) + { + return sz; + } + + // + // The _startSeq variable points to the start of the sequence for which + // we expect to read at least _minSeqSize bytes from the stream. + // + // If not initialized or if we already read more data than _minSeqSize, + // we reset _startSeq and _minSeqSize for this sequence (possibly a + // top-level sequence or enclosed sequence it doesn't really matter). + // + // Otherwise, we are reading an enclosed sequence and we have to bump + // _minSeqSize by the minimum size that this sequence will require on + // the stream. + // + // The goal of this check is to ensure that when we start un-marshalling + // a new sequence, we check the minimal size of this new sequence against + // the estimated remaining buffer size. This estimatation is based on + // the minimum size of the enclosing sequences, it's _minSeqSize. + // + if(this._startSeq === -1 || this._buf.position > (this._startSeq + this._minSeqSize)) + { + this._startSeq = this._buf.position; + this._minSeqSize = sz * minSize; + } + else + { + this._minSeqSize += sz * minSize; + } + + // + // If there isn't enough data to read on the stream for the sequence (and + // possibly enclosed sequences), something is wrong with the marshalled + // data: it's claiming having more data that what is possible to read. + // + if(this._startSeq + this._minSeqSize > this._buf.limit) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return sz; + }, + startSize: function() + { + var pos = this._buf.position; + this.writeInt(0); // Placeholder for 32-bit size + return pos; + }, + endSize: function(pos) + { + Debug.assert(pos >= 0); + this.rewriteInt(this._buf.position - pos - 4, pos); + }, + writeBlob: function(v) + { + if(v === null) + { + return; + } + this.expand(v.length); + this._buf.putArray(v); + }, + readBlob: function(sz) + { + if(this._buf.remaining < sz) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + try + { + return this._buf.getArray(sz); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + // Read/write format and tag for optionals + writeOpt: function(tag, format) + { + Debug.assert(this._writeEncapsStack !== null); + if(this._writeEncapsStack.encoder !== null) + { + return this._writeEncapsStack.encoder.writeOpt(tag, format); + } + return this.writeOptImpl(tag, format); + }, + readOpt: function(tag, expectedFormat) + { + Debug.assert(this._readEncapsStack !== null); + if(this._readEncapsStack.decoder !== null) + { + return this._readEncapsStack.decoder.readOpt(tag, expectedFormat); + } + return this.readOptImpl(tag, expectedFormat); + }, + writeOptValue: function(tag, format, write, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, format)) + { + write.call(this, v); + } + } + }, + readOptValue: function(tag, format, read) + { + if(this.readOpt(tag, format)) + { + return read.call(this); + } + else + { + return undefined; + } + }, + writeByte: function(v) + { + this.expand(1); + this._buf.put(v); + }, + rewriteByte: function(v, dest) + { + this._buf.putAt(dest, v); + }, + readByte: function() + { + try + { + return this._buf.get(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeByteSeq: function(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this.writeSize(v.length); + this.expand(v.length); + this._buf.putArray(v); + } + }, + readByteSeq: function() + { + return this._buf.getArray(this.readAndCheckSeqSize(1)); + }, + writeBool: function(v) + { + this.expand(1); + this._buf.put(v ? 1 : 0); + }, + rewriteBool: function(v, dest) + { + this._buf.putAt(dest, v ? 1 : 0); + }, + readBool: function() + { + try + { + return this._buf.get() === 1; + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeShort: function(v) + { + this.expand(2); + this._buf.putShort(v); + }, + readShort: function() + { + try + { + return this._buf.getShort(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeInt: function(v) + { + this.expand(4); + this._buf.putInt(v); + }, + rewriteInt: function(v, dest) + { + this._buf.putIntAt(dest, v); + }, + readInt: function() + { + try + { + return this._buf.getInt(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeLong: function(v) + { + this.expand(8); + this._buf.putLong(v); + }, + readLong: function() + { + try + { + return this._buf.getLong(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeFloat: function(v) + { + this.expand(4); + this._buf.putFloat(v); + }, + readFloat: function() + { + try + { + return this._buf.getFloat(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeDouble: function(v) + { + this.expand(8); + this._buf.putDouble(v); + }, + readDouble: function() + { + try + { + return this._buf.getDouble(); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeString: function(v) + { + if(v === null || v.length === 0) + { + this.writeSize(0); + } + else + { + this._buf.writeString(this, v); + } + }, + readString: function() + { + var len = this.readSize(); + if(len === 0) + { + return ""; + } + // + // Check the buffer has enough bytes to read. + // + if(this._buf.remaining < len) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + try + { + return this._buf.getString(len); + } + catch(ex) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + }, + writeProxy: function(v) + { + this._instance.proxyFactory().proxyToStream(v, this); + }, + writeOptProxy: function(tag, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, OptionalFormat.FSize)) + { + var pos = this.startSize(); + this.writeProxy(v); + this.endSize(pos); + } + } + }, + readProxy: function(type) + { + return this._instance.proxyFactory().streamToProxy(this, type); + }, + readOptProxy: function(tag, type) + { + if(this.readOpt(tag, OptionalFormat.FSize)) + { + this.skip(4); + return this.readProxy(type); + } + else + { + return undefined; + } + }, + writeEnum: function(v) + { + if(this.isWriteEncoding_1_0()) + { + if(v.maxValue < 127) + { + this.writeByte(v.value); + } + else if(v.maxValue < 32767) + { + this.writeShort(v.value); + } + else + { + this.writeInt(v.value); + } + } + else + { + this.writeSize(v.value); + } + }, + readEnum: function(T) + { + var v; + if(this.getReadEncoding().equals(Ice.Encoding_1_0)) + { + if(T.maxValue < 127) + { + v = this.readByte(); + } + else if(T.maxValue < 32767) + { + v = this.readShort(); + } + else + { + v = this.readInt(); + } + } + else + { + v = this.readSize(); + } + + var e = T.valueOf(v); + if(e === undefined) + { + throw new Ice.MarshalException("enumerator value " + v + " is out of range"); + } + return e; + }, + readOptEnum: function(tag, T) + { + if(this.readOpt(tag, OptionalFormat.Size)) + { + return this.readEnum(T); + } + else + { + return undefined; + } + }, + writeObject: function(v) + { + this.initWriteEncaps(); + this._writeEncapsStack.encoder.writeObject(v); + }, + writeOptObject: function(tag, v) + { + if(v !== undefined) + { + if(this.writeOpt(tag, OptionalFormat.Class)) + { + this.writeObject(v); + } + } + }, + readObject: function(patcher, T) + { + this.initReadEncaps(); + // + // BUGFIX: + // With Chrome linux the invokation of readObject on the decoder some times + // calls BasicStream.readObject with the decoder object as this param. + // Use call instead of directly invoke the method to workaround this bug. + // + this._readEncapsStack.decoder.readObject.call( + this._readEncapsStack.decoder, + function(obj){ + if(obj !== null && !(obj.ice_instanceof(T))) + { + ExUtil.throwUOE(T.ice_staticId(), obj); + } + patcher(obj); + }); + }, + readOptObject: function(tag, patcher, T) + { + if(this.readOpt(tag, OptionalFormat.Class)) + { + this.readObject(patcher, T); + } + else + { + patcher(undefined); + } + }, + writeUserException: function(e) + { + this.initWriteEncaps(); + this._writeEncapsStack.encoder.writeUserException(e); + }, + throwException: function() + { + this.initReadEncaps(); + this._readEncapsStack.decoder.throwException(); + }, + sliceObjects: function(b) + { + this._sliceObjects = b; + }, + readOptImpl: function(readTag, expectedFormat) + { + var b, v, format, tag, offset; + + if(this.isReadEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + while(true) + { + if(this._buf.position >= this._readEncapsStack.start + this._readEncapsStack.sz) + { + return false; // End of encapsulation also indicates end of optionals. + } + + v = this.readByte(); + + if(v === OPTIONAL_END_MARKER) + { + this._buf.position -= 1; // Rewind. + return false; + } + + format = OptionalFormat.valueOf(v & 0x07); // First 3 bits. + tag = v >> 3; + if(tag === 30) + { + tag = this.readSize(); + } + + if(tag > readTag) + { + offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind + this._buf.position -= offset; + return false; // No optional data members with the requested tag. + } + + if(tag < readTag) + { + this.skipOpt(format); // Skip optional data members + } + else + { + if(format !== expectedFormat) + { + throw new Ice.MarshalException("invalid optional data member `" + tag + "': unexpected format"); + } + return true; + } + } + }, + writeOptImpl: function(tag, format) + { + if(this.isWriteEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + var v = format.value; + if(tag < 30) + { + v |= tag << 3; + this.writeByte(v); + } + else + { + v |= 0x0F0; // tag = 30 + this.writeByte(v); + this.writeSize(tag); + } + return true; + }, + skipOpt: function(format) + { + switch(format) + { + case OptionalFormat.F1: + this.skip(1); + break; + case OptionalFormat.F2: + this.skip(2); + break; + case OptionalFormat.F4: + this.skip(4); + break; + case OptionalFormat.F8: + this.skip(8); + break; + case OptionalFormat.Size: + this.skipSize(); + break; + case OptionalFormat.VSize: + this.skip(this.readSize()); + break; + case OptionalFormat.FSize: + this.skip(this.readInt()); + break; + case OptionalFormat.Class: + this.readObject(null, Ice.Object); + break; + } + }, + skipOpts: function() + { + var b, v, format; + // + // Skip remaining un-read optional members. + // + while(true) + { + if(this._buf.position >= this._readEncapsStack.start + this._readEncapsStack.sz) + { + return; // End of encapsulation also indicates end of optionals. + } + + b = this.readByte(); + v = b < 0 ? b + 256 : b; + if(v === OPTIONAL_END_MARKER) + { + return; + } + + format = OptionalFormat.valueOf(v & 0x07); // Read first 3 bits. + if((v >> 3) === 30) + { + this.skipSize(); + } + this.skipOpt(format); + } + }, + skip: function(size) + { + if(size > this._buf.remaining) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + this._buf.position += size; + }, + skipSize: function() + { + var b = this.readByte(); + if(b === 255) + { + this.skip(4); + } + }, + isEmpty: function() + { + return this._buf.empty(); + }, + expand: function(n) + { + if(!this._unlimited && this._buf && this._buf.position + n > this._messageSizeMax) + { + ExUtil.throwMemoryLimitException(this._buf.position + n, this._messageSizeMax); + } + this._buf.expand(n); + }, + createObject: function(id) + { + var obj = null, Class; + try + { + var typeId = id.length > 2 ? id.substr(2).replace("::", ".") : ""; + /*jshint -W061 */ + Class = eval(typeId); + /*jshint +W061 */ + if(Class !== undefined) + { + obj = new Class(); + } + } + catch(ex) + { + throw new Ice.NoObjectFactoryException("no object factory", id, ex); + } + + return obj; + }, + getTypeId: function(compactId) + { + var typeId = Ice.CompactIdRegistry.get(compactId); + return typeId === undefined ? "" : typeId; + }, + isReadEncoding_1_0: function() + { + return this._readEncapsStack !== null ? this._readEncapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + }, + isWriteEncoding_1_0: function() + { + return this._writeEncapsStack ? this._writeEncapsStack.encoding_1_0 : this._encoding.equals(Ice.Encoding_1_0); + }, + initReadEncaps: function() + { + if(this._readEncapsStack === null) // Lazy initialization + { + this._readEncapsStack = this._readEncapsCache; + if(this._readEncapsStack !== null) + { + this._readEncapsCache = this._readEncapsCache.next; + } + else + { + this._readEncapsStack = new ReadEncaps(); + } + this._readEncapsStack.setEncoding(this._encoding); + this._readEncapsStack.sz = this._buf.limit; + } + + if(this._readEncapsStack.decoder === null) // Lazy initialization. + { + var factoryManager = this._instance.servantFactoryManager(); + if(this._readEncapsStack.encoding_1_0) + { + this._readEncapsStack.decoder = new EncapsDecoder10(this, this._readEncapsStack, this._sliceObjects, factoryManager); + } + else + { + this._readEncapsStack.decoder = new EncapsDecoder11(this, this._readEncapsStack, this._sliceObjects, factoryManager); + } + } + }, + initWriteEncaps: function() + { + if(!this._writeEncapsStack) // Lazy initialization + { + this._writeEncapsStack = this._writeEncapsCache; + if(this._writeEncapsStack) + { + this._writeEncapsCache = this._writeEncapsCache.next; + } + else + { + this._writeEncapsStack = new WriteEncaps(); + } + this._writeEncapsStack.setEncoding(this._encoding); + } + + if(this._writeEncapsStack.format === FormatType.DefaultFormat) + { + this._writeEncapsStack.format = this._instance.defaultsAndOverrides().defaultFormat; + } + + if(!this._writeEncapsStack.encoder) // Lazy initialization. + { + if(this._writeEncapsStack.encoding_1_0) + { + this._writeEncapsStack.encoder = new EncapsEncoder10(this, this._writeEncapsStack); + } + else + { + this._writeEncapsStack.encoder = new EncapsEncoder11(this, this._writeEncapsStack); + } + } + }, + createUserException: function(id) + { + var userEx = null, Class; + + try + { + var typeId = id.length > 2 ? id.substr(2).replace("::", ".") : ""; + /*jshint -W061 */ + Class = eval(typeId); + /*jshint +W061 */ + if(Class !== undefined) + { + userEx = new Class(); + } + } + catch(ex) + { + throw new Ice.MarshalException(ex); + } + + return userEx; + } + }); + + var defineProperty = Object.defineProperty; + + defineProperty(BasicStream.prototype, "pos", { + get: function() { return this._buf.position; }, + set: function(n) { this._buf.position = n; } + }); + + defineProperty(BasicStream.prototype, "size", { + get: function() { return this._buf.limit; } + }); + + defineProperty(BasicStream.prototype, "instance", { + get: function() { return this._instance; } + }); + + defineProperty(BasicStream.prototype, "closure", { + get: function() { return this._type; }, + set: function(type) { this._type = type; } + }); + + defineProperty(BasicStream.prototype, "buffer", { + get: function() { return this._buf; } + }); + + var defineBuiltinHelper = function(write, read, sz, format) + { + var helper = { + write: function(os, v) { return write.call(os, v); }, + read: function(is) { return read.call(is); }, + writeOpt: function(os, tag, v) { os.writeOptValue(tag, format, write, v); }, + readOpt: function(is, tag) { return is.readOptValue(tag, format, read); }, + }; + defineProperty(helper, "minWireSize", { + get: function() { return sz; } + }); + return helper; + }; + + var stream = BasicStream.prototype; + Ice.ByteHelper = defineBuiltinHelper(stream.writeByte, stream.readByte, 1, Ice.OptionalFormat.F1); + Ice.BoolHelper = defineBuiltinHelper(stream.writeBool, stream.readBool, 1, Ice.OptionalFormat.F1); + Ice.ShortHelper = defineBuiltinHelper(stream.writeShort, stream.readShort, 2, Ice.OptionalFormat.F2); + Ice.IntHelper = defineBuiltinHelper(stream.writeInt, stream.readInt, 4, Ice.OptionalFormat.F4); + Ice.LongHelper = defineBuiltinHelper(stream.writeLong, stream.readLong, 8, Ice.OptionalFormat.F8); + Ice.FloatHelper = defineBuiltinHelper(stream.writeFloat, stream.readFloat, 4, Ice.OptionalFormat.F4); + Ice.DoubleHelper = defineBuiltinHelper(stream.writeDouble, stream.readDouble, 8, Ice.OptionalFormat.F8); + Ice.StringHelper = defineBuiltinHelper(stream.writeString, stream.readString, 1, Ice.OptionalFormat.VSize); + + Ice.ObjectHelper = { + write: function(os, v) + { + os.writeObject(v); + }, + read: function(is) + { + var o; + is.readObject(function(v) { o = v; }, Ice.Object); + return o; + }, + writeOpt: function(os, tag, v) + { + os.writeOptValue(tag, Ice.OptionalFormat.Class, stream.writeObject, v); + }, + readOpt: function(is, tag) + { + var o; + is.readOptObject(tag, function(v) { o = v; }, Ice.Object); + return o; + }, + }; + + defineProperty(Ice.ObjectHelper, "minWireSize", { + get: function() { return 1; } + }); + + Ice.BasicStream = BasicStream; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/BatchOutgoingAsync.js b/js/src/Ice/BatchOutgoingAsync.js new file mode 100644 index 00000000000..ccebcad9ba5 --- /dev/null +++ b/js/src/Ice/BatchOutgoingAsync.js @@ -0,0 +1,37 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_FOR_ACTIONSCRIPT_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResult"); + + var Ice = global.Ice || {}; + + var AsyncResult = Ice.AsyncResult; + + var BatchOutgoingAsync = Ice.Class(AsyncResult, { + __init__: function(communicator, operation) + { + AsyncResult.call(this, communicator, operation, null, null, null, null); + }, + __sent: function(connection) + { + this._state |= AsyncResult.Done | AsyncResult.OK | AsyncResult.Sent; + this._os.resize(0); + this.succeed(this); + }, + __finishedEx: function(exc, sent) + { + this.__exception(exc); + } + }); + + Ice.BatchOutgoingAsync = BatchOutgoingAsync; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Buffer.js b/js/src/Ice/Buffer.js new file mode 100644 index 00000000000..71c4f614bc6 --- /dev/null +++ b/js/src/Ice/Buffer.js @@ -0,0 +1,429 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// Ice.Buffer implementation to be used by Node.js, it uses node Buffer +// as the store. +// + +// +// Define Node.Buffer as an alias to NodeJS global Buffer type, +// that allow us to refer to Ice.Buffer as Buffer in this file. +// +(function(global){ + var Node = { Buffer: global.Buffer }; + + require("Ice/Long"); + require("Ice/Class"); + + var Ice = global.Ice || {}; + var Long = Ice.Long; + + var __BufferOverflowException__ = "BufferOverflowException"; + var __BufferUnderflowException__ = "BufferUnderflowException"; + var __IndexOutOfBoundsException__ = "IndexOutOfBoundsException"; + + var Buffer = Ice.Class({ + __init__: function(buffer) + { + if(buffer !== undefined) + { + this.b = buffer; + } + else + { + this.b = null; + } + this._position = 0; + this._limit = 0; + this._shrinkCounter = 0; + }, + empty: function() + { + return this._limit === 0; + }, + resize: function(n) + { + if(n === 0) + { + this.clear(); + } + else if(n > this.capacity) + { + this.reserve(n); + } + this._limit = n; + }, + clear: function() + { + this.b = null; + this._position = 0; + this._limit = 0; + }, + // + // Call expand(n) to add room for n additional bytes. Note that expand() + // examines the current position of the buffer first; we don't want to + // expand the buffer if the caller is writing to a location that is + // already in the buffer. + // + expand: function(n) + { + var sz = this.capacity === 0 ? n : this._position + n; + if(sz > this._limit) + { + this.resize(sz); + } + }, + reset: function() + { + if(this._limit > 0 && this._limit * 2 < this.capacity) + { + // + // If the current buffer size is smaller than the + // buffer capacity, we shrink the buffer memory to the + // current size. This is to avoid holding on to too much + // memory if it's not needed anymore. + // + if(++this._shrinkCounter > 2) + { + this.reserve(this._limit); + this._shrinkCounter = 0; + } + } + else + { + this._shrinkCounter = 0; + } + this._limit = 0; + this._position = 0; + }, + reserve: function(n) + { + var b, capacity; + if(n > this.capacity) + { + capacity = Math.max(n, 2 * this.capacity); + capacity = Math.max(1024, capacity); + if(this.b === null) + { + this.b = new Node.Buffer(capacity); + } + else + { + b = new Node.Buffer(capacity); + this.b.copy(b); + this.b = b; + } + } + else if(n < this.capacity) + { + this.b = this.b.slice(0, this.capacity); + } + else + { + return; + } + }, + put: function(v) + { + if(this._position === this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeUInt8(v, this._position, true); + this._position++; + }, + putAt: function(i, v) + { + if(i >= this._limit) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeUInt8(v, i, true); + }, + putArray: function(v) + { + //Expects a Nodejs Buffer + if(this._position + v.length > this._limit) + { + throw new Error(__BufferOverflowException__); + } + v.copy(this.b, this._position); + this._position += v.length; + }, + putShort: function(v) + { + if(this._position + 2 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeInt16LE(v, this._position, true); + this._position += 2; + }, + putShortAt: function(i, v) + { + if(i + 2 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeInt16LE(v, i, true); + }, + putInt: function(v) + { + if(this._position + 4 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeInt32LE(v, this._position, true); + this._position += 4; + }, + putIntAt: function(i, v) + { + if(i + 4 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeInt32LE(v, i, true); + }, + putFloat: function(v) + { + if(this._position + 4 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeFloatLE(v, this._position, true); + this._position += 4; + }, + putFloatAt: function(i, v) + { + if(i + 4 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeFloatLE(v, i, true); + }, + putDouble: function(v) + { + if(this._position + 8 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeDoubleLE(v, this._position, true); + this._position += 8; + }, + putDoubleAt: function(i, v) + { + if(i + 8 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeDoubleLE(v, i, true); + }, + putLong: function(v) + { + if(this._position + 8 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.b.writeUInt32LE(v.low, this._position, true); + this._position += 4; + this.b.writeUInt32LE(v.high, this._position, true); + this._position += 4; + }, + putLongAt: function(i, v) + { + if(i + 8 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.b.writeUInt32LE(v.low, i, true); + this.b.writeUInt32LE(v.high, i + 4, true); + }, + writeString: function(stream, v) + { + var sz = Node.Buffer.byteLength(v); + stream.writeSize(sz); + stream.expand(sz); + this.putString(v, sz); + }, + putString: function(v, sz) + { + if(this._position + sz > this._limit) + { + throw new Error(__BufferOverflowException__); + } + var bytes = this.b.write(v, this._position); + // + // Check all bytes were written + // + if(bytes < sz) + { + throw new Error(__IndexOutOfBoundsException__); + } + this._position += sz; + }, + get: function() + { + if(this._position >= this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.b.readUInt8(this._position, true); + this._position++; + return v; + }, + getAt: function(i) + { + if(i < 0 || i >= this._limit) + { + throw new Error(__IndexOutOfBoundsException__); + } + return this.b.readUInt8(i, true); + }, + getArray: function(length) + { + if(this._position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var buffer = new Node.Buffer(length); + this.b.slice(this._position, this._position + length).copy(buffer); + this._position += length; + return buffer; + }, + getArrayAt: function(position, length) + { + if(position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var buffer = new Node.Buffer(length); + length = length === undefined ? (this.b.length - position) : length; + this.b.slice(position, position + length).copy(buffer); + return buffer; + }, + getShort: function() + { + var v; + if(this._limit - this._position < 2) + { + throw new Error(__BufferUnderflowException__); + } + v = this.b.readInt16LE(this._position, true); + this._position += 2; + return v; + }, + getInt: function() + { + var v; + if(this._limit - this._position < 4) + { + throw new Error(__BufferUnderflowException__); + } + v = this.b.readInt32LE(this._position, true); + this._position += 4; + return v; + }, + getFloat: function() + { + if(this._limit - this._position < 4) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.b.readFloatLE(this._position, true); + this._position += 4; + return v; + }, + getDouble: function() + { + if(this._limit - this._position < 8) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.b.readDoubleLE(this._position, true); + this._position += 8; + return v; + }, + getLong: function() + { + if(this._limit - this._position < 8) + { + throw new Error(__BufferUnderflowException__); + } + var v = new Long(); + v.low = this.b.readUInt32LE(this._position, true); + this._position += 4; + v.high = this.b.readUInt32LE(this._position, true); + this._position += 4; + return v; + }, + getString: function(length) + { + if(this._position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var s =this.b.toString("utf8", this._position, this._position + length); + this._position += length; + return s; + } + }); + + var prototype = Buffer.prototype; + + Object.defineProperty(prototype, "position", { + get: function() { return this._position; }, + set: function(position){ + if(position >= 0 && position <= this._limit) + { + this._position = position; + } + } + }); + + Object.defineProperty(prototype, "limit", { + get: function() { return this._limit; }, + set: function(limit){ + if(limit <= this.capacity) + { + this._limit = limit; + if(this._position > limit) + { + this._position = limit; + } + } + } + }); + + Object.defineProperty(prototype, "capacity", { + get: function() { return this.b === null ? 0 : this.b.length; } + }); + + Object.defineProperty(prototype, "remaining", { + get: function() { return this._limit - this._position; } + }); + + // + // Create a native buffer from an array of bytes. + // + Buffer.createNative = function(data) + { + if(data === undefined) + { + return new Node.Buffer(0); + } + else + { + return new Node.Buffer(data); + } + }; + + Ice.Buffer = Buffer; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Class.js b/js/src/Ice/Class.js new file mode 100644 index 00000000000..1e1778fb71f --- /dev/null +++ b/js/src/Ice/Class.js @@ -0,0 +1,58 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var Class = function() + { + var base; + var desc; + var constructor; + + if(arguments.length == 1) + { + desc = arguments[0]; + } + else if(arguments.length == 2) + { + base = arguments[0]; + desc = arguments[1]; + } + + if(desc !== undefined) + { + constructor = desc.__init__; + if(constructor) + { + delete desc.__init__; + } + } + + var o = constructor || function(){}; + + if(base !== undefined) + { + o.prototype = new base(); + o.prototype.constructor = o; + } + + if(desc !== undefined) + { + for(var key in desc) + { + o.prototype[key] = desc[key]; + } + } + return o; + }; + + Ice.Class = Class; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Communicator.js b/js/src/Ice/Communicator.js new file mode 100644 index 00000000000..b2427221eb1 --- /dev/null +++ b/js/src/Ice/Communicator.js @@ -0,0 +1,166 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Instance"); + require("Ice/Promise"); + require("Ice/UUID"); + require("Ice/AsyncResultBase"); + + var Ice = global.Ice || {}; + + var Instance = Ice.Instance; + var Promise = Ice.Promise; + var UUID = Ice.UUID; + + // + // Ice.Communicator + // + var Communicator = Ice.Class({ + __init__: function(initData) + { + this._instance = new Instance(initData); + }, + // + // Certain initialization tasks need to be completed after the + // constructor. + // + finishSetup: function(promise) + { + this._instance.finishSetup(this, promise); + }, + destroy: function() + { + return this._instance.destroy(); + }, + shutdown: function() + { + this._instance.objectAdapterFactory().shutdown(); + }, + waitForShutdown: function() + { + return this._instance.objectAdapterFactory().waitForShutdown(); + }, + isShutdown: function() + { + return this._instance.objectAdapterFactory().isShutdown(); + }, + stringToProxy: function(s) + { + return this._instance.proxyFactory().stringToProxy(s); + }, + proxyToString: function(proxy) + { + return this._instance.proxyFactory().proxyToString(proxy); + }, + propertyToProxy: function(s) + { + return this._instance.proxyFactory().propertyToProxy(s); + }, + proxyToProperty: function(proxy, prefix) + { + return this._instance.proxyFactory().proxyToProperty(proxy, prefix); + }, + stringToIdentity: function(s) + { + return this._instance.stringToIdentity(s); + }, + identityToString: function(ident) + { + return this._instance.identityToString(ident); + }, + createObjectAdapter: function(name) + { + var promise = new Ice.AsyncResultBase(this, "createObjectAdapter", this, null, null); + this._instance.objectAdapterFactory().createObjectAdapter(name, null, promise); + return promise; + }, + createObjectAdapterWithEndpoints: function(name, endpoints) + { + if(name.length === 0) + { + name = UUID.generateUUID(); + } + + this.getProperties().setProperty(name + ".Endpoints", endpoints); + var promise = new Ice.AsyncResultBase(this, "createObjectAdapterWithEndpoints", this, null, null); + this._instance.objectAdapterFactory().createObjectAdapter(name, null, promise); + return promise; + }, + createObjectAdapterWithRouter: function(name, router) + { + if(name.length === 0) + { + name = UUID.generateUUID(); + } + + var promise = new Ice.AsyncResultBase(this, "createObjectAdapterWithRouter", this, null, null); + + // + // We set the proxy properties here, although we still use the proxy supplied. + // + var properties = this.proxyToProperty(router, name + ".Router"); + for(var e = properties.entries; e !== null; e = e.next) + { + this.getProperties().setProperty(e.key, e.value); + } + + this._instance.objectAdapterFactory().createObjectAdapter(name, router, promise); + return promise; + }, + addObjectFactory: function(factory, id) + { + this._instance.servantFactoryManager().add(factory, id); + }, + findObjectFactory: function(id) + { + return this._instance.servantFactoryManager().find(id); + }, + getImplicitContext: function() + { + return this._instance.getImplicitContext(); + }, + getProperties: function() + { + return this._instance.initializationData().properties; + }, + getLogger: function() + { + return this._instance.initializationData().logger; + }, + getDefaultRouter: function() + { + return this._instance.referenceFactory().getDefaultRouter(); + }, + setDefaultRouter: function(router) + { + this._instance.setDefaultRouter(router); + }, + getDefaultLocator: function() + { + return this._instance.referenceFactory().getDefaultLocator(); + }, + setDefaultLocator: function(locator) + { + this._instance.setDefaultLocator(locator); + }, + flushBatchRequests: function() + { + return this._instance.outgoingConnectionFactory().flushAsyncBatchRequests(); + } + }); + + Object.defineProperty(Communicator.prototype, "instance", { + get: function() { return this._instance; } + }); + + Ice.Communicator = Communicator; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/CompactIdRegistry.js b/js/src/Ice/CompactIdRegistry.js new file mode 100644 index 00000000000..b3f46caadf3 --- /dev/null +++ b/js/src/Ice/CompactIdRegistry.js @@ -0,0 +1,15 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/HashMap"); + var Ice = global.Ice || {}; + Ice.CompactIdRegistry = new Ice.HashMap(); + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectRequestHandler.js b/js/src/Ice/ConnectRequestHandler.js new file mode 100644 index 00000000000..c6053f3945a --- /dev/null +++ b/js/src/Ice/ConnectRequestHandler.js @@ -0,0 +1,429 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResult"); + require("Ice/AsyncStatus"); + require("Ice/BasicStream"); + require("Ice/BatchOutgoingAsync"); + require("Ice/ConnectionRequestHandler"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/LocalExceptionWrapper"); + require("Ice/OutgoingAsync"); + require("Ice/Protocol"); + require("Ice/ReferenceMode"); + require("Ice/Exception"); + require("Ice/Promise"); + + var Ice = global.Ice || {}; + + var AsyncResult = Ice.AsyncResult; + var AsyncStatus = Ice.AsyncStatus; + var BasicStream = Ice.BasicStream; + var BatchOutgoingAsync = Ice.BatchOutgoingAsync; + var ConnectionRequestHandler = Ice.ConnectionRequestHandler; + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var LocalExceptionWrapper = Ice.LocalExceptionWrapper; + var OutgoingAsync = Ice.OutgoingAsync; + var Protocol = Ice.Protocol; + var ReferenceMode = Ice.ReferenceMode; + var LocalException = Ice.LocalException; + var Promise = Ice.Promise; + + var ConnectRequestHandler = Ice.Class({ + __init__: function(ref, proxy) + { + this._reference = ref; + this._response = ref.getMode() === ReferenceMode.ModeTwoway; + this._proxy = proxy; + this._batchAutoFlush = ref.getInstance().initializationData().properties.getPropertyAsIntWithDefault( + "Ice.BatchAutoFlush", 1) > 0 ? true : false; + this._initialized = false; + this._flushing = false; + this._batchRequestInProgress = false; + this._batchRequestsSize = Protocol.requestBatchHdr.length; + this._batchStream = + new BasicStream(ref.getInstance(), Protocol.currentProtocolEncoding, this._batchAutoFlush); + this._updateRequestHandler = false; + + this._connection = null; + this._compress = false; + this._exception = null; + this._requests = []; + this._updateRequestHandler = false; + this._pendingPromises = []; + }, + connect: function() + { + var self = this; + this._reference.getConnection().then( + function(connection, compress) + { + self.setConnection(connection, compress); + }).exception( + function(ex) + { + self.setException(ex); + }); + + if(this.initialized()) + { + Debug.assert(this._connection !== null); + return new ConnectionRequestHandler(this._reference, this._connection, this._compress); + } + else + { + // The proxy request handler will be updated when the connection is set. + this._updateRequestHandler = true; + return this; + } + }, + prepareBatchRequest: function(os) + { + if(!this.initialized()) + { + this._batchRequestInProgress = true; + this._batchStream.swap(os); + return; + } + + this._connection.prepareBatchRequest(os); + }, + finishBatchRequest: function(os) + { + if(!this.initialized()) + { + Debug.assert(this._batchRequestInProgress); + this._batchRequestInProgress = false; + + this._batchStream.swap(os); + + if(!this._batchAutoFlush && + this._batchStream.size + this._batchRequestsSize > this._reference.getInstance().messageSizeMax()) + { + ExUtil.throwMemoryLimitException(this._batchStream.size + this._batchRequestsSize, + this._reference.getInstance().messageSizeMax()); + } + + this._requests.push(new Request(this._batchStream)); + return; + } + this._connection.finishBatchRequest(os, this._compress); + }, + abortBatchRequest: function() + { + if(!this.initialized()) + { + Debug.assert(this._batchRequestInProgress); + this._batchRequestInProgress = false; + + var dummy = new BasicStream(this._reference.getInstance(), Protocol.currentProtocolEncoding, + this._batchAutoFlush); + this._batchStream.swap(dummy); + this._batchRequestsSize = Protocol.requestBatchHdr.length; + + return; + } + this._connection.abortBatchRequest(); + }, + sendAsyncRequest: function(out) + { + if(!this.initialized()) + { + this._requests.push(new Request(out)); + return AsyncStatus.Queued; + } + return this._connection.sendAsyncRequest(out, this._compress, this._response); + }, + flushAsyncBatchRequests: function(out) + { + if(!this.initialized()) + { + this._requests.push(new Request(out)); + return AsyncStatus.Queued; + } + return this._connection.flushAsyncBatchRequests(out); + }, + getReference: function() + { + return this._reference; + }, + getConnection: function() + { + if(this._exception !== null) + { + throw this._exception; + } + else + { + return this._connection; + } + }, + onConnection: function(r) + { + // + // Called by ObjectPrx.ice_getConnection + // + + if(this._exception !== null) + { + r.__exception(this._exception); + } + else if(this._connection !== null) + { + Debug.assert(this._initialized); + r.succeed(this._connection, r); + } + else + { + this._pendingPromises.push(r); + } + }, + // + // Implementation of Reference_GetConnectionCallback + // + + setConnection: function(connection, compress) + { + Debug.assert(this._exception === null && this._connection === null); + Debug.assert(this._updateRequestHandler || this._requests.length === 0); + + this._connection = connection; + this._compress = compress; + + // + // If this proxy is for a non-local object, and we are using a router, then + // add this proxy to the router info object. + // + var ri = this._reference.getRouterInfo(); + if(ri !== null) + { + var self = this; + var promise = ri.addProxy(this._proxy).then( + function() + { + // + // The proxy was added to the router info, we're now ready to send the + // queued requests. + // + self.flushRequests(); + }).exception( + function(ex) + { + self.setException(ex); + }); + + if(!promise.completed()) + { + return; // The request handler will be initialized once addProxy completes. + } + } + + // + // We can now send the queued requests. + // + this.flushRequests(); + }, + setException: function(ex) + { + Debug.assert(!this._initialized && this._exception === null); + Debug.assert(this._updateRequestHandler || this._requests.length === 0); + + this._exception = ex; + this._proxy = null; // Break cyclic reference count. + + // + // If some requests were queued, we notify them of the failure. + // + if(this._requests.length > 0) + { + this.flushRequestsWithException(ex); + } + + for(var i = 0; i < this._pendingPromises.length; ++i) + { + this._pendingPromises[i].fail(ex); + } + this._pendingPromises = []; + }, + initialized: function() + { + if(this._initialized) + { + Debug.assert(this._connection !== null); + return true; + } + else + { + if(this._exception !== null) + { + throw this._exception; + } + else + { + return this._initialized; + } + } + }, + flushRequests: function() + { + Debug.assert(this._connection !== null && !this._initialized); + + // + // We set the _flushing flag to true to prevent any additional queuing. Callers + // might block for a little while as the queued requests are being sent but this + // shouldn't be an issue as the request sends are non-blocking. + // + this._flushing = true; + + try + { + while(this._requests.length > 0) + { + var request = this._requests[0]; + if(request.out !== null) + { + this._connection.sendAsyncRequest(request.out, this._compress, this._response); + } + else if(request.batchOut !== null) + { + this._connection.flushAsyncBatchRequests(request.batchOut); + } + else + { + var os = new BasicStream(request.os.instance, Protocol.currentProtocolEncoding); + this._connection.prepareBatchRequest(os); + try + { + request.os.pos = 0; + os.writeBlob(request.os.readBlob(request.os.size)); + } + catch(ex) + { + this._connection.abortBatchRequest(); + throw ex; + } + this._connection.finishBatchRequest(os, this._compress); + } + this._requests.shift(); + } + } + catch(ex) + { + if(ex instanceof LocalExceptionWrapper) + { + Debug.assert(this._exception === null && this._requests.length > 0); + this._exception = ex.inner; + this.flushRequestsWithExceptionWrapper(ex); + } + else if(ex instanceof LocalException) + { + Debug.assert(this._exception === null && this._requests.length > 0); + this._exception = ex; + this.flushRequestsWithException(ex); + } + else + { + throw ex; + } + } + + // + // We've finished sending the queued requests and the request handler now send + // the requests over the connection directly. It's time to substitute the + // request handler of the proxy with the more efficient connection request + // handler which does not have any synchronization. This also breaks the cyclic + // reference count with the proxy. + // + // NOTE: _updateRequestHandler is immutable once _flushing = true + // + if(this._updateRequestHandler && this._exception === null) + { + this._proxy.__setRequestHandler( + new ConnectionRequestHandler(this._reference, this._connection, this._compress)); + } + + Debug.assert(!this._initialized); + if(this._exception === null) + { + this._initialized = true; + this._flushing = false; + } + this._proxy = null; // Break cyclic reference count. + + var p; + for(var i = 0; i < this._pendingPromises.length; ++i) + { + p = this._pendingPromises[i]; + p.succeed(this._connection, p); + } + this._pendingPromises = []; + }, + flushRequestsWithException: function(ex) + { + for(var i = 0; i < this._requests.length; ++i) + { + var request = this._requests[i]; + if(request.out !== null) + { + request.out.__finishedEx(ex, false); + } + else if(request.batchOut !== null) + { + request.batchOut.__finishedEx(ex, false); + } + } + this._requests = []; + }, + flushRequestsWithExceptionWrapper: function(ex) + { + for(var i = 0; i < this._requests.length; ++i) + { + var request = this._requests[i]; + if(request.out !== null) + { + request.out.__finishedWrapper(ex); + } + else if(request.batchOut !== null) + { + request.batchOut.__finishedEx(ex.inner, false); + } + } + this._requests = []; + } + }); + + Ice.ConnectRequestHandler = ConnectRequestHandler; + global.Ice = Ice; + + var Request = function(arg) + { + this.os = null; + this.out = null; + this.batchOut = null; + + if(arg instanceof BasicStream) + { + this.os = new BasicStream(arg.instance, Protocol.currentProtocolEncoding); + this.os.swap(arg); + } + else if(arg instanceof OutgoingAsync) + { + this.out = arg; + } + else + { + Debug.assert(arg instanceof BatchOutgoingAsync); + this.batchOut = arg; + } + }; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectionBatchOutgoingAsync.js b/js/src/Ice/ConnectionBatchOutgoingAsync.js new file mode 100644 index 00000000000..f4f4d801544 --- /dev/null +++ b/js/src/Ice/ConnectionBatchOutgoingAsync.js @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_FOR_ACTIONSCRIPT_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/BatchOutgoingAsync"); + + var Ice = global.Ice || {}; + + var BatchOutgoingAsync = Ice.BatchOutgoingAsync; + + Ice.ConnectionBatchOutgoingAsync = Ice.Class(BatchOutgoingAsync, { + __init__: function(con, communicator, operation) + { + BatchOutgoingAsync.call(this, communicator, operation); + this._connection = con; + }, + __send: function() + { + this._connection.flushAsyncBatchRequests(this); + } + }); + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectionI.js b/js/src/Ice/ConnectionI.js new file mode 100644 index 00000000000..f08d9474bd3 --- /dev/null +++ b/js/src/Ice/ConnectionI.js @@ -0,0 +1,2030 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncStatus"); + require("Ice/AsyncResultBase"); + require("Ice/BasicStream"); + require("Ice/ConnectionBatchOutgoingAsync"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/HashMap"); + require("Ice/IncomingAsync"); + require("Ice/LocalExceptionWrapper"); + require("Ice/Promise"); + require("Ice/Protocol"); + require("Ice/SocketOperation"); + require("Ice/Timer"); + require("Ice/TraceUtil"); + require("Ice/Version"); + require("Ice/Exception"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var AsyncStatus = Ice.AsyncStatus; + var AsyncResultBase = Ice.AsyncResultBase; + var BasicStream = Ice.BasicStream; + var ConnectionBatchOutgoingAsync = Ice.ConnectionBatchOutgoingAsync; + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var HashMap = Ice.HashMap; + var IncomingAsync = Ice.IncomingAsync; + var LocalExceptionWrapper = Ice.LocalExceptionWrapper; + var Promise = Ice.Promise; + var Protocol = Ice.Protocol; + var SocketOperation = Ice.SocketOperation; + var Timer = Ice.Timer; + var TraceUtil = Ice.TraceUtil; + var ProtocolVersion = Ice.ProtocolVersion; + var EncodingVersion = Ice.EncodingVersion; + + var StateNotInitialized = 0; + var StateNotValidated = 1; + var StateActive = 2; + var StateHolding = 3; + var StateClosing = 4; + var StateClosed = 5; + var StateFinished = 6; + + var MessageInfo = function(instance) + { + this.stream = new BasicStream(instance, Protocol.currentProtocolEncoding, false); + + this.invokeNum = 0; + this.requestId = 0; + this.compress = false; + this.servantManager = null; + this.adapter = null; + this.outAsync = null; + }; + + var Class = Ice.Class; + + var ConnectionI = Class({ + __init__: function(communicator, instance, reaper, transceiver, endpoint, incoming, adapter) + { + this._communicator = communicator; + this._instance = instance; + this._reaper = reaper; + this._transceiver = transceiver; + this._desc = transceiver.toString(); + this._type = transceiver.type(); + this._endpoint = endpoint; + this._incoming = incoming; + this._adapter = adapter; + var initData = instance.initializationData(); + this._logger = initData.logger; // Cached for better performance. + this._traceLevels = instance.traceLevels(); // Cached for better performance. + this._timer = instance.timer(); + this._writeTimeoutId = 0; + this._writeTimeoutScheduled = false; + this._readTimeoutId = 0; + this._readTimeoutScheduled = false; + + this._hasMoreData = { value: false }; + + this._warn = initData.properties.getPropertyAsInt("Ice.Warn.Connections") > 0; + this._warnUdp = instance.initializationData().properties.getPropertyAsInt("Ice.Warn.Datagrams") > 0; + this._acmAbsoluteTimeoutMillis = 0; + + this._nextRequestId = 1; + this._batchAutoFlush = initData.properties.getPropertyAsIntWithDefault("Ice.BatchAutoFlush", 1) > 0 ? true : false; + this._batchStream = new BasicStream(instance, Protocol.currentProtocolEncoding, this._batchAutoFlush); + this._batchStreamInUse = false; + this._batchRequestNum = 0; + this._batchRequestCompress = false; + this._batchMarker = 0; + + this._sendStreams = []; + + this._readStream = new BasicStream(instance, Protocol.currentProtocolEncoding); + this._readHeader = false; + this._writeStream = new BasicStream(instance, Protocol.currentProtocolEncoding); + + this._readStreamPos = -1; + this._writeStreamPos = -1; + + this._dispatchCount = 0; + + this._state = StateNotInitialized; + this._shutdownInitiated = false; + this._validated = false; + + this._readProtocol = new ProtocolVersion(); + this._readProtocolEncoding = new EncodingVersion(); + + this._asyncRequests = new HashMap(); // Map<int, OutgoingAsync> + + this._exception = null; + + this._startPromise = null; + this._closePromises = []; + this._holdPromises = []; + this._finishedPromises = []; + + if(this._adapter !== null) + { + this._servantManager = this._adapter.getServantManager(); + } + else + { + this._servantManager = null; + } + + if(this._endpoint.datagram()) + { + this._acmTimeout = 0; + } + else + { + if(this._adapter !== null) + { + this._acmTimeout = this._adapter.getACM(); + } + else + { + this._acmTimeout = this._instance.clientACM(); + } + } + }, + start: function() + { + Debug.assert(this._startPromise === null); + + try + { + // The connection might already be closed if the communicator was destroyed. + if(this._state >= StateClosed) + { + Debug.assert(this._exception !== null); + return new Promise().fail(this._exception); + } + + this._startPromise = new Promise(); + var self = this; + this._transceiver.setCallbacks( + function() { self.message(SocketOperation.Write); }, // connected callback + function() { self.message(SocketOperation.Read); }, // read callback + function() { self.message(SocketOperation.Write); } // write callback + ); + this.initialize(); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.exception(ex); + } + return new Promise().fail(ex); + } + + return this._startPromise; + }, + activate: function() + { + if(this._state <= StateNotValidated) + { + return; + } + + if(this._acmTimeout > 0) + { + this._acmAbsoluteTimeoutMillis = Date.now() + this._acmTimeout * 1000; + } + + this.setState(StateActive); + }, + hold: function() + { + if(this._state <= StateNotValidated) + { + return; + } + + this.setState(StateHolding); + }, + destroy: function(reason) + { + switch(reason) + { + case ConnectionI.ObjectAdapterDeactivated: + { + this.setStateEx(StateClosing, new Ice.ObjectAdapterDeactivatedException()); + break; + } + + case ConnectionI.CommunicatorDestroyed: + { + this.setStateEx(StateClosing, new Ice.CommunicatorDestroyedException()); + break; + } + } + }, + close: function(force) + { + var __r = new AsyncResultBase(this._communicator, "close", this, null, null); + + if(force) + { + this.setStateEx(StateClosed, new Ice.ForcedCloseConnectionException()); + __r.succeed(__r); + } + else + { + // + // If we do a graceful shutdown, then we wait until all + // outstanding requests have been completed. Otherwise, + // the CloseConnectionException will cause all outstanding + // requests to be retried, regardless of whether the + // server has processed them or not. + // + this._closePromises.push(__r); + this.checkClose(); + } + + return __r; + }, + checkClose: function() + { + // + // If close(false) has been called, then we need to check if all + // requests have completed and we can transition to StateClosing. + // We also complete outstanding promises. + // + if(this._asyncRequests.size === 0 && this._closePromises.length > 0) + { + this.setStateEx(StateClosing, new Ice.CloseConnectionException()); + for(var i = 0; i < this._closePromises.length; ++i) + { + this._closePromises[i].succeed(this._closePromises[i]); + } + this._closePromises = []; + } + }, + isActiveOrHolding: function() + { + return this._state > StateNotValidated && this._state < StateClosing; + }, + isFinished: function() + { + if(this._state !== StateFinished || this._dispatchCount !== 0) + { + return false; + } + + Debug.assert(this._state === StateFinished); + return true; + }, + throwException: function() + { + if(this._exception !== null) + { + Debug.assert(this._state >= StateClosing); + throw this._exception; + } + }, + waitUntilHolding: function() + { + var promise = new Promise(); + this._holdPromises.push(promise); + this.checkState(); + return promise; + }, + waitUntilFinished: function() + { + var promise = new Promise(); + this._finishedPromises.push(promise); + this.checkState(); + return promise; + }, + monitor: function(now) + { + if(this._state !== StateActive) + { + return; + } + + // + // Active connection management for idle connections. + // + if(this._acmTimeout <= 0 || + this._asyncRequests.size > 0 || this._dispatchCount > 0 || + this._readStream.size > Protocol.headerSize || !this._writeStream.isEmpty() || + !this._batchStream.isEmpty()) + { + return; + } + + if(now >= this._acmAbsoluteTimeoutMillis) + { + this.setStateEx(StateClosing, new Ice.ConnectionTimeoutException()); + } + }, + sendAsyncRequest: function(out, compress, response) + { + var requestId = 0; + var os = out.__os(); + + if(this._exception !== null) + { + // + // If the connection is closed before we even have a chance + // to send our request, we always try to send the request + // again. + // + throw new LocalExceptionWrapper(this._exception, true); + } + + Debug.assert(this._state > StateNotValidated); + Debug.assert(this._state < StateClosing); + + // + // Ensure the message isn't bigger than what we can send with the + // transport. + // + this._transceiver.checkSendSize(os, this._instance.messageSizeMax()); + + if(response) + { + // + // Create a new unique request ID. + // + requestId = this._nextRequestId++; + if(requestId <= 0) + { + this._nextRequestId = 1; + requestId = this._nextRequestId++; + } + + // + // Fill in the request ID. + // + os.pos = Protocol.headerSize; + os.writeInt(requestId); + } + + var status; + try + { + status = this.sendMessage(OutgoingMessage.create(out, out.__os(), compress, requestId)); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + Debug.assert(this._exception !== null); + throw this._exception; + } + else + { + throw ex; + } + } + + if(response) + { + // + // Add to the async requests map. + // + this._asyncRequests.set(requestId, out); + } + + return status; + }, + prepareBatchRequest: function(os) + { + if(this._exception !== null) + { + // + // If there were no batch requests queued when the connection failed, we can safely + // retry with a new connection. Otherwise, we must throw to notify the caller that + // some previous batch requests were not sent. + // + if(this._batchStream.isEmpty()) + { + throw new LocalExceptionWrapper(this._exception, true); + } + else + { + throw this._exception; + } + } + + Debug.assert(this._state > StateNotValidated); + Debug.assert(this._state < StateClosing); + + if(this._batchStream.isEmpty()) + { + try + { + this._batchStream.writeBlob(Protocol.requestBatchHdr); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + } + throw ex; + } + } + + this._batchStreamInUse = true; + this._batchMarker = this._batchStream.size; + this._batchStream.swap(os); + + // + // The batch stream now belongs to the caller, until + // finishBatchRequest() or abortBatchRequest() is called. + // + }, + finishBatchRequest: function(os, compress) + { + try + { + // + // Get the batch stream back. + // + this._batchStream.swap(os); + + if(this._exception !== null) + { + throw this._exception; + } + + var flush = false; + if(this._batchAutoFlush) + { + // + // Throw memory limit exception if the first message added causes us to go over + // limit. Otherwise put aside the marshalled message that caused limit to be + // exceeded and rollback stream to the marker. + try + { + this._transceiver.checkSendSize(this._batchStream.buffer, this._instance.messageSizeMax()); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + if(this._batchRequestNum > 0) + { + flush = true; + } + else + { + throw ex; + } + } + else + { + throw ex; + } + } + } + + if(flush) + { + // + // Temporarily save the last request. + // + var sz = this._batchStream.size - this._batchMarker; + this._batchStream.pos = this._batchMarker; + var lastRequest = this._batchStream.readBlob(sz); + this._batchStream.resize(this._batchMarker, false); + + try + { + // + // Fill in the number of requests in the batch. + // + this._batchStream.pos = Protocol.headerSize; + this._batchStream.writeInt(this._batchRequestNum); + + this.sendMessage(OutgoingMessage.createForStream(this._batchStream, this._batchRequestCompress, + true)); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + Debug.assert(this._exception !== null); + throw this._exception; + } + else + { + throw ex; + } + } + + // + // Reset the batch stream. + // + this._batchStream = + new BasicStream(this._instance, Protocol.currentProtocolEncoding, this._batchAutoFlush); + this._batchRequestNum = 0; + this._batchRequestCompress = false; + this._batchMarker = 0; + + // + // Check again if the last request doesn't exceed the maximum message size. + // + if(Protocol.requestBatchHdr.length + lastRequest.length > this._instance.messageSizeMax()) + { + ExUtil.throwMemoryLimitException( + Protocol.requestBatchHdr.length + lastRequest.length, + this._instance.messageSizeMax()); + } + + // + // Start a new batch with the last message that caused us to go over the limit. + // + this._batchStream.writeBlob(Protocol.requestBatchHdr); + this._batchStream.writeBlob(lastRequest); + } + + // + // Increment the number of requests in the batch. + // + ++this._batchRequestNum; + + // + // We compress the whole batch if there is at least one compressed + // message. + // + if(compress) + { + this._batchRequestCompress = true; + } + + // + // The batch stream is not in use anymore. + // + Debug.assert(this._batchStreamInUse); + this._batchStreamInUse = false; + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.abortBatchRequest(); + } + throw ex; + } + }, + abortBatchRequest: function() + { + this._batchStream = new BasicStream(this._instance, Protocol.currentProtocolEncoding, this._batchAutoFlush); + this._batchRequestNum = 0; + this._batchRequestCompress = false; + this._batchMarker = 0; + + Debug.assert(this._batchStreamInUse); + this._batchStreamInUse = false; + }, + flushBatchRequests: function() + { + var result = new ConnectionBatchOutgoingAsync(this, this._communicator, "flushBatchRequests"); + try + { + result.__send(); + } + catch(ex) + { + result.__exception(ex); + } + return result; + }, + flushAsyncBatchRequests: function(outAsync) + { + if(this._exception !== null) + { + throw this._exception; + } + + var status; + if(this._batchRequestNum === 0) + { + outAsync.__sent(this); + return AsyncStatus.Sent; + } + + // + // Fill in the number of requests in the batch. + // + this._batchStream.pos = Protocol.headerSize; + this._batchStream.writeInt(this._batchRequestNum); + + this._batchStream.swap(outAsync.__os()); + + try + { + status = this.sendMessage(OutgoingMessage.create(outAsync, outAsync.__os(), this._batchRequestCompress, + 0)); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + Debug.assert(this._exception !== null); + throw this._exception; + } + else + { + throw ex; + } + } + + // + // Reset the batch stream. + // + this._batchStream = new BasicStream(this._instance, Protocol.currentProtocolEncoding, this._batchAutoFlush); + this._batchRequestNum = 0; + this._batchRequestCompress = false; + this._batchMarker = 0; + return status; + }, + sendResponse: function(os, compressFlag) + { + Debug.assert(this._state > StateNotValidated); + + try + { + if(--this._dispatchCount === 0) + { + if(this._state === StateFinished) + { + this._reaper.add(this); + } + this.checkState(); + } + + if(this._state >= StateClosed) + { + Debug.assert(this._exception !== null); + throw this._exception; + } + + this.sendMessage(OutgoingMessage.createForStream(os, compressFlag !== 0, true)); + + if(this._state === StateClosing && this._dispatchCount === 0) + { + this.initiateShutdown(); + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + } + else + { + throw ex; + } + } + }, + sendNoResponse: function() + { + Debug.assert(this._state > StateNotValidated); + try + { + if(--this._dispatchCount === 0) + { + if(this._state === StateFinished) + { + this._reaper.add(this); + } + this.checkState(); + } + + if(this._state >= StateClosed) + { + Debug.assert(this._exception !== null); + throw this._exception; + } + + if(this._state === StateClosing && this._dispatchCount === 0) + { + this.initiateShutdown(); + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + } + else + { + throw ex; + } + } + }, + endpoint: function() + { + return this._endpoint; + }, + setAdapter: function(adapter) + { + if(this._state <= StateNotValidated || this._state >= StateClosing) + { + return; + } + Debug.assert(this._state < StateClosing); + + this._adapter = adapter; + + if(this._adapter !== null) + { + this._servantManager = this._adapter.getServantManager(); + if(this._servantManager === null) + { + this._adapter = null; + } + } + else + { + this._servantManager = null; + } + }, + getAdapter: function() + { + return this._adapter; + }, + getEndpoint: function() + { + return this._endpoint; + }, + createProxy: function(ident) + { + // + // Create a reference and return a reverse proxy for this + // reference. + // + return this._instance.proxyFactory().referenceToProxy( + this._instance.referenceFactory().createFixed(ident, this)); + }, + message: function(operation) + { + if(this._state >= StateClosed) + { + return; + } + + this.unscheduleTimeout(operation); + // + // Keep reading until no more data is available. + // + this._hasMoreData.value = (operation & SocketOperation.Read) !== 0; + do + { + var info = null; + + try + { + if((operation & SocketOperation.Write) !== 0 && this._writeStream.buffer.remaining > 0) + { + if(!this._transceiver.write(this._writeStream.buffer)) + { + Debug.assert(!this._writeStream.isEmpty()); + this.scheduleTimeout(SocketOperation.Write, this._endpoint.timeout()); + return; + } + Debug.assert(this._writeStream.buffer.remaining === 0); + } + if((operation & SocketOperation.Read) !== 0 && !this._readStream.isEmpty()) + { + if(this._readHeader) // Read header if necessary. + { + if(!this._transceiver.read(this._readStream.buffer, this._hasMoreData)) + { + // + // We didn't get enough data to complete the header. + // + return; + } + + Debug.assert(this._readStream.buffer.remaining === 0); + this._readHeader = false; + + var pos = this._readStream.pos; + if(pos < Protocol.headerSize) + { + // + // This situation is possible for small UDP packets. + // + throw new Ice.IllegalMessageSizeException(); + } + + this._readStream.pos = 0; + var magic0 = this._readStream.readByte(); + var magic1 = this._readStream.readByte(); + var magic2 = this._readStream.readByte(); + var magic3 = this._readStream.readByte(); + if(magic0 !== Protocol.magic[0] || magic1 !== Protocol.magic[1] || + magic2 !== Protocol.magic[2] || magic3 !== Protocol.magic[3]) + { + var bme = new Ice.BadMagicException(); + bme.badMagic = Ice.Buffer.createNative([magic0, magic1, magic2, magic3]); + throw bme; + } + + this._readProtocol.__read(this._readStream); + Protocol.checkSupportedProtocol(this._readProtocol); + + this._readProtocolEncoding.__read(this._readStream); + Protocol.checkSupportedProtocolEncoding(this._readProtocolEncoding); + + this._readStream.readByte(); // messageType + this._readStream.readByte(); // compress + var size = this._readStream.readInt(); + if(size < Protocol.headerSize) + { + throw new Ice.IllegalMessageSizeException(); + } + if(size > this._instance.messageSizeMax()) + { + ExUtil.throwMemoryLimitException(size, this._instance.messageSizeMax()); + } + if(size > this._readStream.size) + { + this._readStream.resize(size); + } + this._readStream.pos = pos; + } + + if(this._readStream.pos != this._readStream.size) + { + if(this._endpoint.datagram()) + { + throw new Ice.DatagramLimitException(); // The message was truncated. + } + else + { + if(!this._transceiver.read(this._readStream.buffer, this._hasMoreData)) + { + Debug.assert(!this._readStream.isEmpty()); + this.scheduleTimeout(SocketOperation.Read, this._endpoint.timeout()); + return; + } + Debug.assert(this._readStream.buffer.remaining === 0); + } + } + } + + if(this._state <= StateNotValidated) + { + if(this._state === StateNotInitialized && !this.initialize()) + { + return; + } + + if(this._state <= StateNotValidated && !this.validate()) + { + return; + } + + this._transceiver.unregister(); + + // + // We start out in holding state. + // + this.setState(StateHolding); + } + else + { + Debug.assert(this._state <= StateClosing); + + // + // We parse messages first, if we receive a close + // connection message we won't send more messages. + // + if((operation & SocketOperation.Read) !== 0) + { + info = this.parseMessage(); + } + + if((operation & SocketOperation.Write) !== 0) + { + this.sendNextMessage(); + } + + // + // We increment the dispatch count to prevent the + // communicator destruction during the callback. + // + if(info !== null && info.outAsync !== null) + { + ++this._dispatchCount; + } + } + } + catch(ex) + { + if(ex instanceof Ice.DatagramLimitException) // Expected. + { + if(this._warnUdp) + { + this._logger.warning("maximum datagram size of " + this._readStream.pos + " exceeded"); + } + this._readStream.resize(Protocol.headerSize); + this._readStream.pos = 0; + this._readHeader = true; + return; + } + else if(ex instanceof Ice.SocketException) + { + this.setStateEx(StateClosed, ex); + return; + } + else if(ex instanceof Ice.LocalException) + { + if(this._endpoint.datagram()) + { + if(this._warn) + { + this._logger.warning("datagram connection exception:\n" + ex + '\n' + this._desc); + } + this._readStream.resize(Protocol.headerSize); + this._readStream.pos = 0; + this._readHeader = true; + } + else + { + this.setStateEx(StateClosed, ex); + } + return; + } + else + { + throw ex; + } + } + + if(this._acmTimeout > 0) + { + this._acmAbsoluteTimeoutMillis = Date.now() + this._acmTimeout * 1000; + } + + this.dispatch(info); + } + while(this._hasMoreData.value); + }, + dispatch: function(info) + { + // + // Notify the factory that the connection establishment and + // validation has completed. + // + if(this._startPromise !== null) + { + this._startPromise.succeed(); + this._startPromise = null; + } + + if(info !== null) + { + if(info.outAsync !== null) + { + info.outAsync.__finished(info.stream); + } + + if(info.invokeNum > 0) + { + this.invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, + info.adapter); + } + } + + // + // Decrease dispatch count. + // + if(info !== null && info.outAsync !== null) + { + if(--this._dispatchCount === 0) + { + if(this._state === StateClosing && !this._shutdownInitiated) + { + try + { + this.initiateShutdown(); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + } + else + { + throw ex; + } + } + } + else if(this._state === StateFinished) + { + this._reaper.add(this); + } + this.checkState(); + } + } + }, + finish: function() + { + Debug.assert(this._state === StateClosed); + this.unscheduleTimeout(SocketOperation.Read | SocketOperation.Write | SocketOperation.Connect); + + if(this._startPromise !== null) + { + this._startPromise.fail(this._exception); + this._startPromise = null; + } + + if(this._sendStreams.length > 0) + { + if(!this._writeStream.isEmpty()) + { + // + // Return the stream to the outgoing call. This is important for + // retriable AMI calls which are not marshalled again. + // + var message = this._sendStreams[0]; + this._writeStream.swap(message.stream); + } + + // + // NOTE: for twoway requests which are not sent, finished can be called twice: the + // first time because the outgoing is in the _sendStreams set and the second time + // because it's either in the _requests/_asyncRequests set. This is fine, only the + // first call should be taken into account by the implementation of finished. + // + for(var i = 0; i < this._sendStreams.length; ++i) + { + var p = this._sendStreams[i]; + if(p.requestId > 0) + { + this._asyncRequests.delete(p.requestId); + } + p.finished(this._exception); + } + this._sendStreams = []; + } + + for(var e = this._asyncRequests.entries; e !== null; e = e.next) + { + e.value.__finishedEx(this._exception, true); + } + this._asyncRequests.clear(); + + // + // This must be done last as this will cause waitUntilFinished() to return (and communicator + // objects such as the timer might be destroyed too). + // + if(this._dispatchCount === 0) + { + this._reaper.add(this); + } + this.setState(StateFinished); + }, + toString: function() + { + return this._desc; + }, + timedOut: function(event) + { + if(this._state <= StateNotValidated) + { + this.setStateEx(StateClosed, new Ice.ConnectTimeoutException()); + } + else if(this._state < StateClosing) + { + this.setStateEx(StateClosed, new Ice.TimeoutException()); + } + else if(this._state === StateClosing) + { + this.setStateEx(StateClosed, new Ice.CloseTimeoutException()); + } + }, + type: function() + { + return this._type; + }, + timeout: function() + { + return this._endpoint.timeout(); + }, + getInfo: function() + { + if(this._state >= StateClosed) + { + throw this._exception; + } + var info = this._transceiver.getInfo(); + info.adapterName = this._adapter !== null ? this._adapter.getName() : ""; + info.incoming = this._incoming; + return info; + }, + exception: function(ex) + { + this.setStateEx(StateClosed, ex); + }, + invokeException: function(ex, invokeNum) + { + // + // Fatal exception while invoking a request. Since sendResponse/sendNoResponse isn't + // called in case of a fatal exception we decrement this._dispatchCount here. + // + + this.setStateEx(StateClosed, ex); + + if(invokeNum > 0) + { + Debug.assert(this._dispatchCount > 0); + this._dispatchCount -= invokeNum; + Debug.assert(this._dispatchCount >= 0); + if(this._dispatchCount === 0) + { + if(this._state === StateFinished) + { + this._reaper.add(this); + } + this.checkState(); + } + } + }, + setStateEx: function(state, ex) + { + Debug.assert(ex instanceof Ice.LocalException); + + // + // If setState() is called with an exception, then only closed + // and closing states are permissible. + // + Debug.assert(state >= StateClosing); + + if(this._state === state) // Don't switch twice. + { + return; + } + + if(this._exception === null) + { + this._exception = ex; + + // + // We don't warn if we are not validated. + // + if(this._warn && this._validated) + { + // + // Don't warn about certain expected exceptions. + // + if(!(this._exception instanceof Ice.CloseConnectionException || + this._exception instanceof Ice.ForcedCloseConnectionException || + this._exception instanceof Ice.ConnectionTimeoutException || + this._exception instanceof Ice.CommunicatorDestroyedException || + this._exception instanceof Ice.ObjectAdapterDeactivatedException || + (this._exception instanceof Ice.ConnectionLostException && this._state === StateClosing))) + { + this.warning("connection exception", this._exception); + } + } + } + + // + // We must set the new state before we notify requests of any + // exceptions. Otherwise new requests may retry on a + // connection that is not yet marked as closed or closing. + // + this.setState(state); + }, + setState: function(state) + { + // + // We don't want to send close connection messages if the endpoint + // only supports oneway transmission from client to server. + // + if(this._endpoint.datagram() && state === StateClosing) + { + state = StateClosed; + } + + // + // Skip graceful shutdown if we are destroyed before validation. + // + if(this._state <= StateNotValidated && state === StateClosing) + { + state = StateClosed; + } + + if(this._state === state) // Don't switch twice. + { + return; + } + + try + { + switch(state) + { + case StateNotInitialized: + { + Debug.assert(false); + break; + } + + case StateNotValidated: + { + if(this._state !== StateNotInitialized) + { + Debug.assert(this._state === StateClosed); + return; + } + // + // Register to receive validation message. + // + if(!this._endpoint.datagram() && !this._incoming) + { + // + // Once validation is complete, a new connection starts out in the + // Holding state. We only want to register the transceiver now if we + // need to receive data in order to validate the connection. + // + this._transceiver.register(); + } + break; + } + + case StateActive: + { + // + // Can only switch from holding or not validated to + // active. + // + if(this._state !== StateHolding && this._state !== StateNotValidated) + { + return; + } + this._transceiver.register(); + break; + } + + case StateHolding: + { + // + // Can only switch from active or not validated to + // holding. + // + if(this._state !== StateActive && this._state !== StateNotValidated) + { + return; + } + if(this._state === StateActive) + { + this._transceiver.unregister(); + } + break; + } + + case StateClosing: + { + // + // Can't change back from closed. + // + if(this._state >= StateClosed) + { + return; + } + if(this._state === StateHolding) + { + // We need to continue to read in closing state. + this._transceiver.register(); + } + break; + } + + case StateClosed: + { + if(this._state === StateFinished) + { + return; + } + this._transceiver.unregister(); + break; + } + + case StateFinished: + { + Debug.assert(this._state === StateClosed); + this._transceiver.close(); + this._communicator = null; + break; + } + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + var msg = "unexpected connection exception:\n " + this._desc + "\n" + ExUtil.toString(ex); + this._instance.initializationData().logger.error(msg); + } + else + { + throw ex; + } + } + + // + // We only register with the connection monitor if our new state + // is StateActive. Otherwise we unregister with the connection + // monitor, but only if we were registered before, i.e., if our + // old state was StateActive. + // + if(this._acmTimeout > 0) + { + if(state === StateActive) + { + this._instance.connectionMonitor().add(this); + } + else if(this._state === StateActive) + { + this._instance.connectionMonitor().remove(this); + } + } + + this._state = state; + + if(this._state === StateClosing && this._dispatchCount === 0) + { + try + { + this.initiateShutdown(); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setStateEx(StateClosed, ex); + } + else + { + throw ex; + } + } + } + else if(this._state === StateClosed) + { + this.finish(); + } + + this.checkState(); + }, + initiateShutdown: function() + { + Debug.assert(this._state === StateClosing); + Debug.assert(this._dispatchCount === 0); + Debug.assert(!this._shutdownInitiated); + + if(!this._endpoint.datagram()) + { + // + // Before we shut down, we send a close connection + // message. + // + var os = new BasicStream(this._instance, Protocol.currentProtocolEncoding, false); + os.writeBlob(Protocol.magic); + Protocol.currentProtocol.__write(os); + Protocol.currentProtocolEncoding.__write(os); + os.writeByte(Protocol.closeConnectionMsg); + os.writeByte(0); // compression status: always report 0 for CloseConnection. + os.writeInt(Protocol.headerSize); // Message size. + + var status = this.sendMessage(OutgoingMessage.createForStream(os, false, false)); + if((status & AsyncStatus.Sent) > 0) + { + // + // Schedule the close timeout to wait for the peer to close the connection. + // + this.scheduleTimeout(SocketOperation.Write, this.closeTimeout()); + } + + // + // The CloseConnection message should be sufficient. Closing the write + // end of the socket is probably an artifact of how things were done + // in IIOP. In fact, shutting down the write end of the socket causes + // problems on Windows by preventing the peer from using the socket. + // For example, the peer is no longer able to continue writing a large + // message after the socket is shutdown. + // + //this._transceiver.shutdownWrite(); + } + }, + initialize: function() + { + var s = this._transceiver.initialize(this._readStream.buffer, this._writeStream.buffer); + if(s != SocketOperation.None) + { + this.scheduleTimeout(s, this.connectTimeout()); + return false; + } + + // + // Update the connection description once the transceiver is initialized. + // + this._desc = this._transceiver.toString(); + this.setState(StateNotValidated); + return true; + }, + validate: function() + { + if(!this._endpoint.datagram()) // Datagram connections are always implicitly validated. + { + if(this._adapter !== null) // The server side has the active role for connection validation. + { + if(this._writeStream.size === 0) + { + this._writeStream.writeBlob(Protocol.magic); + Protocol.currentProtocol.__write(this._writeStream); + Protocol.currentProtocolEncoding.__write(this._writeStream); + this._writeStream.writeByte(Protocol.validateConnectionMsg); + this._writeStream.writeByte(0); // Compression status (always zero for validate connection). + this._writeStream.writeInt(Protocol.headerSize); // Message size. + TraceUtil.traceSend(this._writeStream, this._logger, this._traceLevels); + this._writeStream.prepareWrite(); + } + + if(this._writeStream.pos != this._writeStream.size && + !this._transceiver.write(this._writeStream.buffer)) + { + this.scheduleTimeout(SocketOperation.Write, this.connectTimeout()); + return false; + } + } + else // The client side has the passive role for connection validation. + { + if(this._readStream.size === 0) + { + this._readStream.resize(Protocol.headerSize); + this._readStream.pos = 0; + } + + if(this._readStream.pos !== this._readStream.size && + !this._transceiver.read(this._readStream.buffer, this._hasMoreData)) + { + this.scheduleTimeout(SocketOperation.Read, this.connectTimeout()); + return false; + } + + Debug.assert(this._readStream.pos === Protocol.headerSize); + this._readStream.pos = 0; + var m = this._readStream.readBlob(4); + if(m[0] !== Protocol.magic[0] || m[1] !== Protocol.magic[1] || + m[2] !== Protocol.magic[2] || m[3] !== Protocol.magic[3]) + { + var bme = new Ice.BadMagicException(); + bme.badMagic = m; + throw bme; + } + + this._readProtocol.__read(this._readStream); + Protocol.checkSupportedProtocol(this._readProtocol); + + this._readProtocolEncoding.__read(this._readStream); + Protocol.checkSupportedProtocolEncoding(this._readProtocolEncoding); + + var messageType = this._readStream.readByte(); + if(messageType !== Protocol.validateConnectionMsg) + { + throw new Ice.ConnectionNotValidatedException(); + } + this._readStream.readByte(); // Ignore compression status for validate connection. + var size = this._readStream.readInt(); + if(size !== Protocol.headerSize) + { + throw new Ice.IllegalMessageSizeException(); + } + TraceUtil.traceRecv(this._readStream, this._logger, this._traceLevels); + this._validated = true; + } + } + + this._writeStream.resize(0); + this._writeStream.pos = 0; + + this._readStream.resize(Protocol.headerSize); + this._readHeader = true; + this._readStream.pos = 0; + + return true; + }, + sendNextMessage: function() + { + if(this._sendStreams.length === 0) + { + return; + } + + Debug.assert(!this._writeStream.isEmpty() && this._writeStream.pos === this._writeStream.size); + try + { + while(true) + { + // + // Notify the message that it was sent. + // + var message = this._sendStreams.shift(); + this._writeStream.swap(message.stream); + message.sent(this); + + // + // If there's nothing left to send, we're done. + // + if(this._sendStreams.length === 0) + { + break; + } + + // + // If we are in the closed state, don't continue sending. + // + // The connection can be in the closed state if parseMessage + // (called before sendNextMessage by message()) closes the + // connection. + // + if(this._state >= StateClosed) + { + return; + } + + // + // Otherwise, prepare the next message stream for writing. + // + message = this._sendStreams[0]; + Debug.assert(!message.prepared); + var stream = message.stream; + + stream.pos = 10; + stream.writeInt(stream.size); + stream.prepareWrite(); + message.prepared = true; + + if(message.outAsync !== null) + { + TraceUtil.trace("sending asynchronous request", stream, this._logger, this._traceLevels); + } + else + { + TraceUtil.traceSend(stream, this._logger, this._traceLevels); + } + this._writeStream.swap(message.stream); + + // + // Send the message. + // + if(this._writeStream.pos != this._writeStream.size && + !this._transceiver.write(this._writeStream.buffer)) + { + Debug.assert(!this._writeStream.isEmpty()); + this.scheduleTimeout(SocketOperation.Write, this._endpoint.timeout()); + return; + } + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.setState(StateClosed, ex); + return; + } + else + { + throw ex; + } + } + + Debug.assert(this._writeStream.isEmpty()); + + // + // If all the messages were sent and we are in the closing state, we schedule + // the close timeout to wait for the peer to close the connection. + // + if(this._state === StateClosing) + { + this.scheduleTimeout(SocketOperation.Write, this.closeTimeout()); + } + }, + sendMessage: function(message) + { + if(this._sendStreams.length > 0) + { + message.doAdopt(); + this._sendStreams.push(message); + return AsyncStatus.Queued; + } + Debug.assert(this._state < StateClosed); + + Debug.assert(!message.prepared); + + var stream = message.stream; + stream.pos = 10; + stream.writeInt(stream.size); + stream.prepareWrite(); + message.prepared = true; + + TraceUtil.trace("sending asynchronous request", message.stream, this._logger, this._traceLevels); + + if(this._transceiver.write(message.stream.buffer)) + { + // + // Entire buffer was written immediately. + // + message.sent(this); + if(this._acmTimeout > 0) + { + this._acmAbsoluteTimeoutMillis = Date.now() + this._acmTimeout * 1000; + } + return AsyncStatus.Sent; + } + message.doAdopt(); + + this._writeStream.swap(message.stream); + this._sendStreams.push(message); + this.scheduleTimeout(SocketOperation.Write, this._endpoint.timeout()); + + return AsyncStatus.Queued; + }, + parseMessage: function() + { + Debug.assert(this._state > StateNotValidated && this._state < StateClosed); + + var info = new MessageInfo(this._instance); + + this._readStream.swap(info.stream); + this._readStream.resize(Protocol.headerSize); + this._readStream.pos = 0; + this._readHeader = true; + + // + // Connection is validated on first message. This is only used by + // setState() to check wether or not we can print a connection + // warning (a client might close the connection forcefully if the + // connection isn't validated). + // + this._validated = true; + + Debug.assert(info.stream.pos === info.stream.size); + + try + { + // + // We don't need to check magic and version here. This has already + // been done by the caller. + // + info.stream.pos = 8; + var messageType = info.stream.readByte(); + info.compress = info.stream.readByte(); + if(info.compress === 2) + { + var ex = new Ice.FeatureNotSupportedException(); + ex.unsupportedFeature = "Cannot uncompress compressed message"; + throw ex; + } + info.stream.pos = Protocol.headerSize; + + switch(messageType) + { + case Protocol.closeConnectionMsg: + { + TraceUtil.traceRecv(info.stream, this._logger, this._traceLevels); + if(this._endpoint.datagram()) + { + if(this._warn) + { + this._logger.warning("ignoring close connection message for datagram connection:\n" + + this._desc); + } + } + else + { + this.setStateEx(StateClosed, new Ice.CloseConnectionException()); + } + break; + } + + case Protocol.requestMsg: + { + if(this._state === StateClosing) + { + TraceUtil.trace("received request during closing\n" + + "(ignored by server, client will retry)", + info.stream, this._logger, this._traceLevels); + } + else + { + TraceUtil.traceRecv(info.stream, this._logger, this._traceLevels); + info.requestId = info.stream.readInt(); + info.invokeNum = 1; + info.servantManager = this._servantManager; + info.adapter = this._adapter; + ++this._dispatchCount; + } + break; + } + + case Protocol.requestBatchMsg: + { + if(this._state === StateClosing) + { + TraceUtil.trace("received batch request during closing\n" + + "(ignored by server, client will retry)", + info.stream, this._logger, this._traceLevels); + } + else + { + TraceUtil.traceRecv(info.stream, this._logger, this._traceLevels); + info.invokeNum = info.stream.readInt(); + if(info.invokeNum < 0) + { + info.invokeNum = 0; + throw new Ice.UnmarshalOutOfBoundsException(); + } + info.servantManager = this._servantManager; + info.adapter = this._adapter; + this._dispatchCount += info.invokeNum; + } + break; + } + + case Protocol.replyMsg: + { + TraceUtil.traceRecv(info.stream, this._logger, this._traceLevels); + info.requestId = info.stream.readInt(); + info.outAsync = this._asyncRequests.get(info.requestId); + this._asyncRequests.delete(info.requestId); + if(info.outAsync === undefined) + { + throw new Ice.UnknownRequestIdException(); + } + this.checkClose(); + break; + } + + case Protocol.validateConnectionMsg: + { + TraceUtil.traceRecv(info.stream, this._logger, this._traceLevels); + if(this._warn) + { + this._logger.warning("ignoring unexpected validate connection message:\n" + this._desc); + } + break; + } + + default: + { + TraceUtil.trace("received unknown message\n(invalid, closing connection)", + info.stream, this._logger, this._traceLevels); + throw new Ice.UnknownMessageException(); + } + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + if(this._endpoint.datagram()) + { + if(this._warn) + { + this._logger.warning("datagram connection exception:\n" + ex + '\n' + this._desc); + } + } + else + { + this.setStateEx(StateClosed, ex); + } + } + else + { + throw ex; + } + } + + return info; + }, + invokeAll: function(stream, invokeNum, requestId, compress, servantManager, adapter) + { + var inc = null; + try + { + while(invokeNum > 0) + { + // + // Prepare the invocation. + // + var response = !this._endpoint.datagram() && requestId !== 0; + inc = new IncomingAsync(this._instance, this, adapter, response, compress, requestId); + + // + // Dispatch the invocation. + // + inc.invoke(servantManager, stream); + + --invokeNum; + inc = null; + } + + stream.clear(); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.invokeException(ex, invokeNum); + } + else + { + throw ex; + } + } + }, + scheduleTimeout: function(op, timeout) + { + if(timeout < 0) + { + return; + } + + var self = this; + if((op & SocketOperation.Read) !== 0) + { + this._readTimeoutId = this._timer.schedule(function() { self.timedOut(); }, timeout); + this._readTimeoutScheduled = true; + } + if((op & (SocketOperation.Write | SocketOperation.Connect)) !== 0) + { + this._writeTimeoutId = this._timer.schedule(function() { self.timedOut(); }, timeout); + this._writeTimeoutScheduled = true; + } + }, + unscheduleTimeout: function(op) + { + if((op & SocketOperation.Read) !== 0 && this._readTimeoutScheduled) + { + this._timer.cancel(this._readTimeoutId); + this._readTimeoutScheduled = false; + } + if((op & (SocketOperation.Write | SocketOperation.Connect)) !== 0 && this._writeTimeoutScheduled) + { + this._timer.cancel(this._writeTimeoutId); + this._writeTimeoutScheduled = false; + } + }, + connectTimeout: function() + { + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideConnectTimeout) + { + return defaultsAndOverrides.overrideConnectTimeoutValue; + } + else + { + return this._endpoint.timeout(); + } + }, + closeTimeout: function() + { + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideCloseTimeout) + { + return defaultsAndOverrides.overrideCloseTimeoutValue; + } + else + { + return this._endpoint.timeout(); + } + }, + warning: function(msg, ex) + { + this._logger.warning(msg + ":\n" + this._desc + "\n" + ExUtil.toString(ex)); + }, + checkState: function() + { + if(this._state < StateHolding || this._dispatchCount > 0) + { + return; + } + + var i; + if(this._holdPromises.length > 0) + { + for(i = 0; i < this._holdPromises.length; ++i) + { + this._holdPromises[i].succeed(); + } + this._holdPromises = []; + } + + // + // We aren't finished until the state is finished and all + // outstanding requests are completed. Otherwise we couldn't + // guarantee that there are no outstanding calls when deactivate() + // is called on the servant locators. + // + if(this._state === StateFinished && this._finishedPromises.length > 0) + { + // + // Clear the OA. See bug 1673 for the details of why this is necessary. + // + this._adapter = null; + + for(i = 0; i < this._finishedPromises.length; ++i) + { + this._finishedPromises[i].succeed(); + } + this._finishedPromises = []; + } + } + }); + + // DestructionReason. + ConnectionI.ObjectAdapterDeactivated = 0; + ConnectionI.CommunicatorDestroyed = 1; + + Ice.ConnectionI = ConnectionI; + global.Ice = Ice; + + var OutgoingMessage = Class({ + __init__: function() + { + this.stream = null; + this.outAsync = null; + this.compress = false; + this.requestId = 0; + this.prepared = false; + this.isSent = false; + }, + doAdopt: function() + { + if(this.adopt) + { + var stream = new BasicStream(this.stream.instance, Protocol.currentProtocolEncoding); + stream.swap(this.stream); + this.stream = stream; + this.adopt = false; + } + }, + sent: function(connection) + { + this.isSent = true; // The message is sent. + + if(this.outAsync !== null) + { + this.outAsync.__sent(connection); + } + }, + finished: function(ex) + { + if(this.outAsync !== null) + { + this.outAsync.__finishedEx(ex, this.isSent); + } + } + }); + + OutgoingMessage.createForStream = function(stream, compress, adopt) + { + var m = new OutgoingMessage(); + m.stream = stream; + m.compress = compress; + m.adopt = adopt; + m.isSent = false; + m.requestId = 0; + m.outAsync = null; + return m; + }; + + OutgoingMessage.create = function(out, stream, compress, requestId) + { + var m = new OutgoingMessage(); + m.stream = stream; + m.compress = compress; + m.outAsync = out; + m.requestId = requestId; + m.isSent = false; + m.adopt = false; + return m; + }; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectionMonitor.js b/js/src/Ice/ConnectionMonitor.js new file mode 100644 index 00000000000..dffe8f945a8 --- /dev/null +++ b/js/src/Ice/ConnectionMonitor.js @@ -0,0 +1,114 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + + var ConnectionMonitor = Ice.Class({ + __init__: function(instance, interval) + { + this._instance = instance; + this._interval = interval; + this._scheduledInterval = 0; + this._timerToken = -1; + this._connections = []; + }, + destroy: function() + { + Debug.assert(this._instance !== null); + this._instance.timer().cancel(this._timerToken); + this._instance = null; + this._connections = null; + }, + checkIntervalForACM: function(acmTimeout) + { + if(acmTimeout <= 0) + { + return; + } + + // + // If Ice.MonitorConnections isn't set (_interval == 0), the given ACM is used + // to determine the check interval: 1/10 of the ACM timeout with a minmal value + // of 5 seconds and a maximum value of 5 minutes. + // + // Note: if Ice.MonitorConnections is set, the timer is scheduled only if ACM + // is configured for the communicator or some object adapters. + // + var interval; + if(this._interval === 0) + { + interval = Math.floor(Math.min(300, Math.max(5, acmTimeout / 10))); + } + else if(this._scheduledInterval === this._interval) + { + return; // Nothing to do, the timer is already scheduled. + } + else + { + interval = this._interval; + } + + // + // If no timer is scheduled yet or if the given ACM requires a smaller interval, + // we re-schedule the timer. + // + if(this._scheduledInterval === 0 || this._scheduledInterval > interval) + { + this._scheduledInterval = interval; + this._instance.timer().cancel(this._timerToken); + var self = this; + this._timerToken = this._instance.timer().scheduleRepeated( + function() { self.runTimerTask(); }, interval * 1000); + } + }, + add: function(connection) + { + Debug.assert(this._instance !== null); + this._connections.push(connection); + }, + remove: function(connection) + { + Debug.assert(this._instance !== null); + var pos = this._connections.indexOf(connection); + Debug.assert(pos !== -1); + this._connections.splice(pos, 1); + }, + runTimerTask: function() + { + var now = Date.now(); + for(var i = 0; i < this._connections.length; ++i) + { + try + { + this._connections[i].monitor(now); + } + catch(ex) + { + if(this._instance === null) + { + return; + } + var msg = "exception in connection monitor:\n" + ExUtil.toString(ex); + this._instance.initializationData().logger.error(msg); + } + } + } + }); + + Ice.ConnectionMonitor = ConnectionMonitor; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectionReaper.js b/js/src/Ice/ConnectionReaper.js new file mode 100644 index 00000000000..af8faa2ef26 --- /dev/null +++ b/js/src/Ice/ConnectionReaper.js @@ -0,0 +1,39 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + + require("Ice/Class"); + + var Ice = global.Ice || {}; + + var ConnectionReaper = Ice.Class({ + __init__: function() + { + this._connections = []; + }, + add: function(connection) + { + this._connections.push(connection); + }, + swapConnections: function() + { + if(this._connections.length === 0) + { + return null; + } + var connections = this._connections; + this._connections = []; + return connections; + } + }); + + Ice.ConnectionReaper = ConnectionReaper; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ConnectionRequestHandler.js b/js/src/Ice/ConnectionRequestHandler.js new file mode 100644 index 00000000000..8027022d0c8 --- /dev/null +++ b/js/src/Ice/ConnectionRequestHandler.js @@ -0,0 +1,64 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Promise"); + require("Ice/Class"); + require("Ice/ReferenceMode"); + + var Ice = global.Ice || {}; + + var Promise = Ice.Promise; + var ReferenceMode = Ice.ReferenceMode; + + var ConnectionRequestHandler = Ice.Class({ + __init__: function(ref, connection, compress) + { + this._reference = ref; + this._response = ref.getMode() == ReferenceMode.ModeTwoway; + this._connection = connection; + this._compress = compress; + }, + prepareBatchRequest: function(out) + { + this._connection.prepareBatchRequest(out); + }, + finishBatchRequest: function(out) + { + this._connection.finishBatchRequest(out, this._compress); + }, + abortBatchRequest: function() + { + this._connection.abortBatchRequest(); + }, + sendAsyncRequest: function(out) + { + return this._connection.sendAsyncRequest(out, this._compress, this._response); + }, + flushAsyncBatchRequests: function(out) + { + return this._connection.flushAsyncBatchRequests(out); + }, + getReference: function() + { + return this._reference; + }, + getConnection: function() + { + return this._connection; + }, + onConnection: function(r) + { + r.succeed(this._connection, r); + } + }); + + Ice.ConnectionRequestHandler = ConnectionRequestHandler; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Debug.js b/js/src/Ice/Debug.js new file mode 100644 index 00000000000..01ea0a27282 --- /dev/null +++ b/js/src/Ice/Debug.js @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + + var Ice = global.Ice || {}; + + var Debug = { + assert: function(b, msg) + { + if(!b) + { + console.log(msg === undefined ? "assertion failed" : msg); + console.log(Error().stack); + process.exit(1); + } + } + }; + + Ice.Debug = Debug; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/DefaultsAndOverrides.js b/js/src/Ice/DefaultsAndOverrides.js new file mode 100644 index 00000000000..94da3a723c7 --- /dev/null +++ b/js/src/Ice/DefaultsAndOverrides.js @@ -0,0 +1,102 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/FormatType"); + require("Ice/EndpointTypes"); + require("Ice/Protocol"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var FormatType = Ice.FormatType; + var EndpointSelectionType = Ice.EndpointSelectionType; + var Protocol = Ice.Protocol; + + var DefaultsAndOverrides = function(properties) + { + var value; + + this.defaultProtocol = properties.getPropertyWithDefault("Ice.Default.Protocol", + Ice.TcpEndpointFactory !== undefined ? "tcp" : "ws"); + + value = properties.getProperty("Ice.Default.Host"); + this.defaultHost = value.length > 0 ? value : null; + + value = properties.getProperty("Ice.Override.Timeout"); + if(value.length > 0) + { + this.overrideTimeout = true; + this.overrideTimeoutValue = properties.getPropertyAsInt("Ice.Override.Timeout"); + } + else + { + this.overrideTimeout = false; + this.overrideTimeoutValue = -1; + } + + value = properties.getProperty("Ice.Override.ConnectTimeout"); + if(value.length > 0) + { + this.overrideConnectTimeout = true; + this.overrideConnectTimeoutValue = properties.getPropertyAsInt("Ice.Override.ConnectTimeout"); + } + else + { + this.overrideConnectTimeout = false; + this.overrideConnectTimeoutValue = -1; + } + + value = properties.getProperty("Ice.Override.CloseTimeout"); + if(value.length > 0) + { + this.overrideCloseTimeout = true; + this.overrideCloseTimeoutValue = properties.getPropertyAsInt("Ice.Override.CloseTimeout"); + } + else + { + this.overrideCloseTimeout = false; + this.overrideCloseTimeoutValue = -1; + } + + this.overrideCompress = false; + this.overrideSecure = false; + + value = properties.getPropertyWithDefault("Ice.Default.EndpointSelection", "Random"); + if(value === "Random") + { + this.defaultEndpointSelection = EndpointSelectionType.Random; + } + else if(value === "Ordered") + { + this.defaultEndpointSelection = EndpointSelectionType.Ordered; + } + else + { + var ex = new Ice.EndpointSelectionTypeParseException(); + ex.str = "illegal value `" + value + "'; expected `Random' or `Ordered'"; + throw ex; + } + + this.defaultLocatorCacheTimeout = properties.getPropertyAsIntWithDefault("Ice.Default.LocatorCacheTimeout", -1); + + this.defaultPreferSecure = properties.getPropertyAsIntWithDefault("Ice.Default.PreferSecure", 0) > 0; + + value = properties.getPropertyWithDefault("Ice.Default.EncodingVersion", + Ice.encodingVersionToString(Protocol.currentEncoding)); + this.defaultEncoding = Ice.stringToEncodingVersion(value); + Protocol.checkSupportedEncoding(this.defaultEncoding); + + var slicedFormat = properties.getPropertyAsIntWithDefault("Ice.Default.SlicedFormat", 0) > 0; + this.defaultFormat = slicedFormat ? FormatType.SlicedFormat : FormatType.CompactFormat; + }; + + Ice.DefaultsAndOverrides = DefaultsAndOverrides; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/DispatchStatus.js b/js/src/Ice/DispatchStatus.js new file mode 100644 index 00000000000..3a20e36aae4 --- /dev/null +++ b/js/src/Ice/DispatchStatus.js @@ -0,0 +1,15 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + var DispatchStatus = {DispatchOK: 0, DispatchUserException: 1, DispatchAsync: 2}; + Ice.DispatchStatus = DispatchStatus; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/EndpointFactoryManager.js b/js/src/Ice/EndpointFactoryManager.js new file mode 100644 index 00000000000..881c9cd998c --- /dev/null +++ b/js/src/Ice/EndpointFactoryManager.js @@ -0,0 +1,146 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/BasicStream"); + require("Ice/Debug"); + require("Ice/OpaqueEndpointI"); + require("Ice/Protocol"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + // + // Local aliases. + // + var Debug = Ice.Debug; + var BasicStream = Ice.BasicStream; + var EndpointParseException = Ice.EndpointParseException; + var OpaqueEndpointI = Ice.OpaqueEndpointI; + var Protocol = Ice.Protocol; + + var EndpointFactoryManager = Ice.Class({ + __init__: function(instance) + { + this._instance = instance; + this._factories = []; + }, + add: function(factory) + { + for(var i = 0; i < this._factories.length; ++i) + { + Debug.assert(this._factories[i].type() != factory.type()); + } + + this._factories.push(factory); + }, + get: function(type) + { + for(var i = 0; i < this._factories.length; ++i) + { + if(this._factories[i].type() === type) + { + return this._factories[i]; + } + } + return null; + }, + create: function(str, oaEndpoint) + { + var s = str.trim(); + if(s.length === 0) + { + throw new EndpointParseException("value has no non-whitespace characters"); + } + + var protocol; + var rest = ""; + var i, length; + var pos = s.search(/[ \t\n\r]+/); + if(pos === -1) + { + protocol = s; + } + else + { + protocol = s.substring(0, pos); + if(pos < s.length) + { + rest = s.substring(pos); + } + } + + if(protocol === "default") + { + protocol = this._instance.defaultsAndOverrides().defaultProtocol; + } + + for(i = 0, length = this._factories.length; i < length; ++i) + { + if(this._factories[i].protocol() === protocol) + { + return this._factories[i].create(rest, oaEndpoint); + } + } + + // + // If the stringified endpoint is opaque, create an unknown endpoint, + // then see whether the type matches one of the known endpoints. + // + if(protocol === "opaque") + { + var ue = OpaqueEndpointI.fromString(rest); + for(i = 0, length = this._factories.length; i < length; ++i) + { + if(this._factories[i].type() == ue.type()) + { + // + // Make a temporary stream, write the opaque endpoint data into the stream, + // and ask the factory to read the endpoint data from that stream to create + // the actual endpoint. + // + var bs = new BasicStream(this._instance, Protocol.currentProtocolEncoding, true); + ue.streamWrite(bs); + bs.pos = 0; + bs.readShort(); // type + return this._factories[i].read(bs); + } + } + return ue; // Endpoint is opaque, but we don't have a factory for its type. + } + + return null; + }, + read: function(s) + { + var type = s.readShort(); + + for(var i = 0; i < this._factories.length; ++i) + { + if(this._factories[i].type() == type) + { + return this._factories[i].read(s); + } + } + return OpaqueEndpointI.fromStream(type, s); + }, + destroy: function() + { + for(var i = 0; i < this._factories.length; ++i) + { + this._factories[i].destroy(); + } + this._factories = []; + } + }); + + Ice.EndpointFactoryManager = EndpointFactoryManager; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/EnumBase.js b/js/src/Ice/EnumBase.js new file mode 100644 index 00000000000..a387219e81c --- /dev/null +++ b/js/src/Ice/EnumBase.js @@ -0,0 +1,163 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + + var Slice = global.Slice || {}; + var Ice = global.Ice || {}; + // + // Ice.EnumBase + // + var EnumBase = Ice.Class({ + __init__: function(name, value) + { + this._name = name; + this._value = value; + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + + var proto = Object.getPrototypeOf(this); + if(!(rhs instanceof proto.constructor)) + { + return false; + } + + return this._value == rhs._value; + }, + hashCode: function() + { + return this._value; + }, + toString: function() + { + return this._name; + } + }); + + var prototype = EnumBase.prototype; + + Object.defineProperty(prototype, 'name', { + enumerable: true, + get: function() { return this._name; } + }); + + Object.defineProperty(prototype, 'value', { + enumerable: true, + get: function() { return this._value; } + }); + + var EnumHelper = Ice.Class({ + __init__: function(enumType) + { + this._enumType = enumType; + }, + write: function(os, v) + { + this._enumType.__write(os, v); + }, + writeOpt: function(os, tag, v) + { + this._enumType.__writeOpt(os, tag, v); + }, + read: function(is) + { + return this._enumType.__read(is); + }, + readOpt: function(is, tag) + { + return this._enumType.__readOpt(is, tag); + } + }); + + Ice.EnumHelper = EnumHelper; + + var write = function(os, v) + { + os.writeEnum(v); + }; + var writeOpt = function(os, tag, v) + { + os.writeOptValue(tag, Ice.OptionalFormat.Size, Ice.BasicStream.prototype.writeEnum, v); + }; + + Slice.defineEnum = function(enumerators) + { + var type = function(n, v) + { + EnumBase.call(this, n, v); + }; + + type.prototype = new EnumBase(); + type.prototype.constructor = type; + + var enums = []; + var maxValue = 0; + for(var e in enumerators) + { + var value = enumerators[e]; + var enumerator = new type(e, value); + enums[value] = enumerator; + Object.defineProperty(type, e, { + enumerable: true, + value: enumerator + }); + if(value > maxValue) + { + maxValue = value; + } + } + + Object.defineProperty(type, "minWireSize", { + get: function(){ return 1; } + }); + + type.__write = write; + type.__read = function(is) + { + return is.readEnum(type); + }; + type.__writeOpt = writeOpt; + type.__readOpt = function(is, tag) + { + return is.readOptEnum(tag, type); + }; + + type.__helper = new EnumHelper(type); + + Object.defineProperty(type, 'valueOf', { + value: function(v) { + if(v === undefined) + { + return type; + } + return enums[v]; } + }); + + Object.defineProperty(type, 'maxValue', { + value: maxValue + }); + + Object.defineProperty(type.prototype, 'maxValue', { + value: maxValue + }); + + return type; + }; + + Ice.EnumBase = EnumBase; + + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ExUtil.js b/js/src/Ice/ExUtil.js new file mode 100644 index 00000000000..c2fc0f0669d --- /dev/null +++ b/js/src/Ice/ExUtil.js @@ -0,0 +1,53 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + // + // Exception utilities + // + + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + // + // Local aliases. + // + var UnexpectedObjectException = Ice.UnexpectedObjectException; + var MemoryLimitException = Ice.MemoryLimitException; + var ExUtil = {}; + + ExUtil.toString = function(ex) + { + if(!ex.stack) + { + return ex.toString(); + } + else + { + return ex.stack; + } + }; + + ExUtil.throwUOE = function(expectedType, v) + { + var type = v.ice_id(); + throw new UnexpectedObjectException("expected element of type `" + expectedType + "' but received '" + + type, type, expectedType); + }; + + ExUtil.throwMemoryLimitException = function(requested, maximum) + { + throw new MemoryLimitException("requested " + requested + " bytes, maximum allowed is " + maximum + + " bytes (see Ice.MessageSizeMax)"); + }; + + Ice.ExUtil = ExUtil; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Exception.js b/js/src/Ice/Exception.js new file mode 100644 index 00000000000..032ace3339d --- /dev/null +++ b/js/src/Ice/Exception.js @@ -0,0 +1,241 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + + var Slice = global.Slice || {}; + var Ice = global.Ice || {}; + + var Class = Ice.Class; + + // + // Ice.Exception + // + var Exception = Class(Error, { + __init__: function(cause) + { + this.ice_cause = cause; + }, + ice_name: function() + { + return "Ice::Exception"; + }, + toString: function() + { + var s = this.ice_name(); + for(var key in this) + { + if(key == "stack" || key.indexOf("_") === 0) + { + continue; + } + + var value = this[key]; + if(typeof value == "function") + { + continue; + } + + s += "\n " + key + ": \"" + value + "\""; + } + return s; + } + }); + + Exception.captureStackTrace = function(object) + { + var stack = new Error().stack; + + var formattedStack; + + // + // In IE 10 and greater the stack will be filled once the Error is throw + // we don't need to do anything. + // + if(stack !== undefined) + { + + var name = object.ice_name ? object.ice_name().replace("::", ".") : ""; + Object.defineProperty(object, "stack", { + get: function(){ + return stack; + } + }); + } + }; + + Ice.Exception = Exception; + + // + // Ice.LocalException + // + var LocalException = Class(Exception, { + __init__: function(cause) + { + Exception.call(this, cause); + Exception.captureStackTrace(this); + }, + ice_name: function() + { + return "Ice::LocalException"; + } + }); + + Ice.LocalException = LocalException; + + Slice.defineLocalException = function(constructor, base, name) + { + var ex = constructor; + ex.prototype = new base(); + ex.prototype.constructor = ex; + ex.prototype.ice_name = function() + { + return name; + }; + return ex; + }; + + // + // Ice.UserException + // + var UserException = Class(Exception, { + __init__: function(cause) + { + Exception.call(this, cause); + Exception.captureStackTrace(this); + }, + ice_name: function() + { + return "Ice::UserException"; + }, + __write: function(os) + { + os.startWriteException(null); + __writeImpl(this, os, this.__mostDerivedType()); + os.endWriteException(); + }, + __read: function(is) + { + is.startReadException(); + __readImpl(this, is, this.__mostDerivedType()); + is.endReadException(false); + }, + __usesClasses: function() + { + return false; + } + }); + Ice.UserException = UserException; + + // + // Private methods + // + + var __writeImpl = function(obj, os, type) + { + // + // The __writeImpl method is a recursive method that goes down the + // class hierarchy to marshal each slice of the class using the + // generated __writeMemberImpl method. + // + + if(type === undefined || type === UserException) + { + return; // Don't marshal anything for Ice.UserException + } + + os.startWriteSlice(type.__id, -1, type.__parent === UserException); + if(type.prototype.__writeMemberImpl) + { + type.prototype.__writeMemberImpl.call(obj, os); + } + os.endWriteSlice(); + __writeImpl(obj, os, type.__parent); + }; + + var __readImpl = function(obj, is, type) + { + // + // The __readImpl method is a recursive method that goes down the + // class hierarchy to marshal each slice of the class using the + // generated __readMemberImpl method. + // + + if(type === undefined || type === UserException) + { + return; // Don't marshal anything for UserException + } + + is.startReadSlice(); + if(type.prototype.__readMemberImpl) + { + type.prototype.__readMemberImpl.call(obj, is); + } + is.endReadSlice(); + __readImpl(obj, is, type.__parent); + }; + + var __writePreserved = function(os) + { + // + // For Slice exceptions which are marked "preserved", the implementation of this method + // replaces the Ice.Object.prototype.__write method. + // + os.startWriteException(this.__slicedData); + __writeImpl(this, os, this.__mostDerivedType()); + os.endWriteException(); + }; + + var __readPreserved = function(is) + { + // + // For Slice exceptions which are marked "preserved", the implementation of this method + // replaces the Ice.Object.prototype.__read method. + // + is.startReadException(); + __readImpl(this, is, this.__mostDerivedType()); + this.__slicedData = is.endReadException(true); + }; + + Slice.defineUserException = function(constructor, base, name, writeImpl, readImpl, preserved, usesClasses) + { + var ex = constructor; + ex.__parent = base; + ex.prototype = new base(); + ex.__id = "::" + name; + ex.prototype.ice_name = function() + { + return name; + }; + + ex.prototype.constructor = ex; + ex.prototype.__mostDerivedType = function() { return ex; }; + if(preserved) + { + ex.prototype.__write = __writePreserved; + ex.prototype.__read = __readPreserved; + } + ex.prototype.__writeMemberImpl = writeImpl; + ex.prototype.__readMemberImpl = readImpl; + + if(usesClasses) + { + ex.prototype.__usesClasses = function() + { + return true; + }; + } + + return ex; + }; + + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/FormatType.js b/js/src/Ice/FormatType.js new file mode 100644 index 00000000000..0cc2820e780 --- /dev/null +++ b/js/src/Ice/FormatType.js @@ -0,0 +1,18 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/EnumBase"); + + var Ice = global.Ice || {}; + + Ice.FormatType = Slice.defineEnum({'DefaultFormat':0, 'CompactFormat':1, 'SlicedFormat':2}); + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/HashMap.js b/js/src/Ice/HashMap.js new file mode 100644 index 00000000000..aa09448c85b --- /dev/null +++ b/js/src/Ice/HashMap.js @@ -0,0 +1,417 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/StringUtil"); + + var Slice = global.Slice || {}; + var Ice = global.Ice || {}; + + var StringUtil = Ice.StringUtil; + + function setInternal(map, key, value, hash, index) + { + // + // Search for an entry with the same key. + // + for(var e = map._table[index]; e !== null; e = e._nextInBucket) + { + if(e._hash === hash && map.keysEqual(key, e._key)) + { + // + // Found a match, update the value. + // + e._value = value; + return undefined; + } + } + + // + // No match found, add a new entry. + // + map.add(key, value, hash, index); + return undefined; + } + + var HashMap = Ice.Class({ + __init__: function(h) + { + this._size = 0; + this._head = null; + this._initialCapacity = 32; + this._loadFactor = 0.75; + this._table = []; + this._keyComparator = function(k1, k2) { return k1 === k2; }; + this._valueComparator = function(k1, k2) { return k1 === k2; }; + + var i, length; + if(h === undefined || h === null || h._size === 0) + { + this._threshold = this._initialCapacity * this._loadFactor; + for(i = 0; i < this._initialCapacity; i++) + { + this._table[i] = null; + } + } + else + { + this._threshold = h._threshold; + this._keyComparator = h._keyComparator; + this._valueComparator = h._valueComparator; + length = h._table.length; + this._table.length = length; + for(i = 0; i < length; i++) + { + this._table[i] = null; + } + this.merge(h); + } + }, + set: function(key, value) + { + var hash = this.computeHash(key); + + var index = this.hashIndex(hash, this._table.length); + + return setInternal(this, key, value, hash, index); + }, + get: function(key) + { + var e = this.findEntry(key, this.computeHash(key)); + return e !== undefined ? e._value : undefined; + }, + has: function(key) + { + return this.findEntry(key, this.computeHash(key)) !== undefined; + }, + delete: function(key) + { + var hash = this.computeHash(key); + + var index = this.hashIndex(hash, this._table.length); + + // + // Search for an entry with the same key. + // + var prev = null; + for(var e = this._table[index]; e !== null; e = e._nextInBucket) + { + if(e._hash === hash && this.keysEqual(key, e._key)) + { + // + // Found a match. + // + this._size--; + + // + // Remove from bucket. + // + if(prev !== null) + { + prev._nextInBucket = e._nextInBucket; + } + else + { + this._table[index] = e._nextInBucket; + } + + // + // Unlink the entry. + // + if(e._prev !== null) + { + e._prev._next = e._next; + } + if(e._next !== null) + { + e._next._prev = e._prev; + } + + if(this._head === e) + { + this._head = e._next; + } + + return e._value; + } + + prev = e; + } + + return undefined; + }, + clear: function() + { + for(var i = 0; i < this._table.length; ++i) + { + this._table[i] = null; + } + this._head = null; + this._size = 0; + }, + forEach: function(fn, obj) + { + obj = obj === undefined ? fn : obj; + for(var e = this._head; e !== null; e = e._next) + { + fn.call(obj, e._key, e._value); + } + }, + equals: function(other) + { + if(other === null || !(other instanceof HashMap) || this._size !== other._size) + { + return false; + } + + for(var e = this._head; e !== null; e = e._next) + { + var oe = other.findEntry(e._key, e._hash); + if(oe === undefined || !this.valuesEqual(e._value, oe._value)) + { + return false; + } + } + + return true; + }, + clone: function() + { + return new HashMap(this); + }, + merge: function(from) + { + for(var e = from._head; e !== null; e = e._next) + { + setInternal(this, e._key, e._value, e._hash, this.hashIndex(e._hash, this._table.length)); + } + }, + add: function(key, value, hash, index) + { + // + // Create a new table entry. + // + /* + var e = + { + key: key, + value: value, + prev: null, + next: null, + _hash: hash + } + */ + var e = Object.create(null, { + "key": { + enumerable: true, + get: function() { return this._key; } + }, + "value": { + enumerable: true, + get: function() { return this._value; } + }, + "next": { + enumerable: true, + get: function() { return this._next; } + }, + "_key": { + enumerable: false, + writable: true, + value: key + }, + "_value": { + enumerable: false, + writable: true, + value: value + }, + "_prev": { + enumerable: false, + writable: true, + value: null + }, + "_next": { + enumerable: false, + writable: true, + value: null + }, + "_nextInBucket": { + enumerable: false, + writable: true, + value: null + }, + "_hash": { + enumerable: false, + writable: true, + value: hash + } + }); + e._nextInBucket = this._table[index]; + this._table[index] = e; + + e._next = this._head; + if(this._head !== null) + { + this._head._prev = e; + } + this._head = e; + + this._size++; + if(this._size >= this._threshold) + { + this.resize(this._table.length * 2); + } + }, + resize: function(capacity) + { + var oldTable = this._table; + + var newTable = []; + for(var i = 0; i < capacity; i++) + { + newTable[i] = null; + } + + // + // Re-assign all entries to buckets. + // + for(var e = this._head; e !== null; e = e._next) + { + var index = this.hashIndex(e._hash, capacity); + e._nextInBucket = newTable[index]; + newTable[index] = e; + } + + this._table = newTable; + this._threshold = (capacity * this._loadFactor); + }, + findEntry: function(key, hash) + { + var index = this.hashIndex(hash, this._table.length); + + // + // Search for an entry with the same key. + // + for(var e = this._table[index]; e !== null; e = e._nextInBucket) + { + if(e._hash === hash && this.keysEqual(key, e._key)) + { + return e; + } + } + + return undefined; + }, + hashIndex: function(hash, len) + { + return hash & (len - 1); + }, + computeHash: function(v) + { + if(typeof(v.hashCode) === "function") + { + return v.hashCode(); + } + + var hash = 0; + var type = typeof(v); + if(type === "string" || v instanceof String) + { + hash = StringUtil.hashCode(v); + } + else if(type === "number" || v instanceof Number) + { + hash = v.toFixed(0); + } + else if(type === "boolean" || v instanceof Boolean) + { + hash = v ? 1 : 0; + } + else if(v !== null) + { + throw "cannot compute hash for value of type " + type; + } + return hash; + }, + keysEqual: function(k1, k2) + { + return this._keyComparator.call(this._keyComparator, k1, k2); + }, + valuesEqual: function(v1, v2) + { + return this._valueComparator.call(this._valueComparator, v1, v2); + } + }); + + var prototype = HashMap.prototype; + + Object.defineProperty(prototype, "size", { + get: function() { return this._size; } + }); + + Object.defineProperty(prototype, "entries", { + get: function() { return this._head; } + }); + + Object.defineProperty(prototype, "keyComparator", { + get: function() { return this._keyComparator; }, + set: function(fn) { this._keyComparator = fn; } + }); + + Object.defineProperty(prototype, "valueComparator", { + get: function() { return this._valueComparator; }, + set: function(fn) { this._valueComparator = fn; } + }); + + Object.defineProperty(HashMap, "compareEquals", { + get: function() { return function(o1, o2) { return o1.equals(o2); }; } + }); + + Slice.defineDictionary = function(module, name, helperName, keyHelper, valueHelper, fixed, useEquals, valueType) + { + if(useEquals) + { + // + // Define a constructor function for a dictionary whose key type requires + // comparison using an equals() method instead of the native comparison + // operators. + // + module[name] = function(h) + { + var r = new HashMap(h); + r.keyComparator = HashMap.compareEquals; + return r; + }; + } + else + { + module[name] = HashMap; + } + + var helper = null; + Object.defineProperty(module, helperName, + { + get: function() + { + if(helper === null) + { + /*jshint -W061 */ + helper = Ice.StreamHelpers.generateDictHelper(eval(keyHelper), eval(valueHelper), fixed, + eval(valueType), module[name]); + /*jshint +W061 */ + } + return helper; + } + }); + }; + + Ice.HashMap = HashMap; + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/HashUtil.js b/js/src/Ice/HashUtil.js new file mode 100644 index 00000000000..7fe2f3f0c8a --- /dev/null +++ b/js/src/Ice/HashUtil.js @@ -0,0 +1,61 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/StringUtil"); + + var Ice = global.Ice || {}; + + var StringUtil = Ice.StringUtil; + + var HashUtil = {}; + + HashUtil.addBoolean = function(h, b) + { + return ((h << 5) + h) ^ (b ? 0 : 1); + }; + + HashUtil.addString = function(h, str) + { + if(str !== undefined && str !== null) + { + h = ((h << 5) + h) ^ StringUtil.hashCode(str); + } + return h; + }; + + HashUtil.addNumber = function(h, num) + { + return ((h << 5) + h) ^ num; + }; + + HashUtil.addHashable = function(h, obj) + { + if(obj !== undefined && obj !== null) + { + h = ((h << 5) + h) ^ obj.hashCode(); + } + return h; + }; + + HashUtil.addArray = function(h, arr, hashCode) + { + if(arr !== undefined && arr !== null) + { + for(var i = 0; i < arr.length; ++i) + { + h = hashCode(h, arr[i]); + } + } + return h; + }; + + Ice.HashUtil = HashUtil; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Ice.js b/js/src/Ice/Ice.js new file mode 100644 index 00000000000..d7ec2f807bd --- /dev/null +++ b/js/src/Ice/Ice.js @@ -0,0 +1,39 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(){ + require("Ice/TcpEndpointFactory"); // Must be loaded before Communicator and Instance + require("Ice/Initialize"); + require("Ice/Communicator"); + require("Ice/HashMap"); + require("Ice/Object"); + require("Ice/Long"); + require("Ice/Logger"); + require("Ice/ObjectPrx"); + require("Ice/Metrics"); + require("Ice/Properties"); + require("Ice/IdentityUtil"); + require("Ice/ProcessLogger"); + require("Ice/Protocol"); + require("Ice/Identity"); + require("Ice/Exception"); + require("Ice/LocalException"); + require("Ice/BuiltinSequences"); + require("Ice/StreamHelpers"); + require("Ice/Promise"); + require("Ice/EndpointTypes"); + require("Ice/Locator"); + require("Ice/Router"); + require("Ice/Version"); + require("Ice/ObjectFactory"); + require("Ice/Buffer"); + require("Ice/ArrayUtil"); + require("Ice/UnknownSlicedObject"); + require("Ice/Process"); +}()); diff --git a/js/src/Ice/IdentityUtil.js b/js/src/Ice/IdentityUtil.js new file mode 100644 index 00000000000..660e9043eef --- /dev/null +++ b/js/src/Ice/IdentityUtil.js @@ -0,0 +1,228 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/ExUtil"); + require("Ice/StringUtil"); + require("Ice/Identity"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var ExUtil = Ice.ExUtil; + var StringUtil = Ice.StringUtil; + var Identity = Ice.Identity; + var IdentityParseException = Ice.IdentityParseException; + + Ice = global.Ice || {}; + /** + * Converts a string to an object identity. + * + * @param s The string to convert. + * + * @return The converted object identity. + **/ + Ice.stringToIdentity = function(s) + { + var ident = new Identity(); + + // + // Find unescaped separator. + // + var slash = -1; + var pos = 0; + while((pos = s.indexOf('/', pos)) !== -1) + { + if(pos === 0 || s.charAt(pos - 1) != '\\') + { + if(slash == -1) + { + slash = pos; + } + else + { + // + // Extra unescaped slash found. + // + var ex = new IdentityParseException(); + ex.str = "unescaped backslash in identity `" + s + "'"; + throw ex; + } + } + pos++; + } + + if(slash == -1) + { + ident.category = ""; + try + { + ident.name = StringUtil.unescapeString(s); + } + catch(e) + { + var ex = new IdentityParseException(); + ex.str = "invalid identity name `" + s + "': " + ExUtil.toString(e); + throw ex; + } + } + else + { + try + { + ident.category = StringUtil.unescapeString(s, 0, slash); + } + catch(e) + { + var ex = new IdentityParseException(); + ex.str = "invalid category in identity `" + s + "': " + ExUtil.toString(e); + throw ex; + } + if(slash + 1 < s.length) + { + try + { + ident.name = StringUtil.unescapeString(s, slash + 1, s.length); + } + catch(e) + { + var ex = new IdentityParseException(); + ex.str = "invalid name in identity `" + s + "': " + ExUtil.toString(e); + throw ex; + } + } + else + { + ident.name = ""; + } + } + + return ident; + }; + + /** + * Converts an object identity to a string. + * + * @param ident The object identity to convert. + * + * @return The string representation of the object identity. + **/ + Ice.identityToString = function(ident) + { + if(ident.category === null || ident.category.length === 0) + { + return StringUtil.escapeString(ident.name, "/"); + } + else + { + return StringUtil.escapeString(ident.category, "/") + '/' + StringUtil.escapeString(ident.name, "/"); + } + }; + + /** + * Compares the object identities of two proxies. + * + * @param lhs A proxy. + * @param rhs A proxy. + * @return -1 if the identity in <code>lhs</code> compares + * less than the identity in <code>rhs</code>; 0 if the identities + * compare equal; 1, otherwise. + * + * @see ProxyIdentityKey + * @see ProxyIdentityAndFacetKey + * @see ProxyIdentityAndFacetCompare + **/ + Ice.proxyIdentityCompare = function(lhs, rhs) + { + if(lhs === rhs) + { + return 0; + } + else if(lhs === null && rhs !== null) + { + return -1; + } + else if(lhs !== null && rhs === null) + { + return 1; + } + else + { + var lhsIdentity = lhs.ice_getIdentity(); + var rhsIdentity = rhs.ice_getIdentity(); + var n; + if((n = lhsIdentity.name.localeCompare(rhsIdentity.name)) !== 0) + { + return n; + } + return lhsIdentity.category.localeCompare(rhsIdentity.category); + } + }; + + /** + * Compares the object identities and facets of two proxies. + * + * @param lhs A proxy. + * @param rhs A proxy. + * @return -1 if the identity and facet in <code>lhs</code> compare + * less than the identity and facet in <code>rhs</code>; 0 if the identities + * and facets compare equal; 1, otherwise. + * + * @see ProxyIdentityAndFacetKey + * @see ProxyIdentityKey + * @see ProxyIdentityCompare + **/ + Ice.proxyIdentityAndFacetCompare = function(lhs, rhs) + { + if(lhs === rhs) + { + return 0; + } + else if(lhs === null && rhs !== null) + { + return -1; + } + else if(lhs !== null && rhs === null) + { + return 1; + } + else + { + var lhsIdentity = lhs.ice_getIdentity(); + var rhsIdentity = rhs.ice_getIdentity(); + var n; + if((n = lhsIdentity.name.localeCompare(rhsIdentity.name)) !== 0) + { + return n; + } + if((n = lhsIdentity.category.localeCompare(rhsIdentity.category)) !== 0) + { + return n; + } + + var lhsFacet = lhs.ice_getFacet(); + var rhsFacet = rhs.ice_getFacet(); + if(lhsFacet === null && rhsFacet === null) + { + return 0; + } + else if(lhsFacet === null) + { + return -1; + } + else if(rhsFacet === null) + { + return 1; + } + return lhsFacet.localeCompare(rhsFacet); + } + }; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ImplicitContextI.js b/js/src/Ice/ImplicitContextI.js new file mode 100644 index 00000000000..c05f1452e58 --- /dev/null +++ b/js/src/Ice/ImplicitContextI.js @@ -0,0 +1,145 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/HashMap"); + require("Ice/LocalException"); + require("Ice/Current"); + require("Ice/Class"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var InitializationException = Ice.InitializationException; + + // + // The base class for all ImplicitContext implementations + // + var ImplicitContextI = Ice.Class({ + __init__: function() + { + this._context = new HashMap(); + }, + getContext: function() + { + return new HashMap(this._context); + }, + setContext: function(context) + { + if(context !== null && context.size > 0) + { + this._context = new HashMap(context); + } + else + { + this._context.clear(); + } + }, + containsKey: function(key) + { + if(key === null) + { + key = ""; + } + + return this._context.has(key); + }, + get: function(key) + { + if(key === null) + { + key = ""; + } + + var val = this._context.get(key); + if(val === null) + { + val = ""; + } + + return val; + }, + put: function(key, value) + { + if(key === null) + { + key = ""; + } + if(value === null) + { + value = ""; + } + + var oldVal = this._context.get(key); + if(oldVal === null) + { + oldVal = ""; + } + + this._context.set(key, value); + + return oldVal; + }, + remove: function(key) + { + if(key === null) + { + key = ""; + } + + var val = this._context.get(key); + this._context.delete(key); + + if(val === null) + { + val = ""; + } + return val; + }, + write: function(prxContext, os) + { + if(prxContext.size === 0) + { + Ice.ContextHelper.write(os, this._context); + } + else + { + var ctx = null; + if(this._context.size === 0) + { + ctx = prxContext; + } + else + { + ctx = new HashMap(this._context); + ctx.merge(prxContext); + } + Ice.ContextHelper.write(os, ctx); + } + } + }); + + ImplicitContextI.create = function(kind) + { + if(kind.length === 0 || kind === "None") + { + return null; + } + else if(kind === "Shared") + { + return new ImplicitContextI(); + } + else + { + throw new InitializationException("'" + kind + "' is not a valid value for Ice.ImplicitContext"); + } + }; + Ice.ImplicitContextI = ImplicitContextI; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/IncomingAsync.js b/js/src/Ice/IncomingAsync.js new file mode 100644 index 00000000000..54587639ae1 --- /dev/null +++ b/js/src/Ice/IncomingAsync.js @@ -0,0 +1,673 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/BasicStream"); + require("Ice/BuiltinSequences"); + require("Ice/Connection"); + require("Ice/Current"); + require("Ice/Debug"); + require("Ice/DispatchStatus"); + require("Ice/Exception"); + require("Ice/HashMap"); + require("Ice/Identity"); + require("Ice/LocalException"); + require("Ice/Protocol"); + require("Ice/StringUtil"); + + var Ice = global.Ice || {}; + + var BasicStream = Ice.BasicStream; + var Current = Ice.Current; + var Debug = Ice.Debug; + var FormatType = Ice.FormatType; + var HashMap = Ice.HashMap; + var Identity = Ice.Identity; + var Protocol = Ice.Protocol; + var StringUtil = Ice.StringUtil; + + var IncomingAsync = Ice.Class({ + __init__: function(instance, connection, adapter, response, compress, requestId) + { + this._instance = instance; + this._response = response; + this._compress = compress; + if(this._response) + { + this._os = new BasicStream(instance, Protocol.currentProtocolEncoding); + } + this._connection = connection; + + this._current = new Current(); + this._current.id = new Identity(); + this._current.adapter = adapter; + this._current.con = this._connection; + this._current.requestId = requestId; + + this._servant = null; + this._locator = null; + this._cookie = { value: null }; + + // + // Prepare the response if necessary. + // + if(response) + { + this._os.writeBlob(Protocol.replyHdr); + + // + // Add the request ID. + // + this._os.writeInt(requestId); + } + + this._is = null; + + this._cb = null; + this._active = true; + }, + __startWriteParams: function(format) + { + if(this._response) + { + Debug.assert(this._os.size == Protocol.headerSize + 4); // Reply status position. + Debug.assert(this._current.encoding !== null); // Encoding for reply is known. + this._os.writeByte(0); + this._os.startWriteEncaps(this._current.encoding, format); + } + + // + // We still return the stream even if no response is expected. The + // servant code might still write some out parameters if for + // example a method with out parameters somehow and erroneously + // invoked as oneway (or if the invocation is invoked on a + // blobject and the blobject erroneously writes a response). + // + return this._os; + }, + __endWriteParams: function(ok) + { + if(this._response) + { + var save = this._os.pos; + this._os.pos = Protocol.headerSize + 4; // Reply status position. + this._os.writeByte(ok ? Protocol.replyOK : Protocol.replyUserException); + this._os.pos = save; + this._os.endWriteEncaps(); + } + }, + __writeEmptyParams: function() + { + if(this._response) + { + Debug.assert(this._os.size === Protocol.headerSize + 4); // Reply status position. + Debug.assert(this._current.encoding !== null); // Encoding for reply is known. + this._os.writeByte(Protocol.replyOK); + this._os.writeEmptyEncaps(this._current.encoding); + } + }, + __writeParamEncaps: function(v, ok) + { + if(this._response) + { + Debug.assert(this._os.size === Protocol.headerSize + 4); // Reply status position. + Debug.assert(this._current.encoding !== null); // Encoding for reply is known. + this._os.writeByte(ok ? Protocol.replyOK : Protocol.replyUserException); + if(v === null || v.length === 0) + { + this._os.writeEmptyEncaps(this._current.encoding); + } + else + { + this._os.writeEncaps(v); + } + } + }, + __writeUserException: function(ex, format) + { + var os = this.__startWriteParams(format); + os.writeUserException(ex); + this.__endWriteParams(false); + }, + __warning: function(ex) + { + Debug.assert(this._instance !== null); + + var s = []; + s.push("dispatch exception:"); + s.push("\nidentity: " + this._instance.identityToString(this._current.id)); + s.push("\nfacet: " + StringUtil.escapeString(this._current.facet, "")); + s.push("\noperation: " + this._current.operation); + if(this._connection !== null) + { + var connInfo = this._connection.getInfo(); + if(connInfo instanceof Ice.IPConnectionInfo) + { + var ipConnInfo = connInfo; + s.push("\nremote host: " + ipConnInfo.remoteAddress + " remote port: " + ipConnInfo.remotePort); + } + } + if(ex.stack) + { + s.push("\n"); + s.push(ex.stack); + } + this._instance.initializationData().logger.warning(s.join("")); + }, + __servantLocatorFinished: function() + { + Debug.assert(this._locator !== null && this._servant !== null); + try + { + this._locator.finished(this._current, this._servant, this._cookie.value); + return true; + } + catch(ex) + { + if(ex instanceof Ice.UserException) + { + Debug.assert(this._connection !== null); + + // + // The operation may have already marshaled a reply; we must overwrite that reply. + // + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUserException); + this._os.startWriteEncaps(); + this._os.writeUserException(ex); + this._os.endWriteEncaps(); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + + this._connection = null; + } + else + { + this.__handleException(ex); + } + return false; + } + }, + __handleException: function(ex) + { + Debug.assert(this._connection !== null); + + var props = this._instance.initializationData().properties; + var s; + if(ex instanceof Ice.RequestFailedException) + { + if(ex.id === null) + { + ex.id = this._current.id; + } + + if(ex.facet === null) + { + ex.facet = this._current.facet; + } + + if(ex.operation === null || ex.operation.length === 0) + { + ex.operation = this._current.operation; + } + + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 1) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + if(ex instanceof Ice.ObjectNotExistException) + { + this._os.writeByte(Protocol.replyObjectNotExist); + } + else if(ex instanceof Ice.FacetNotExistException) + { + this._os.writeByte(Protocol.replyFacetNotExist); + } + else if(ex instanceof Ice.OperationNotExistException) + { + this._os.writeByte(Protocol.replyOperationNotExist); + } + else + { + Debug.assert(false); + } + ex.id.__write(this._os); + + // + // For compatibility with the old FacetPath. + // + if(ex.facet === null || ex.facet.length === 0) + { + Ice.StringSeqHelper.write(this._os, null); + } + else + { + Ice.StringSeqHelper.write(this._os, [ ex.facet ]); + } + + this._os.writeString(ex.operation); + + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else if(ex instanceof Ice.UnknownLocalException) + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownLocalException); + this._os.writeString(ex.unknown); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else if(ex instanceof Ice.UnknownUserException) + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownUserException); + this._os.writeString(ex.unknown); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else if(ex instanceof Ice.UnknownException) + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownException); + this._os.writeString(ex.unknown); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else if(ex instanceof Ice.LocalException) + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownLocalException); + //this._os.writeString(ex.toString()); + s = [ ex.ice_name() ]; + if(ex.stack) + { + s.push("\n"); + s.push(ex.stack); + } + this._os.writeString(s.join("")); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else if(ex instanceof Ice.UserException) + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownUserException); + //this._os.writeString(ex.toString()); + s = [ ex.ice_name() ]; + if(ex.stack) + { + s.push("\n"); + s.push(ex.stack); + } + this._os.writeString(s.join("")); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + else + { + if(props.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + + if(this._response) + { + this._os.resize(Protocol.headerSize + 4); // Reply status position. + this._os.writeByte(Protocol.replyUnknownException); + //this._os.writeString(ex.toString()); + this._os.writeString(ex.stack ? ex.stack : ""); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + } + + this._connection = null; + }, + invoke: function(servantManager, stream) + { + this._is = stream; + + var start = this._is.pos; + + // + // Read the current. + // + this._current.id.__read(this._is); + + // + // For compatibility with the old FacetPath. + // + var facetPath = Ice.StringSeqHelper.read(this._is); + if(facetPath.length > 0) + { + if(facetPath.length > 1) + { + throw new Ice.MarshalException(); + } + this._current.facet = facetPath[0]; + } + else + { + this._current.facet = ""; + } + + this._current.operation = this._is.readString(); + this._current.mode = Ice.OperationMode.valueOf(this._is.readByte()); + this._current.ctx = new HashMap(); + var sz = this._is.readSize(); + while(sz-- > 0) + { + var first = this._is.readString(); + var second = this._is.readString(); + this._current.ctx.set(first, second); + } + + // + // Don't put the code above into the try block below. Exceptions + // in the code above are considered fatal, and must propagate to + // the caller of this operation. + // + + if(servantManager !== null) + { + this._servant = servantManager.findServant(this._current.id, this._current.facet); + if(this._servant === null) + { + this._locator = servantManager.findServantLocator(this._current.id.category); + if(this._locator === null && this._current.id.category.length > 0) + { + this._locator = servantManager.findServantLocator(""); + } + + if(this._locator !== null) + { + try + { + this._servant = this._locator.locate(this._current, this._cookie); + } + catch(ex) + { + if(ex instanceof Ice.UserException) + { + var encoding = this._is.skipEncaps(); // Required for batch requests. + + if(this._response) + { + this._os.writeByte(Protocol.replyUserException); + this._os.startWriteEncaps(encoding, FormatType.DefaultFormat); + this._os.writeUserException(ex); + this._os.endWriteEncaps(); + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + + this._connection = null; + return; + } + else + { + this._is.skipEncaps(); // Required for batch requests. + this.__handleException(ex); + return; + } + } + } + } + } + + try + { + if(this._servant !== null) + { + // + // DispatchAsync is a "pseudo dispatch status", used internally only + // to indicate async dispatch. + // + if(this._servant.__dispatch(this, this._current) === Ice.DispatchStatus.DispatchAsync) + { + // + // If this was an asynchronous dispatch, we're done here. + // + return; + } + + if(this._locator !== null && !this.__servantLocatorFinished()) + { + return; + } + } + else + { + // + // Skip the input parameters, this is required for reading + // the next batch request if dispatching batch requests. + // + this._is.skipEncaps(); + + if(servantManager !== null && servantManager.hasServant(this._current.id)) + { + throw new Ice.FacetNotExistException(this._current.id, this._current.facet, + this._current.operation); + } + else + { + throw new Ice.ObjectNotExistException(this._current.id, this._current.facet, + this._current.operation); + } + } + } + catch(ex) + { + if(this._servant !== null && this._locator !== null && !this.__servantLocatorFinished()) + { + return; + } + this.__handleException(ex); + return; + } + + // + // Don't put the code below into the try block above. Exceptions + // in the code below are considered fatal, and must propagate to + // the caller of this operation. + // + + Debug.assert(this._connection !== null); + + if(this._response) + { + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + + this._connection = null; + }, + startReadParams: function() + { + // + // Remember the encoding used by the input parameters, we'll + // encode the response parameters with the same encoding. + // + this._current.encoding = this._is.startReadEncaps(); + return this._is; + }, + endReadParams: function() + { + this._is.endReadEncaps(); + }, + readEmptyParams: function() + { + this._current.encoding = new Ice.EncodingVersion(); + this._is.skipEmptyEncaps(this._current.encoding); + }, + readParamEncaps: function() + { + this._current.encoding = new Ice.EncodingVersion(); + return this._is.readEncaps(this._current.encoding); + }, + __response: function() + { + try + { + if(this._locator !== null && !this.__servantLocatorFinished()) + { + return; + } + + Debug.assert(this._connection !== null); + + if(this._response) + { + this._connection.sendResponse(this._os, this._compress); + } + else + { + this._connection.sendNoResponse(); + } + + this._connection = null; + } + catch(ex) + { + this._connection.invokeException(ex, 1); + } + }, + __exception: function(exc) + { + try + { + if(this._locator !== null && !this.__servantLocatorFinished()) + { + return; + } + + this.__handleException(exc); + } + catch(ex) + { + this._connection.invokeException(ex, 1); + } + }, + __validateResponse: function(ok) + { + if(!this._active) + { + return false; + } + this._active = false; + return true; + }, + ice_exception: function(ex) + { + if(!this._active) + { + return; + } + this._active = false; + + if(this._connection !== null) + { + this.__exception(ex); + } + else + { + // + // Response has already been sent. + // + if(this._instance.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + this.__warning(ex); + } + } + } + }); + + Ice.IncomingAsync = IncomingAsync; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Initialize.js b/js/src/Ice/Initialize.js new file mode 100644 index 00000000000..e1dd0c4a2b1 --- /dev/null +++ b/js/src/Ice/Initialize.js @@ -0,0 +1,104 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Protocol"); + require("Ice/LocalException"); + require("Ice/Communicator"); + require("Ice/Properties"); + + var Ice = global.Ice || {}; + var Protocol = Ice.Protocol; + + // + // Ice.InitializationData + // + Ice.InitializationData = function() + { + this.properties = null; + this.logger = null; + }; + + Ice.InitializationData.prototype.clone = function() + { + var r = new Ice.InitializationData(); + r.properties = this.properties; + r.logger = this.logger; + return r; + }; + + // + // Ice.initialize() + // + Ice.initialize = function(arg1, arg2) + { + var args = null; + var initData = null; + + if(arg1 instanceof Array) + { + args = arg1; + } + else if(arg1 instanceof Ice.InitializationData) + { + initData = arg1; + } + else if(arg1 !== undefined && arg1 !== null) + { + throw new Ice.InitializationException("invalid argument to initialize"); + } + + if(arg2 !== undefined && arg2 !== null) + { + if(arg2 instanceof Ice.InitializationData && initData === null) + { + initData = arg2; + } + else + { + throw new Ice.InitializationException("invalid argument to initialize"); + } + + } + + if(initData === null) + { + initData = new Ice.InitializationData(); + } + else + { + initData = initData.clone(); + } + initData.properties = Ice.createProperties(args, initData.properties); + + var result = new Ice.Communicator(initData); + result.finishSetup(null); + return result; + }; + + // + // Ice.createProperties() + // + Ice.createProperties = function(args, defaults) + { + return new Ice.Properties(args, defaults); + }; + + Ice.currentProtocol = function() + { + return Protocol.currentProtocol.clone(); + }; + + Ice.currentEncoding = function() + { + return Protocol.currentEncoding.clone(); + }; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Instance.js b/js/src/Ice/Instance.js new file mode 100644 index 00000000000..92ba88894d7 --- /dev/null +++ b/js/src/Ice/Instance.js @@ -0,0 +1,573 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResultBase"); + require("Ice/ConnectionMonitor"); + require("Ice/Debug"); + require("Ice/DefaultsAndOverrides"); + require("Ice/EndpointFactoryManager"); + require("Ice/HashMap"); + require("Ice/ImplicitContextI"); + require("Ice/IdentityUtil"); + require("Ice/LocatorManager"); + require("Ice/Logger"); + require("Ice/ObjectAdapterFactory"); + require("Ice/ObjectFactoryManager"); + require("Ice/OutgoingConnectionFactory"); + require("Ice/Promise"); + require("Ice/Properties"); + require("Ice/ProxyFactory"); + require("Ice/RetryQueue"); + require("Ice/RouterManager"); + require("Ice/Timer"); + require("Ice/TraceLevels"); + require("Ice/Reference"); + require("Ice/LocalException"); + require("Ice/Exception"); + require("Ice/ProcessLogger"); + + // + // We don't load the endpoint factories here, instead the Ice.js + // file for NodeJS loads the TcpEndpointFactory and the Ice.js + // file for the web browser includes the IceWS endpoint factory. + // + //require("Ice/TcpEndpointFactory"); + //require("IceWS/EndpointFactory"); + + var Ice = global.Ice || {}; + + var AsyncResultBase = Ice.AsyncResultBase; + var ConnectionMonitor = Ice.ConnectionMonitor; + var Debug = Ice.Debug; + var DefaultsAndOverrides = Ice.DefaultsAndOverrides; + var EndpointFactoryManager = Ice.EndpointFactoryManager; + var HashMap = Ice.HashMap; + var ImplicitContextI = Ice.ImplicitContextI; + var LocatorManager = Ice.LocatorManager; + var Logger = Ice.Logger; + var ObjectAdapterFactory = Ice.ObjectAdapterFactory; + var ObjectFactoryManager = Ice.ObjectFactoryManager; + var OutgoingConnectionFactory = Ice.OutgoingConnectionFactory; + var Promise = Ice.Promise; + var Properties = Ice.Properties; + var ProxyFactory = Ice.ProxyFactory; + var RetryQueue = Ice.RetryQueue; + var RouterManager = Ice.RouterManager; + var Timer = Ice.Timer; + var TraceLevels = Ice.TraceLevels; + var ReferenceFactory = Ice.ReferenceFactory; + + var StateActive = 0; + var StateDestroyInProgress = 1; + var StateDestroyed = 2; + + // + // Instance - only for use by Communicator + // + var Instance = Ice.Class({ + __init__: function(initData) + { + this._state = StateActive; + this._initData = initData; + + this._traceLevels = null; + this._defaultsAndOverrides = null; + this._messageSizeMax = null; + this._clientACM = null; + this._serverACM = null; + this._implicitContext = null; + this._routerManager = null; + this._locatorManager = null; + this._referenceFactory = null; + this._proxyFactory = null; + this._outgoingConnectionFactory = null; + this._connectionMonitor = null; + this._servantFactoryManager = null; + this._objectAdapterFactory = null; + this._protocolSupport = null; + this._retryQueue = null; + this._endpointHostResolver = null; + this._endpointFactoryManager = null; + }, + initializationData: function() + { + // + // No check for destruction. It must be possible to access the + // initialization data after destruction. + // + // This value is immutable. + // + return this._initData; + }, + traceLevels: function() + { + // This value is immutable. + Debug.assert(this._traceLevels !== null); + return this._traceLevels; + }, + defaultsAndOverrides: function() + { + // This value is immutable. + Debug.assert(this._defaultsAndOverrides !== null); + return this._defaultsAndOverrides; + }, + routerManager: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._routerManager !== null); + return this._routerManager; + }, + locatorManager: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._locatorManager !== null); + return this._locatorManager; + }, + referenceFactory: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._referenceFactory !== null); + return this._referenceFactory; + }, + proxyFactory: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._proxyFactory !== null); + return this._proxyFactory; + }, + outgoingConnectionFactory: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._outgoingConnectionFactory !== null); + return this._outgoingConnectionFactory; + }, + preferIPv6: function() + { + return this._preferIPv6; + }, + connectionMonitor: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._connectionMonitor !== null); + return this._connectionMonitor; + }, + servantFactoryManager: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._servantFactoryManager !== null); + return this._servantFactoryManager; + }, + objectAdapterFactory: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._objectAdapterFactory !== null); + return this._objectAdapterFactory; + }, + protocolSupport: function() + { + if(this._state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + return this._protocolSupport; + }, + retryQueue: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._retryQueue !== null); + return this._retryQueue; + }, + timer: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._timer !== null); + return this._timer; + }, + endpointFactoryManager: function() + { + if(this._state === StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.assert(this._endpointFactoryManager !== null); + return this._endpointFactoryManager; + }, + messageSizeMax: function() + { + // This value is immutable. + return this._messageSizeMax; + }, + clientACM: function() + { + // This value is immutable. + return this._clientACM; + }, + serverACM: function() + { + // This value is immutable. + return this._serverACM; + }, + getImplicitContext: function() + { + return this._implicitContext; + }, + stringToIdentity: function(s) + { + return Ice.stringToIdentity(s); + }, + identityToString: function(ident) + { + return Ice.identityToString(ident); + }, + + setDefaultLocator: function(locator) + { + if(this._state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + this._referenceFactory = this._referenceFactory.setDefaultLocator(locator); + }, + setDefaultRouter: function(router) + { + if(this._state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + this._referenceFactory = this._referenceFactory.setDefaultRouter(router); + }, + setLogger: function(logger) + { + this._initData.logger = logger; + }, + finishSetup: function(communicator, promise) + { + // + // If promise == null, it means the caller is requesting a synchronous setup. + // Otherwise, we resolve the promise after all initialization is complete. + // + + try + { + this._timer = new Timer(); + + if(this._initData.properties === null) + { + this._initData.properties = Properties.createProperties(); + } + + if(this._initData.logger === null) + { + this._initData.logger = Ice.getProcessLogger(); + } + + this._traceLevels = new TraceLevels(this._initData.properties); + + this._defaultsAndOverrides = new DefaultsAndOverrides(this._initData.properties); + + var defMessageSizeMax = 1024; + var num = this._initData.properties.getPropertyAsIntWithDefault("Ice.MessageSizeMax", defMessageSizeMax); + if(num < 1) + { + this._messageSizeMax = defMessageSizeMax * 1024; // Ignore non-sensical values. + } + else if(num > 0x7fffffff / 1024) + { + this._messageSizeMax = 0x7fffffff; + } + else + { + this._messageSizeMax = num * 1024; // Property is in kilobytes, _messageSizeMax in bytes + } + + // + // Client ACM enabled by default. Server ACM disabled by default. + // + this._clientACM = this._initData.properties.getPropertyAsIntWithDefault("Ice.ACM.Client", 60); + this._serverACM = this._initData.properties.getPropertyAsInt("Ice.ACM.Server"); + + this._implicitContext = ImplicitContextI.create(this._initData.properties.getProperty("Ice.ImplicitContext")); + + this._routerManager = new RouterManager(); + + this._locatorManager = new LocatorManager(this._initData.properties); + + this._referenceFactory = new ReferenceFactory(this, communicator); + + this._proxyFactory = new ProxyFactory(this); + + this._endpointFactoryManager = new EndpointFactoryManager(this); + + if(typeof(Ice.TcpEndpointFactory) !== "undefined") + { + this._endpointFactoryManager.add(new Ice.TcpEndpointFactory(this)); + } + + if(typeof(IceWS) !== "undefined" && typeof(IceWS.EndpointFactory) !== "undefined") + { + this._endpointFactoryManager.add(new IceWS.EndpointFactory(this, false)); + this._endpointFactoryManager.add(new IceWS.EndpointFactory(this, true)); + } + + this._outgoingConnectionFactory = new OutgoingConnectionFactory(communicator, this); + this._servantFactoryManager = new ObjectFactoryManager(); + + this._objectAdapterFactory = new ObjectAdapterFactory(this, communicator); + + this._retryQueue = new RetryQueue(this); + + // + // Get default router and locator proxies. Don't move this + // initialization before the plug-in initialization!!! The proxies + // might depend on endpoint factories to be installed by plug-ins. + // + var router = Ice.RouterPrx.uncheckedCast(this._proxyFactory.propertyToProxy("Ice.Default.Router")); + if(router !== null) + { + this._referenceFactory = this._referenceFactory.setDefaultRouter(router); + } + + var loc = Ice.LocatorPrx.uncheckedCast(this._proxyFactory.propertyToProxy("Ice.Default.Locator")); + if(loc !== null) + { + this._referenceFactory = this._referenceFactory.setDefaultLocator(loc); + } + + // + // Create the connection monitor and ensure the interval for + // monitoring connections is appropriate for client & server + // ACM. + // + var interval = this._initData.properties.getPropertyAsInt("Ice.MonitorConnections"); + this._connectionMonitor = new ConnectionMonitor(this, interval); + this._connectionMonitor.checkIntervalForACM(this._clientACM); + this._connectionMonitor.checkIntervalForACM(this._serverACM); + + if(promise !== null) + { + promise.succeed(communicator); + } + } + catch(ex) + { + if(promise !== null) + { + if(ex instanceof Ice.LocalException) + { + this.destroy().finally(function() + { + promise.fail(ex); + }); + } + else + { + promise.fail(ex); + } + } + else + { + if(ex instanceof Ice.LocalException) + { + this.destroy(); + } + throw ex; + } + } + }, + // + // Only for use by Ice.CommunicatorI + // + destroy: function() + { + var promise = new AsyncResultBase(null, "destroy", null, this, null); + + // + // If the _state is not StateActive then the instance is + // either being destroyed, or has already been destroyed. + // + if(this._state != StateActive) + { + promise.succeed(promise); + return promise; + } + + // + // We cannot set state to StateDestroyed otherwise instance + // methods called during the destroy process (such as + // outgoingConnectionFactory() from + // ObjectAdapterI::deactivate() will cause an exception. + // + this._state = StateDestroyInProgress; + + var self = this; + Ice.Promise.try( + function() + { + if(self._objectAdapterFactory) + { + return self._objectAdapterFactory.shutdown(); + } + } + ).then( + function() + { + if(self._outgoingConnectionFactory !== null) + { + self._outgoingConnectionFactory.destroy(); + } + + if(self._objectAdapterFactory !== null) + { + return self._objectAdapterFactory.destroy(); + } + } + ).then( + function() + { + if(self._outgoingConnectionFactory !== null) + { + return self._outgoingConnectionFactory.waitUntilFinished(); + } + } + ).then( + function() + { + if(self._retryQueue) + { + self._retryQueue.destroy(); + } + + self._objectAdapterFactory = null; + self._outgoingConnectionFactory = null; + self._retryQueue = null; + + if(self._connectionMonitor) + { + self._connectionMonitor.destroy(); + self._connectionMonitor = null; + } + + if(self._timer) + { + self._timer.destroy(); + self._timer = null; + } + + if(self._servantFactoryManager) + { + self._servantFactoryManager.destroy(); + self._servantFactoryManager = null; + } + + if(self._referenceFactory) + { + //self._referenceFactory.destroy(); // No destroy function defined. + self._referenceFactory = null; + } + + // self._proxyFactory.destroy(); // No destroy function defined. + self._proxyFactory = null; + + if(self._routerManager) + { + self._routerManager.destroy(); + self._routerManager = null; + } + + if(self._locatorManager) + { + self._locatorManager.destroy(); + self._locatorManager = null; + } + + if(self._endpointFactoryManager) + { + self._endpointFactoryManager.destroy(); + self._endpointFactoryManager = null; + } + + self._state = StateDestroyed; + + if(self._initData.properties.getPropertyAsInt("Ice.Warn.UnusedProperties") > 0) + { + var unusedProperties = self._initData.properties.getUnusedProperties(); + if(unusedProperties.length > 0) + { + var message = []; + message.push("The following properties were set but never read:"); + for(var i = 0; i < unusedProperties.length; ++i) + { + message.push("\n "); + message.push(unusedProperties[i]); + } + self._initData.logger.warning(message.join("")); + } + } + + promise.succeed(promise); + } + ).exception( + function(ex) + { + promise.fail(ex); + } + ); + return promise; + }, + }); + + Ice.Instance = Instance; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/LocalExceptionWrapper.js b/js/src/Ice/LocalExceptionWrapper.js new file mode 100644 index 00000000000..993858b8e7c --- /dev/null +++ b/js/src/Ice/LocalExceptionWrapper.js @@ -0,0 +1,83 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Exception"); + require("Ice/ExUtil"); + require("Ice/Debug"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var ExUtil = Ice.ExUtil; + var Debug = Ice.Debug; + + var LocalExceptionWrapper = Ice.Class(Error, { + __init__: function(ex, retry) + { + retry = retry === undefined ? false : retry; + + if(ex instanceof Ice.LocalException) + { + this._ex = ex; + this._retry = retry; + } + else + { + Debug.assert(ex instanceof LocalExceptionWrapper); + this._ex = ex._ex; + this._retry = ex._retry; + } + } + }); + + LocalExceptionWrapper.throwWrapper = function(ex) + { + if(ex instanceof Ice.UserException) + { + throw new LocalExceptionWrapper(new Ice.UnknownUserException(ex.ice_name()), false); + } + else if(ex instanceof Ice.LocalException) + { + if(ex instanceof Ice.UnknownException || + ex instanceof Ice.ObjectNotExistException || + ex instanceof Ice.OperationNotExistException || + ex instanceof Ice.FacetNotExistException) + { + throw new LocalExceptionWrapper(ex, false); + } + var e = new Ice.UnknownLocalException(ex.ice_name(), ex); + throw new LocalExceptionWrapper(e, false); + } + + var ue = new Ice.UnknownException(ExUtil.toString(ex), ex); + throw new LocalExceptionWrapper(ue, false); + }; + + var prototype = LocalExceptionWrapper.prototype; + + Object.defineProperty(prototype, "inner", { + get: function() { return this._ex; } + }); + + // + // If true, always repeat the request. Don't take retry settings + // or "at-most-once" guarantees into account. + // + // If false, only repeat the request if the retry settings allow + // to do so, and if "at-most-once" does not need to be guaranteed. + // + Object.defineProperty(prototype, "retry", { + get: function() { return this._retry; } + }); + + Ice.LocalExceptionWrapper = LocalExceptionWrapper; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/LocatorInfo.js b/js/src/Ice/LocatorInfo.js new file mode 100644 index 00000000000..9b255978463 --- /dev/null +++ b/js/src/Ice/LocatorInfo.js @@ -0,0 +1,608 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/HashMap"); + require("Ice/Promise"); + require("Ice/Protocol"); + require("Ice/Locator"); + require("Ice/LocalException"); + require("Ice/Exception"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var HashMap = Ice.HashMap; + var Promise = Ice.Promise; + var Protocol = Ice.Protocol; + var LocatorRegistryPrx = Ice.LocatorRegisterPrx; + + var Class = Ice.Class; + + var LocatorInfo = Class({ + __init__: function(locator, table, background) + { + this._locator = locator; + this._locatorRegistry = null; + this._table = table; + this._background = background; + + this._adapterRequests = new HashMap(); // Map<String, Request> + this._objectRequests = new HashMap(); // Map<Ice.Identity, Request> + this._objectRequests.keyComparator = HashMap.compareEquals; + }, + destroy: function() + { + this._locatorRegistry = null; + this._table.clear(); + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + + if(rhs instanceof LocatorInfo) + { + return this._locator.equals(rhs._locator); + } + + return false; + }, + hashCode: function() + { + return this._locator.hashCode(); + }, + getLocator: function() + { + return this._locator; + }, + getLocatorRegistry: function() + { + if(this._locatorRegistry !== null) + { + return new Promise().succeed(this._locatorRegistry); + } + + var self = this; + return this._locator.getRegistry().then( + function(reg) + { + // + // The locator registry can't be located. + // + self._locatorRegistry = LocatorRegistryPrx.uncheckedCast(reg.ice_locator(null)); + return self._locatorRegistry; + }); + }, + getEndpoints: function(ref, wellKnownRef, ttl, p) + { + var promise = p || new Promise(); // success callback receives (endpoints, cached) + + Debug.assert(ref.isIndirect()); + var endpoints = null; + var cached = { value: false }; + if(!ref.isWellKnown()) + { + endpoints = this._table.getAdapterEndpoints(ref.getAdapterId(), ttl, cached); + if(!cached.value) + { + if(this._background && endpoints !== null) + { + this.getAdapterRequest(ref).addCallback(ref, wellKnownRef, ttl, null); + } + else + { + this.getAdapterRequest(ref).addCallback(ref, wellKnownRef, ttl, promise); + return promise; + } + } + } + else + { + var r = this._table.getObjectReference(ref.getIdentity(), ttl, cached); + if(!cached.value) + { + if(this._background && r !== null) + { + this.getObjectRequest(ref).addCallback(ref, null, ttl, null); + } + else + { + this.getObjectRequest(ref).addCallback(ref, null, ttl, promise); + return promise; + } + } + + if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(!r.isWellKnown()) + { + this.getEndpoints(r, ref, ttl, promise); + return promise; + } + } + + Debug.assert(endpoints !== null); + if(ref.getInstance().traceLevels().location >= 1) + { + this.getEndpointsTrace(ref, endpoints, true); + } + promise.succeed(endpoints, true); + + return promise; + }, + clearCache: function(ref) + { + Debug.assert(ref.isIndirect()); + + if(!ref.isWellKnown()) + { + var endpoints = this._table.removeAdapterEndpoints(ref.getAdapterId()); + + if(endpoints !== null && ref.getInstance().traceLevels().location >= 2) + { + this.trace("removed endpoints from locator table\n", ref, endpoints); + } + } + else + { + var r = this._table.removeObjectReference(ref.getIdentity()); + if(r !== null) + { + if(!r.isIndirect()) + { + if(ref.getInstance().traceLevels().location >= 2) + { + this.trace("removed endpoints from locator table", ref, r.getEndpoints()); + } + } + else if(!r.isWellKnown()) + { + this.clearCache(r); + } + } + } + }, + trace: function(msg, ref, endpoints) + { + Debug.assert(ref.isIndirect()); + + var s = []; + s.push(msg); + s.push("\n"); + if(!ref.isWellKnown()) + { + s.push("adapter = "); + s.push(ref.getAdapterId()); + s.push("\n"); + } + else + { + s.push("object = "); + s.push(ref.getInstance().identityToString(ref.getIdentity())); + s.push("\n"); + } + + s.push("endpoints = "); + for(var i = 0; i < endpoints.length; i++) + { + s.push(endpoints[i].toString()); + if(i + 1 < endpoints.length) + { + s.push(":"); + } + } + + ref.getInstance().initializationData().logger.trace(ref.getInstance().traceLevels().locationCat, s.join("")); + }, + getEndpointsException: function(ref, exc) + { + Debug.assert(ref.isIndirect()); + + var instance = ref.getInstance(); + var s, e; + try + { + throw exc; + } + catch(ex) + { + if(ex instanceof Ice.AdapterNotFoundException) + { + if(instance.traceLevels().location >= 1) + { + s = []; + s.push("adapter not found\n"); + s.push("adapter = "); + s.push(ref.getAdapterId()); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + + e = new Ice.NotRegisteredException(); + e.kindOfObject = "object adapter"; + e.id = ref.getAdapterId(); + throw e; + } + else if(ex instanceof Ice.ObjectNotFoundException) + { + if(instance.traceLevels().location >= 1) + { + s = []; + s.push("object not found\n"); + s.push("object = "); + s.push(instance.identityToString(ref.getIdentity())); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + + e = new Ice.NotRegisteredException(); + e.kindOfObject = "object"; + e.id = instance.identityToString(ref.getIdentity()); + throw e; + } + else if(ex instanceof Ice.NotRegisteredException) + { + throw ex; + } + else if(ex instanceof Ice.LocalException) + { + if(instance.traceLevels().location >= 1) + { + s = []; + s.push("couldn't contact the locator to retrieve adapter endpoints\n"); + if(ref.getAdapterId().length > 0) + { + s.push("adapter = "); + s.push(ref.getAdapterId()); + s.push("\n"); + } + else + { + s.push("object = "); + s.push(instance.identityToString(ref.getIdentity())); + s.push("\n"); + } + s.push("reason = " + ExUtil.toString(ex)); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + throw ex; + } + else + { + Debug.assert(false); + } + } + }, + getEndpointsTrace: function(ref, endpoints, cached) + { + if(endpoints !== null && endpoints.length > 0) + { + if(cached) + { + this.trace("found endpoints in locator table", ref, endpoints); + } + else + { + this.trace("retrieved endpoints from locator, adding to locator table", ref, endpoints); + } + } + else + { + var instance = ref.getInstance(); + var s = []; + s.push("no endpoints configured for "); + if(ref.getAdapterId().length > 0) + { + s.push("adapter\n"); + s.push("adapter = "); + s.push(ref.getAdapterId()); + s.push("\n"); + } + else + { + s.push("object\n"); + s.push("object = "); + s.push(instance.identityToString(ref.getIdentity())); + s.push("\n"); + } + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + }, + getAdapterRequest: function(ref) + { + if(ref.getInstance().traceLevels().location >= 1) + { + var instance = ref.getInstance(); + var s = []; + s.push("searching for adapter by id\n"); + s.push("adapter = "); + s.push(ref.getAdapterId()); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + + var request = this._adapterRequests.get(ref.getAdapterId()); + if(request !== undefined) + { + return request; + } + request = new AdapterRequest(this, ref); + this._adapterRequests.set(ref.getAdapterId(), request); + return request; + }, + getObjectRequest: function(ref) + { + if(ref.getInstance().traceLevels().location >= 1) + { + var instance = ref.getInstance(); + var s = []; + s.push("searching for object by id\n"); + s.push("object = "); + s.push(instance.identityToString(ref.getIdentity())); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.join("")); + } + + var request = this._objectRequests.get(ref.getIdentity()); + if(request !== undefined) + { + return request; + } + request = new ObjectRequest(this, ref); + this._objectRequests.set(ref.getIdentity(), request); + return request; + }, + finishRequest: function(ref, wellKnownRefs, proxy, notRegistered) + { + if(proxy === null || proxy.__reference().isIndirect()) + { + // + // Remove the cached references of well-known objects for which we tried + // to resolved the endpoints if these endpoints are empty. + // + for(var i = 0; i < wellKnownRefs.length; ++i) + { + this._table.removeObjectReference(wellKnownRefs[i].getIdentity()); + } + } + + if(!ref.isWellKnown()) + { + if(proxy !== null && !proxy.__reference().isIndirect()) + { + // Cache the adapter endpoints. + this._table.addAdapterEndpoints(ref.getAdapterId(), proxy.__reference().getEndpoints()); + } + else if(notRegistered) // If the adapter isn't registered anymore, remove it from the cache. + { + this._table.removeAdapterEndpoints(ref.getAdapterId()); + } + + Debug.assert(this._adapterRequests.has(ref.getAdapterId())); + this._adapterRequests.delete(ref.getAdapterId()); + } + else + { + if(proxy !== null && !proxy.__reference().isWellKnown()) + { + // Cache the well-known object reference. + this._table.addObjectReference(ref.getIdentity(), proxy.__reference()); + } + else if(notRegistered) // If the well-known object isn't registered anymore, remove it from the cache. + { + this._table.removeObjectReference(ref.getIdentity()); + } + + Debug.assert(this._objectRequests.has(ref.getIdentity())); + this._objectRequests.delete(ref.getIdentity()); + } + } + }); + + Ice.LocatorInfo = LocatorInfo; + global.Ice = Ice; + + var RequestCallback = Class({ + __init__: function(ref, ttl, promise) + { + this._ref = ref; + this._ttl = ttl; + this._promise = promise; + }, + response: function(locatorInfo, proxy) + { + var endpoints = null; + if(proxy !== null) + { + var r = proxy.__reference(); + if(this._ref.isWellKnown() && !Protocol.isSupported(this._ref.getEncoding(), r.getEncoding())) + { + // + // If a well-known proxy and the returned proxy + // encoding isn't supported, we're done: there's + // no compatible endpoint we can use. + // + } + else if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(this._ref.isWellKnown() && !r.isWellKnown()) + { + // + // We're resolving the endpoints of a well-known object and the proxy returned + // by the locator is an indirect proxy. We now need to resolve the endpoints + // of this indirect proxy. + // + var self = this; + locatorInfo.getEndpoints(r, this._ref, this._ttl).then( + function(endpts, b) + { + if(self._promise !== null) + { + self._promise.succeed(endpts, b); + } + }, + function(ex) + { + if(self._promise !== null) + { + self._promise.fail(ex); + } + }); + return; + } + } + + if(this._ref.getInstance().traceLevels().location >= 1) + { + locatorInfo.getEndpointsTrace(this._ref, endpoints, false); + } + + if(this._promise !== null) + { + this._promise.succeed(endpoints === null ? [] : endpoints, false); + } + }, + exception: function(locatorInfo, exc) + { + try + { + locatorInfo.getEndpointsException(this._ref, exc); // This throws. + } + catch(ex) + { + if(this._promise !== null) + { + this._promise.fail(ex); + } + } + } + }); + + var Request = Class({ + __init__: function(locatorInfo, ref) + { + this._locatorInfo = locatorInfo; + this._ref = ref; + + this._callbacks = []; // Array<RequestCallback> + this._wellKnownRefs = []; // Array<Reference> + this._sent = false; + this._response = false; + this._proxy = null; + this._exception = null; + }, + addCallback: function(ref, wellKnownRef, ttl, promise) + { + var callback = new RequestCallback(ref, ttl, promise); + if(this._response) + { + callback.response(this._locatorInfo, this._proxy); + } + else if(this._exception !== null) + { + callback.exception(this._locatorInfo, this._exception); + } + else + { + this._callbacks.push(callback); + if(wellKnownRef !== null) // This request is to resolve the endpoints of a cached well-known object ref + { + this._wellKnownRefs.push(wellKnownRef); + } + if(!this._sent) + { + this._sent = true; + this.send(); + } + } + }, + response: function(proxy) + { + this._locatorInfo.finishRequest(this._ref, this._wellKnownRefs, proxy, false); + this._response = true; + this._proxy = proxy; + for(var i = 0; i < this._callbacks.length; ++i) + { + this._callbacks[i].response(this._locatorInfo, proxy); + } + }, + exception: function(ex) + { + this._locatorInfo.finishRequest(this._ref, this._wellKnownRefs, null, ex instanceof Ice.UserException); + this._exception = ex; + for(var i = 0; i < this._callbacks.length; ++i) + { + this._callbacks[i].exception(this._locatorInfo, ex); + } + } + }); + + var ObjectRequest = Class(Request, { + __init__: function(locatorInfo, reference) + { + Request.call(this, locatorInfo, reference); + Debug.assert(reference.isWellKnown()); + }, + send: function() + { + try + { + var self = this; + this._locatorInfo.getLocator().findObjectById(this._ref.getIdentity()).then( + function(proxy) + { + self.response(proxy); + }, + function(ex) + { + self.exception(ex); + }); + } + catch(ex) + { + this.exception(ex); + } + } + }); + + var AdapterRequest = Class(Request, { + __init__: function(locatorInfo, reference) + { + Request.call(this, locatorInfo, reference); + Debug.assert(reference.isIndirect()); + }, + send: function() + { + try + { + var self = this; + this._locatorInfo.getLocator().findAdapterById(this._ref.getAdapterId()).then( + function(proxy) + { + self.response(proxy); + }, + function(ex) + { + self.exception(ex); + }); + } + catch(ex) + { + this.exception(ex); + } + } + }); +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/LocatorManager.js b/js/src/Ice/LocatorManager.js new file mode 100644 index 00000000000..e7389b844af --- /dev/null +++ b/js/src/Ice/LocatorManager.js @@ -0,0 +1,88 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/HashMap"); + require("Ice/LocatorInfo"); + require("Ice/LocatorTable"); + require("Ice/Locator"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var LocatorInfo = Ice.LocatorInfo; + var LocatorTable = Ice.LocatorTable; + var LocatorPrx = Ice.LocatorPrx; + + var LocatorManager = Ice.Class({ + __init__: function(properties) + { + this._background = properties.getPropertyAsInt("Ice.BackgroundLocatorCacheUpdates") > 0; + + this._table = new HashMap(); // Map<Ice.LocatorPrx, LocatorInfo> + this._table.keyComparator = HashMap.compareEquals; + this._locatorTables = new HashMap(); // Map<Ice.Identity, LocatorTable> + this._locatorTables.keyComparator = HashMap.compareEquals; + }, + destroy: function() + { + for(var e = this._table.entries; e !== null; e = e.next) + { + e.value.destroy(); + } + this._table.clear(); + this._locatorTables.clear(); + }, + // + // Returns locator info for a given locator. Automatically creates + // the locator info if it doesn't exist yet. + // + find: function(loc) + { + if(loc === null) + { + return null; + } + + // + // The locator can't be located. + // + var locator = LocatorPrx.uncheckedCast(loc.ice_locator(null)); + + // + // TODO: reap unused locator info objects? + // + + var info = this._table.get(locator); + if(info === undefined) + { + // + // Rely on locator identity for the adapter table. We want to + // have only one table per locator (not one per locator + // proxy). + // + var table = this._locatorTables.get(locator.ice_getIdentity()); + if(table === undefined) + { + table = new LocatorTable(); + this._locatorTables.set(locator.ice_getIdentity(), table); + } + + info = new LocatorInfo(locator, table, this._background); + this._table.set(locator, info); + } + + return info; + } + }); + + Ice.LocatorManager = LocatorManager; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/LocatorTable.js b/js/src/Ice/LocatorTable.js new file mode 100644 index 00000000000..e80465b5be9 --- /dev/null +++ b/js/src/Ice/LocatorTable.js @@ -0,0 +1,114 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/HashMap"); + require("Ice/Debug"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var Debug = Ice.Debug; + + var LocatorTable = Ice.Class({ + __init__: function() + { + this._adapterEndpointsTable = new HashMap(); // Map<String, EndpointTableEntry> + this._objectTable = new HashMap(); // Map<Ice.Identity, ReferenceTableEntry> + this._objectTable.keyComparator = HashMap.compareEquals; + }, + clear: function() + { + this._adapterEndpointsTable.clear(); + this._objectTable.clear(); + }, + getAdapterEndpoints: function(adapter, ttl, cached) + { + if(ttl === 0) // Locator cache disabled. + { + cached.value = false; + return null; + } + + var entry = this._adapterEndpointsTable.get(adapter); + if(entry !== undefined) + { + cached.value = this.checkTTL(entry.time, ttl); + return entry.endpoints; + } + cached.value = false; + return null; + }, + addAdapterEndpoints: function(adapter, endpoints) + { + this._adapterEndpointsTable.set(adapter, new EndpointTableEntry(Date.now(), endpoints)); + }, + removeAdapterEndpoints: function(adapter) + { + var entry = this._adapterEndpointsTable.get(adapter); + this._adapterEndpointsTable.delete(adapter); + return entry !== undefined ? entry.endpoints : null; + }, + getObjectReference: function(id, ttl, cached) + { + if(ttl === 0) // Locator cache disabled. + { + cached.value = false; + return null; + } + + var entry = this._objectTable.get(id); + if(entry !== undefined) + { + cached.value = this.checkTTL(entry.time, ttl); + return entry.reference; + } + cached.value = false; + return null; + }, + addObjectReference: function(id, ref) + { + this._objectTable.set(id, new ReferenceTableEntry(Date.now(), ref)); + }, + removeObjectReference: function(id) + { + var entry = this._objectTable.get(id); + this._objectTable.delete(id); + return entry !== undefined ? entry.reference : null; + }, + checkTTL: function(time, ttl) + { + Debug.assert(ttl !== 0); + if(ttl < 0) // TTL = infinite + { + return true; + } + else + { + return Date.now() - time <= (ttl * 1000); + } + } + }); + + Ice.LocatorTable = LocatorTable; + global.Ice = Ice; + + var EndpointTableEntry = function(time, endpoints) + { + this.time = time; + this.endpoints = endpoints; + }; + + var ReferenceTableEntry = function(time, reference) + { + this.time = time; + this.reference = reference; + }; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Logger.js b/js/src/Ice/Logger.js new file mode 100644 index 00000000000..38ab8d8ee4b --- /dev/null +++ b/js/src/Ice/Logger.js @@ -0,0 +1,93 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + require("Ice/Class"); + + var Logger = Ice.Class({ + __init__: function(prefix) + { + if(prefix !== undefined && prefix.length > 0) + { + this._prefix = prefix + ": "; + } + else + { + this._prefix = ""; + } + }, + print: function(message) + { + this.write(message, false); + }, + trace: function(category, message) + { + var s = []; + var d = new Date(); + s.push("-- "); + s.push(this.timestamp()); + s.push(' '); + s.push(this._prefix); + s.push(category); + s.push(": "); + s.push(message); + this.write(s.join(""), true); + }, + warning: function(message) + { + var s = []; + var d = new Date(); + s.push("-! "); + s.push(this.timestamp()); + s.push(' '); + s.push(this._prefix); + s.push("warning: "); + s.push(message); + this.write(s.join(""), true); + }, + error: function(message) + { + var s = []; + var d = new Date(); + s.push("!! "); + s.push(this.timestamp()); + s.push(' '); + s.push(this._prefix); + s.push("error: "); + s.push(message); + this.write(s.join(""), true); + }, + cloneWithPrefix: function(prefix) + { + return new Logger(prefix); + }, + write: function(message, indent) + { + if(indent) + { + message = message.replace(/\n/g, "\n "); + } + + console.log(message); + }, + timestamp: function() + { + var d = new Date(); + var mon = d.getMonth() + 1; + mon = mon < 10 ? "0" + mon : mon; + var day = d.getDate(); + day = day < 10 ? "0" + day : day; + return mon + "-" + day + "-" + d.getFullYear() + " " + d.toLocaleTimeString() + "." + d.getMilliseconds(); + } + }); + Ice.Logger = Logger; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Long.js b/js/src/Ice/Long.js new file mode 100644 index 00000000000..1445b4098e9 --- /dev/null +++ b/js/src/Ice/Long.js @@ -0,0 +1,100 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + + // + // The Long type represents a signed 64-bit integer as two 32-bit values + // corresponding to the high and low words. + // + var Ice = global.Ice || {}; + + var Long = Ice.Class({ + __init__: function(high, low) + { + this.high = high; + this.low = low; + }, + hashCode: function() + { + return this.low; + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + if(!(rhs instanceof Long)) + { + return false; + } + return this.high === rhs.high && this.low === rhs.low; + }, + toString: function() + { + return this.high + ":" + this.low; + }, + toNumber: function() + { + if((this.high & Long.SIGN_MASK) != 0) + { + var low = ~this.low; + var high = ~this.high; + if(low < 0xFFFFFFFF) + { + low += 1; + } + else + { + low = 0; + high += 1; + if(high > Long.HIGH_MAX) + { + return Number.NEGATIVE_INFINITY; + } + } + return -1 * (high * Long.HIGH_MASK) + low; + } + else + { + if(this.high > Long.HIGH_MAX) + { + return Number.POSITIVE_INFINITY; + } + return (this.high * Long.HIGH_MASK) + this.low; + } + } + }); + + // + // (high & SIGN_MASK) != 0 denotes a negative number; + // that is, the most significant bit is set. + // + Long.SIGN_MASK = 0x80000000; + + // + // When converting to a JavaScript Number we left shift the + // high word by 32 bits. As that isn't possible using JavaScript's + // left shift operator, we multiply the value by 2^32 which will + // produce the same result. + // + Long.HIGH_MASK = 0x100000000; + + // + // The maximum value for the high word when coverting to + // a JavaScript Number is 2^21 - 1, in which case all + // 53 bits are used. + // + Long.HIGH_MAX = 0x1FFFFF; + + Ice.Long = Long; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Makefile b/js/src/Ice/Makefile new file mode 100644 index 00000000000..0b1edfc2694 --- /dev/null +++ b/js/src/Ice/Makefile @@ -0,0 +1,163 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +LIBNAME = Ice + +TARGETS = $(call mklibtargets,$(LIBNAME)) + +SLICES = $(SDIR)/BuiltinSequences.ice \ + $(SDIR)/Connection.ice \ + $(SDIR)/ConnectionF.ice \ + $(SDIR)/Current.ice \ + $(SDIR)/Endpoint.ice \ + $(SDIR)/EndpointF.ice \ + $(SDIR)/EndpointTypes.ice \ + $(SDIR)/Identity.ice \ + $(SDIR)/LocalException.ice \ + $(SDIR)/Locator.ice \ + $(SDIR)/Metrics.ice \ + $(SDIR)/ObjectAdapterF.ice \ + $(SDIR)/Process.ice \ + $(SDIR)/ProcessF.ice \ + $(SDIR)/PropertiesAdmin.ice \ + $(SDIR)/Router.ice \ + $(SDIR)/SliceChecksumDict.ice \ + $(SDIR)/Version.ice + +SDIR = $(slicedir)/Ice + +GEN_SRCS = $(patsubst $(SDIR)/%.ice, %.js, $(SLICES)) + +COMMON_SRCS = \ + Address.js \ + ArrayUtil.js \ + AsyncResultBase.js \ + AsyncResult.js \ + AsyncStatus.js \ + Base64.js \ + BasicStream.js \ + BatchOutgoingAsync.js \ + Class.js \ + Communicator.js \ + ConnectionBatchOutgoingAsync.js \ + ConnectionI.js \ + ConnectionMonitor.js \ + ConnectionReaper.js \ + ConnectionRequestHandler.js \ + ConnectRequestHandler.js \ + DefaultsAndOverrides.js \ + DispatchStatus.js \ + EndpointFactoryManager.js \ + EnumBase.js \ + Exception.js \ + ExUtil.js \ + FormatType.js \ + HashMap.js \ + HashUtil.js \ + IdentityUtil.js \ + ImplicitContextI.js \ + IncomingAsync.js \ + Initialize.js \ + Instance.js \ + LocalExceptionWrapper.js \ + LocatorInfo.js \ + LocatorManager.js \ + LocatorTable.js \ + Logger.js \ + Long.js \ + ObjectAdapterFactory.js \ + ObjectAdapterI.js \ + ObjectFactory.js \ + ObjectFactoryManager.js \ + Object.js \ + ObjectPrx.js \ + OpaqueEndpointI.js \ + Operation.js \ + OptionalFormat.js \ + OutgoingAsync.js \ + OutgoingConnectionFactory.js \ + ProcessLogger.js \ + Promise.js \ + Properties.js \ + Property.js \ + PropertyNames.js \ + Protocol.js \ + ProxyBatchOutgoingAsync.js \ + ProxyFactory.js \ + Reference.js \ + ReferenceMode.js \ + RetryQueue.js \ + RouterInfo.js \ + RouterManager.js \ + ServantManager.js \ + SocketOperation.js \ + StreamHelpers.js \ + StringUtil.js \ + Struct.js \ + Timer.js \ + TraceLevels.js \ + TraceUtil.js \ + CompactIdRegistry.js \ + UnknownSlicedObject.js \ + UUID.js + +NODEJS_SRCS = \ + Buffer.js \ + Ice.js \ + TcpEndpointFactory.js \ + TcpEndpointI.js \ + TcpTransceiver.js \ + +BROWSER_SRCS = \ + browser/Buffer.js \ + browser/EndpointInfo.js \ + browser/ConnectionInfo.js \ + browser/Transceiver.js \ + browser/EndpointI.js \ + browser/EndpointFactory.js + +ifneq ($(OPTIMIZE),yes) + NODEJS_SRCS := $(NODEJS_SRCS) Debug.js + BROWSER_SRCS := $(BROWSER_SRCS) browser/Debug.js +endif + +SRCS := $(BROWSER_SRCS) $(GEN_SRCS) $(COMMON_SRCS) +INSTALL_SRCS := $(NODEJS_SRCS) $(GEN_SRCS) $(COMMON_SRCS) + +include $(top_srcdir)/config/Make.rules.js + +# Prevent generation of these files from .ice files +Communicator.js: + +Properties.js: + +Logger.js: + +ServantLocator.js: + +ObjectFactory.js: + + +SLICE2JSFLAGS := $(SLICE2JSFLAGS) --ice -I$(slicedir) + +# IceWS slice files +browser/%.js: $(slicedir)/IceWS/%.ice $(SLICE2JS) $(SLICEPARSERLIB) + rm -f browser/$(*F).js + $(SLICE2JS) $(SLICE2JSFLAGS) $< + mv $(*F).js browser/ + +lint:: $(SRCS) + jshint $(LINTFLAGS) $(NODEJS_SRCS) $(BROWSER_SRCS) $(GEN_SRCS) $(COMMON_SRCS) + +install:: all + $(call installlib,$(DESTDIR)$(install_libdir),$(libdir),$(LIBNAME)) + $(call installmodule,$(DESTDIR)$(install_moduledir),$(INSTALL_SRCS),$(LIBNAME)) + diff --git a/js/src/Ice/Makefile.mak b/js/src/Ice/Makefile.mak new file mode 100644 index 00000000000..17e2a3688a7 --- /dev/null +++ b/js/src/Ice/Makefile.mak @@ -0,0 +1,167 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +LIBNAME = Ice + +GEN_SRCS = BuiltinSequences.js \ + Connection.js \ + ConnectionF.js \ + Current.js \ + Endpoint.js \ + EndpointF.js \ + EndpointTypes.js \ + Identity.js \ + LocalException.js \ + Locator.js \ + Metrics.js \ + ObjectAdapterF.js \ + Process.js \ + ProcessF.js \ + PropertiesAdmin.js \ + Router.js \ + SliceChecksumDict.js \ + Version.js + +COMMON_SRCS = \ + Address.js \ + ArrayUtil.js \ + AsyncResultBase.js \ + AsyncResult.js \ + AsyncStatus.js \ + Base64.js \ + BasicStream.js \ + BatchOutgoingAsync.js \ + Class.js \ + Communicator.js \ + ConnectionBatchOutgoingAsync.js \ + ConnectionI.js \ + ConnectionMonitor.js \ + ConnectionReaper.js \ + ConnectionRequestHandler.js \ + ConnectRequestHandler.js \ + DefaultsAndOverrides.js \ + DispatchStatus.js \ + EndpointFactoryManager.js \ + EnumBase.js \ + Exception.js \ + ExUtil.js \ + FormatType.js \ + HashMap.js \ + HashUtil.js \ + IdentityUtil.js \ + ImplicitContextI.js \ + IncomingAsync.js \ + Initialize.js \ + Instance.js \ + LocalExceptionWrapper.js \ + LocatorInfo.js \ + LocatorManager.js \ + LocatorTable.js \ + Logger.js \ + Long.js \ + ObjectAdapterFactory.js \ + ObjectAdapterI.js \ + ObjectFactory.js \ + ObjectFactoryManager.js \ + Object.js \ + ObjectPrx.js \ + OpaqueEndpointI.js \ + Operation.js \ + OptionalFormat.js \ + OutgoingAsync.js \ + OutgoingConnectionFactory.js \ + ProcessLogger.js \ + Promise.js \ + Properties.js \ + Property.js \ + PropertyNames.js \ + Protocol.js \ + ProxyBatchOutgoingAsync.js \ + ProxyFactory.js \ + Reference.js \ + ReferenceMode.js \ + RetryQueue.js \ + RouterInfo.js \ + RouterManager.js \ + ServantManager.js \ + SocketOperation.js \ + StreamHelpers.js \ + StringUtil.js \ + Struct.js \ + Timer.js \ + TraceLevels.js \ + TraceUtil.js \ + CompactIdRegistry.js \ + UnknownSlicedObject.js \ + UUID.js + +NODEJS_SRCS = \ + Buffer.js \ + Ice.js \ + TcpEndpointFactory.js \ + TcpEndpointI.js \ + TcpTransceiver.js \ + +BROWSER_SRCS = \ + browser\Buffer.js \ + browser\EndpointInfo.js \ + browser\ConnectionInfo.js \ + browser\Transceiver.js \ + browser\EndpointI.js \ + browser\EndpointFactory.js + +!if "$(OPTIMIZE)" != "yes" +NODEJS_SRCS = $(NODEJS_SRCS) Debug.js +BROWSER_SRCS = $(BROWSER_SRCS) browser\Debug.js +!endif + +SDIR = $(slicedir)\Ice + +SRCS = $(BROWSER_SRCS) $(GEN_SRCS) $(COMMON_SRCS) +INSTALL_SRCS = $(NODEJS_SRCS) $(GEN_SRCS) $(COMMON_SRCS) + +!include $(top_srcdir)\config\Make.rules.mak.js + +# Prevent generation of these files from .ice files +Communicator.js: + +Properties.js: + +Logger.js: + +ServantLocator.js: + +ObjectFactory.js: + + +SLICE2JSFLAGS = $(SLICE2JSFLAGS) --ice -I"$(slicedir)" + +# IceWS slice files +browser\EndpointInfo.js: "$(SLICE2JS)" "$(SLICEPARSERLIB)" + del /q browser\EndpointInfo.js + "$(SLICE2JS)" $(SLICE2JSFLAGS) "$(slicedir)\IceWS\EndpointInfo.ice" + move EndpointInfo.js browser + +browser\ConnectionInfo.js: "$(SLICE2JS)" "$(SLICEPARSERLIB)" + -del /q browser\ConnectionInfo.js + "$(SLICE2JS)" $(SLICE2JSFLAGS) "$(slicedir)\IceWS\ConnectionInfo.ice" + move ConnectionInfo.js browser + +MODULEDIR = $(install_moduledir)\$(LIBNAME) + +install:: all + @if not exist $(MODULEDIR) \ + @echo "Creating $(MODULEDIR)" && \ + mkdir "$(MODULEDIR)" + @for %i in ( $(INSTALL_SRCS) ) do \ + copy %i "$(MODULEDIR)" + copy package.json "$(MODULEDIR)" + diff --git a/js/src/Ice/Object.js b/js/src/Ice/Object.js new file mode 100644 index 00000000000..a60a60d6d9d --- /dev/null +++ b/js/src/Ice/Object.js @@ -0,0 +1,307 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + // + // Ice.Object + // + // Using IceObject in this file to avoid collisions with the native Object. + // + require("Ice/Class"); + require("Ice/DispatchStatus"); + require("Ice/Exception"); + require("Ice/FormatType"); + require("Ice/StreamHelpers"); + require("Ice/OptionalFormat"); + + var Ice = global.Ice || {}; + var Slice = global.Slice || {}; + + var Class = Ice.Class; + + var nextAddress = 0; + + var IceObject = Class({ + __init__: function() + { + // Fake Address used as the hashCode for this object instance. + this.__address = nextAddress++; + }, + hashCode: function() + { + return this.__address; + }, + ice_isA: function(s, current) + { + return this.__mostDerivedType().__ids.indexOf(s) >= 0; + }, + ice_ping: function(current) + { + }, + ice_ids: function(current) + { + return this.__mostDerivedType().__ids; + }, + ice_id: function(current) + { + return this.__mostDerivedType().__id; + }, + toString: function() + { + return "[object " + this.ice_id() + "]"; + }, + ice_preMarshal: function() + { + }, + ice_postUnmarshal: function() + { + }, + __write: function(os) + { + os.startWriteObject(null); + __writeImpl(this, os, this.__mostDerivedType()); + os.endWriteObject(); + }, + __read: function(is) + { + is.startReadObject(); + __readImpl(this, is, this.__mostDerivedType()); + is.endReadObject(false); + }, + ice_instanceof: function(T) + { + if(T) + { + if(this instanceof T) + { + return true; + } + return this.__mostDerivedType().__instanceof(T); + } + return false; + }, + // + // __mostDerivedType returns the the most derived Ice generated class. This is + // necessary because the user might extend Slice generated classes. The user + // class extensions don't have __id, __ids, __instanceof etc static members so + // the implementation of ice_id, ice_ids and ice_instanceof would fail trying + // to access those members of the user defined class. Instead, ice_id, ice_ids + // and ice_instanceof call __mostDerivedType to get the most derived Ice class. + // + // The __mostDerivedType is overriden by each Slice generated class, see the + // Slice.defineObject method implementation for details. + // + __mostDerivedType: function() + { + return IceObject; + } + }); + + // + // These methods are used for object parameters. + // + IceObject.write = function(os, v) + { + os.writeObject(v); + }; + + IceObject.writeOpt = function(os, tag, v) + { + os.writeOptObject(tag, v); + }; + + IceObject.read = function(is) + { + var v = { value: null }; + is.readObject(function(o) { v.value = o; }, IceObject); + return v; + }; + + IceObject.readOpt = function(is, tag) + { + var v = { value: undefined }; + is.readOptObject(tag, function(o) { v.value = o; }, IceObject); + return v; + }; + + IceObject.ice_staticId = function() + { + return IceObject.__id; + }; + + IceObject.__instanceof = function(T) + { + if(T === this) + { + return true; + } + + for(var i in this.__implements) + { + if(this.__implements[i].__instanceof(T)) + { + return true; + } + } + + if(this.__parent) + { + return this.__parent.__instanceof(T); + } + return false; + }; + + IceObject.__ids = ["::Ice::Object"]; + IceObject.__id = IceObject.__ids[0]; + IceObject.__compactId = -1; + IceObject.__preserved = false; + + // + // Private methods + // + + var __writeImpl = function(obj, os, type) + { + // + // The __writeImpl method is a recursive method that goes down the + // class hierarchy to marshal each slice of the class using the + // generated __writeMemberImpl method. + // + + if(type === undefined || type === IceObject) + { + return; // Don't marshal anything for IceObject + } + + os.startWriteSlice(type.__id, type.__compactId, type.__parent === IceObject); + if(type.prototype.__writeMemberImpl) + { + type.prototype.__writeMemberImpl.call(obj, os); + } + os.endWriteSlice(); + __writeImpl(obj, os, type.__parent); + }; + + var __readImpl = function(obj, is, type) + { + // + // The __readImpl method is a recursive method that goes down the + // class hierarchy to marshal each slice of the class using the + // generated __readMemberImpl method. + // + + if(type === undefined || type === IceObject) + { + return; // Don't marshal anything for IceObject + } + + is.startReadSlice(); + if(type.prototype.__readMemberImpl) + { + type.prototype.__readMemberImpl.call(obj, is); + } + is.endReadSlice(); + __readImpl(obj, is, type.__parent); + }; + + var __writePreserved = function(os) + { + // + // For Slice classes which are marked "preserved", the implementation of this method + // replaces the Ice.Object.prototype.__write method. + // + os.startWriteObject(this.__slicedData); + __writeImpl(this, os, this.__mostDerivedType()); + os.endWriteObject(); + }; + + var __readPreserved = function(is) + { + // + // For Slice classes which are marked "preserved", the implementation of this method + // replaces the Ice.Object.prototype.__read method. + // + is.startReadObject(); + __readImpl(this, is, this.__mostDerivedType()); + this.__slicedData = is.endReadObject(true); + }; + + Ice.Object = IceObject; + + Slice.defineLocalObject = function(constructor, base) + { + var obj = constructor || function(){}; + + if(base !== undefined) + { + obj.prototype = new base(); + obj.__parent = base; + obj.prototype.constructor = constructor; + } + + return obj; + }; + + Slice.defineObject = function(constructor, base, intfs, scope, ids, compactId, writeImpl, readImpl, preserved) + { + var obj = constructor || function(){}; + + obj.prototype = new base(); + obj.__parent = base; + obj.__ids = ids; + obj.__id = ids[scope]; + obj.__compactId = compactId; + obj.__instanceof = IceObject.__instanceof; + obj.__implements = intfs; + + // + // These methods are used for object parameters. + // + obj.write = function(os, v) + { + os.writeObject(v); + }; + obj.writeOpt = function(os, tag, v) + { + os.writeOptObject(tag, v); + }; + obj.read = function(is) + { + var v = { value: null }; + is.readObject(function(o) { v.value = o; }, obj); + return v; + }; + obj.readOpt = function(is, tag) + { + var v = { value: undefined }; + is.readOptObject(tag, function(o) { v.value = o; }, obj); + return v; + }; + + obj.ice_staticId = function() + { + return ids[scope]; + }; + + obj.prototype.constructor = obj; + obj.prototype.__mostDerivedType = function() { return obj; }; + if(preserved) + { + obj.prototype.__write = __writePreserved; + obj.prototype.__read = __readPreserved; + } + obj.prototype.__writeMemberImpl = writeImpl; + obj.prototype.__readMemberImpl = readImpl; + + return obj; + }; + + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ObjectAdapterFactory.js b/js/src/Ice/ObjectAdapterFactory.js new file mode 100644 index 00000000000..8fe50d5d872 --- /dev/null +++ b/js/src/Ice/ObjectAdapterFactory.js @@ -0,0 +1,150 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResultBase"); + require("Ice/LocalException"); + require("Ice/ObjectAdapterI"); + require("Ice/Promise"); + require("Ice/UUID"); + + var Ice = global.Ice || {}; + + var AsyncResultBase = Ice.AsyncResultBase; + var ObjectAdapterI = Ice.ObjectAdapterI; + var Promise = Ice.Promise; + var UUID = Ice.UUID; + + // + // Only for use by Instance. + // + var ObjectAdapterFactory = Ice.Class({ + __init__: function(instance, communicator) + { + this._instance = instance; + this._communicator = communicator; + this._adapters = []; + this._adapterNamesInUse = []; + this._shutdownPromise = new Promise(); + }, + shutdown: function() + { + // + // Ignore shutdown requests if the object adapter factory has + // already been shut down. + // + if(this._instance === null) + { + return this._shutdownPromise; + } + + this._instance = null; + this._communicator = null; + var self = this; + Promise.all( + this._adapters.map(function(adapter) + { + return adapter.deactivate(); + }) + ).then( + function() + { + self._shutdownPromise.succeed(); + }, + function(ex) + { + self._shutdownPromise.fail(ex); + } + ); + return this._shutdownPromise; + }, + waitForShutdown: function() + { + var self = this; + return this._shutdownPromise.then( + function() + { + return Promise.all(self._adapters.map(function(adapter) + { + return adapter.waitForDeactivate(); + })); + }); + }, + isShutdown: function() + { + return this._instance === null; + }, + destroy: function() + { + var self = this; + return this.waitForShutdown().then( + function() + { + return Promise.all(self._adapters.map(function(adapter) + { + return adapter.destroy(); + })); + }); + }, + createObjectAdapter: function(name, router, promise) + { + if(this._instance === null) + { + throw new Ice.ObjectAdapterDeactivatedException(); + } + + var adapter = null; + try + { + if(name.length === 0) + { + var uuid = UUID.generateUUID(); + adapter = new ObjectAdapterI(this._instance, this._communicator, this, uuid, null, true, promise); + } + else + { + if(this._adapterNamesInUse.indexOf(name) !== -1) + { + throw new Ice.AlreadyRegisteredException("object adapter", name); + } + adapter = new ObjectAdapterI(this._instance, this._communicator, this, name, router, false, promise); + this._adapterNamesInUse.push(name); + } + this._adapters.push(adapter); + } + catch(ex) + { + promise.fail(ex, promise); + } + }, + removeObjectAdapter: function(adapter) + { + if(this._instance === null) + { + return; + } + + var n = this._adapters.indexOf(adapter); + if(n !== -1) + { + this._adapters.splice(n, 1); + } + + n = this._adapterNamesInUse.indexOf(adapter.getName()); + if(n !== -1) + { + this._adapterNamesInUse.splice(n, 1); + } + } + }); + + Ice.ObjectAdapterFactory = ObjectAdapterFactory; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ObjectAdapterI.js b/js/src/Ice/ObjectAdapterI.js new file mode 100644 index 00000000000..422b6a759e6 --- /dev/null +++ b/js/src/Ice/ObjectAdapterI.js @@ -0,0 +1,547 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResultBase"); + require("Ice/Debug"); + require("Ice/Identity"); + require("Ice/LocalException"); + require("Ice/Promise"); + require("Ice/PropertyNames"); + require("Ice/Router"); + require("Ice/ServantManager"); + require("Ice/StringUtil"); + require("Ice/UUID"); + + var Ice = global.Ice || {}; + + var AsyncResultBase = Ice.AsyncResultBase; + var Debug = Ice.Debug; + var Identity = Ice.Identity; + var Promise = Ice.Promise; + var PropertyNames = Ice.PropertyNames; + var ServantManager = Ice.ServantManager; + var StringUtil = Ice.StringUtil; + var UUID = Ice.UUID; + + var _suffixes = + [ + "ACM", + "AdapterId", + "Endpoints", + "Locator", + "Locator.EncodingVersion", + "Locator.EndpointSelection", + "Locator.ConnectionCached", + "Locator.PreferSecure", + "Locator.CollocationOptimized", + "Locator.Router", + "PublishedEndpoints", + "RegisterProcess", + "ReplicaGroupId", + "Router", + "Router.EncodingVersion", + "Router.EndpointSelection", + "Router.ConnectionCached", + "Router.PreferSecure", + "Router.CollocationOptimized", + "Router.Locator", + "Router.Locator.EndpointSelection", + "Router.Locator.ConnectionCached", + "Router.Locator.PreferSecure", + "Router.Locator.CollocationOptimized", + "Router.Locator.LocatorCacheTimeout", + "Router.LocatorCacheTimeout", + "ProxyOptions", + "ThreadPool.Size", + "ThreadPool.SizeMax", + "ThreadPool.SizeWarn", + "ThreadPool.StackSize", + "ThreadPool.Serialize" + ]; + + // + // Only for use by IceInternal.ObjectAdapterFactory + // + var ObjectAdapterI = Ice.Class({ + __init__: function(instance, communicator, objectAdapterFactory, name, router, noConfig, promise) + { + this._deactivated = false; + this._instance = instance; + this._communicator = communicator; + this._objectAdapterFactory = objectAdapterFactory; + this._servantManager = new ServantManager(instance, name); + this._name = name; + this._routerEndpoints = []; + this._routerInfo = null; + this._destroyed = false; + this._noConfig = noConfig; + + if(this._noConfig) + { + this._reference = this._instance.referenceFactory().createFromString("dummy -t", ""); + promise.succeed(this, promise); + return; + } + + var properties = this._instance.initializationData().properties; + var unknownProps = []; + var noProps = this.filterProperties(unknownProps); + + // + // Warn about unknown object adapter properties. + // + if(unknownProps.length !== 0 && properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0) + { + var message = ["found unknown properties for object adapter `" + name + "':"]; + for(var i = 0; i < unknownProps.length; ++i) + { + message.push("\n " + unknownProps[i]); + } + this._instance.initializationData().logger.warning(message.join("")); + } + + // + // Make sure named adapter has some configuration. + // + if(router === null && noProps) + { + var ex = new Ice.InitializationException(); + ex.reason = "object adapter `" + this._name + "' requires configuration"; + throw ex; + } + + // + // Setup a reference to be used to get the default proxy options + // when creating new proxies. By default, create twoway proxies. + // + var proxyOptions = properties.getPropertyWithDefault(this._name + ".ProxyOptions", "-t"); + try + { + this._reference = this._instance.referenceFactory().createFromString("dummy " + proxyOptions, ""); + } + catch(e) + { + if(e instanceof Ice.ProxyParseException) + { + var ex = new Ice.InitializationException(); + ex.reason = "invalid proxy options `" + proxyOptions + "' for object adapter `" + name + "'"; + throw ex; + } + else + { + throw e; + } + } + + try + { + + if(router === null) + { + router = Ice.RouterPrx.uncheckedCast( + this._instance.proxyFactory().propertyToProxy(this._name + ".Router")); + } + if(router !== null) + { + this._routerInfo = this._instance.routerManager().find(router); + Debug.assert(this._routerInfo !== null); + + // + // Make sure this router is not already registered with another adapter. + // + if(this._routerInfo.getAdapter() !== null) + { + throw new Ice.AlreadyRegisteredException( + "object adapter with router", + this._instance.identityToString(router.ice_getIdentity())); + } + + // + // Add the router's server proxy endpoints to this object + // adapter. + // + var self = this; + this._routerInfo.getServerEndpoints().then( + function(endpoints) + { + var i; + + for(i = 0; i < endpoints.length; ++i) + { + self._routerEndpoints.push(endpoints[i]); + } + self._routerEndpoints.sort( // Must be sorted. + function(e1, e2) + { + return e1.compareTo(e2); + }); + + // + // Remove duplicate endpoints, so we have a list of unique + // endpoints. + // + for(i = 0; i < self._routerEndpoints.length - 1;) + { + var e1 = self._routerEndpoints[i]; + var e2 = self._routerEndpoints[i + 1]; + if(e1.equals(e2)) + { + self._routerEndpoints.splice(i, 1); + } + else + { + ++i; + } + } + + // + // Associate this object adapter with the router. This way, + // new outgoing connections to the router's client proxy will + // use this object adapter for callbacks. + // + self._routerInfo.setAdapter(self); + + // + // Also modify all existing outgoing connections to the + // router's client proxy to use this object adapter for + // callbacks. + // + return self._instance.outgoingConnectionFactory().setRouterInfo(self._routerInfo); + } + ).then( + function() + { + promise.succeed(self, promise); + }, + function(ex) + { + promise.fail(ex, promise); + }); + } + else + { + var endpoints = properties.getProperty(this._name + ".Endpoints"); + if(endpoints.length > 0) + { + throw new Ice.FeatureNotSupportedException("object adapter endpoints not supported"); + } + promise.succeed(this, promise); + } + } + catch(ex) + { + this.destroy(); + throw ex; + } + }, + getName: function() + { + // + // No mutex lock necessary, _name is immutable. + // + return this._noConfig ? "" : this._name; + }, + getCommunicator: function() + { + return this._communicator; + }, + activate: function() + { + }, + hold: function() + { + this.checkForDeactivation(); + }, + waitForHold: function() + { + var promise = new AsyncResultBase(this._communicator, "waitForHold", null, null, this); + if(adapter.checkForDeactivation(promise)) + { + return promise; + } + return promise.succeed(promise); + }, + deactivate: function() + { + var promise = new AsyncResultBase(this._communicator, "deactivate", null, null, this); + if(!this._deactivated) + { + this._deactivated = true; + this._instance.outgoingConnectionFactory().removeAdapter(this); + } + return promise.succeed(promise); + }, + waitForDeactivate: function() + { + var promise = new AsyncResultBase(this._communicator, "deactivate", null, null, this); + return promise.succeed(promise); + }, + isDeactivated: function() + { + return this._deactivated; + }, + destroy: function() + { + var promise = new AsyncResultBase(this._communicator, "destroy", null, null, this); + if(!this._deactivated) + { + this.deactivate(); + } + if(!this._destroyed) + { + this._destroyed = true; + this._servantManager.destroy(); + this._objectAdapterFactory.removeObjectAdapter(this); + } + return promise.succeed(promise); + }, + add: function(object, ident) + { + return this.addFacet(object, ident, ""); + }, + addFacet: function(object, ident, facet) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + // + // Create a copy of the Identity argument, in case the caller + // reuses it. + // + var id = ident.clone(); + + this._servantManager.addServant(object, id, facet); + + return this.newProxy(id, facet); + }, + addWithUUID: function(object) + { + return this.addFacetWithUUID(object, ""); + }, + addFacetWithUUID: function(object, facet) + { + return this.addFacet(object, new Identity(UUID.generateUUID(), ""), facet); + }, + addDefaultServant: function(servant, category) + { + this.checkForDeactivation(); + + this._servantManager.addDefaultServant(servant, category); + }, + remove: function(ident) + { + return this.removeFacet(ident, ""); + }, + removeFacet: function(ident, facet) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + return this._servantManager.removeServant(ident, facet); + }, + removeAllFacets: function(ident) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + return this._servantManager.removeAllFacets(ident); + }, + removeDefaultServant: function(category) + { + this.checkForDeactivation(); + + return this._servantManager.removeDefaultServant(category); + }, + find: function(ident) + { + return this.findFacet(ident, ""); + }, + findFacet: function(ident, facet) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + return this._servantManager.findServant(ident, facet); + }, + findAllFacets: function(ident) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + return this._servantManager.findAllFacets(ident); + }, + findByProxy: function(proxy) + { + this.checkForDeactivation(); + + var ref = proxy.__reference(); + return this.findFacet(ref.getIdentity(), ref.getFacet()); + }, + findDefaultServant: function(category) + { + this.checkForDeactivation(); + + return this._servantManager.findDefaultServant(category); + }, + addServantLocator: function(locator, prefix) + { + this.checkForDeactivation(); + + this._servantManager.addServantLocator(locator, prefix); + }, + removeServantLocator: function(prefix) + { + this.checkForDeactivation(); + + return this._servantManager.removeServantLocator(prefix); + }, + findServantLocator: function(prefix) + { + this.checkForDeactivation(); + + return this._servantManager.findServantLocator(prefix); + }, + createProxy: function(ident) + { + this.checkForDeactivation(); + this.checkIdentity(ident); + + return this.newProxy(ident, ""); + }, + createDirectProxy: function(ident) + { + return this.createProxy(ident); + }, + createIndirectProxy: function(ident) + { + throw new Ice.FeatureNotSupportedException("setLocator not supported"); + }, + setLocator: function(locator) + { + throw new Ice.FeatureNotSupportedException("setLocator not supported"); + }, + refreshPublishedEndpoints: function() + { + throw new Ice.FeatureNotSupportedException("refreshPublishedEndpoints not supported"); + }, + getEndpoints: function() + { + return []; + }, + getPublishedEndpoints: function() + { + return []; + }, + getServantManager: function() + { + // + // _servantManager is immutable. + // + return this._servantManager; + }, + newProxy: function(ident, facet) + { + var endpoints = []; + + // + // Now we also add the endpoints of the router's server proxy, if + // any. This way, object references created by this object adapter + // will also point to the router's server proxy endpoints. + // + for(var i = 0; i < this._routerEndpoints.length; ++i) + { + endpoints.push(this._routerEndpoints[i]); + } + + // + // Create a reference and return a proxy for this reference. + // + var ref = this._instance.referenceFactory().create(ident, facet, this._reference, endpoints); + return this._instance.proxyFactory().referenceToProxy(ref); + }, + checkForDeactivation: function(promise) + { + if(this._deactivated) + { + var ex = new Ice.ObjectAdapterDeactivatedException(); + ex.name = this.getName(); + + if(promise !== undefined) + { + promise.fail(ex, promise); + return true; + } + else + { + throw ex; + } + } + + return false; + }, + checkIdentity: function(ident) + { + if(ident.name === undefined || ident.name === null || ident.name.length === 0) + { + var e = new Ice.IllegalIdentityException(); + e.id = ident.clone(); + throw e; + } + + if(ident.category === undefined || ident.category === null) + { + ident.category = ""; + } + }, + filterProperties: function(unknownProps) + { + // + // Do not create unknown properties list if Ice prefix, i.e., Ice, Glacier2, etc. + // + var addUnknown = true, i; + var prefix = this._name + "."; + for(i = 0; i < PropertyNames.clPropNames.length; ++i) + { + if(prefix.indexOf(PropertyNames.clPropNames[i] + ".") === 0) + { + addUnknown = false; + break; + } + } + + var noProps = true; + var props = this._instance.initializationData().properties.getPropertiesForPrefix(prefix); + for(var e = props.entries; e !== null; e = e.next) + { + var valid = false; + for(i = 0; i < _suffixes.length; ++i) + { + if(e.key === prefix + _suffixes[i]) + { + noProps = false; + valid = true; + break; + } + } + + if(!valid && addUnknown) + { + unknownProps.push(e.key); + } + } + + return noProps; + } + }); + + Ice.ObjectAdapterI = ObjectAdapterI; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ObjectFactory.js b/js/src/Ice/ObjectFactory.js new file mode 100644 index 00000000000..2f178c4c73c --- /dev/null +++ b/js/src/Ice/ObjectFactory.js @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + require("Ice/Class"); + + var ObjectFactory = Ice.Class({ + create: function(type) + { + throw new Error("not implemented"); + }, + destroy: function() + { + throw new Error("not implemented"); + } + }); + + Ice.ObjectFactory = ObjectFactory; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ObjectFactoryManager.js b/js/src/Ice/ObjectFactoryManager.js new file mode 100644 index 00000000000..bffaeaa3070 --- /dev/null +++ b/js/src/Ice/ObjectFactoryManager.js @@ -0,0 +1,76 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/HashMap"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var AlreadyRegisteredException = Ice.AlreadyRegisteredException; + var NotRegisteredException = Ice.NotRegisteredException; + + // + // Only for use by Instance + // + var ObjectFactoryManager = Ice.Class({ + __init__: function() + { + this._factoryMap = new HashMap(); // Map<String, ObjectFactory> + }, + add: function(factory, id) + { + var o, ex; + o = this._factoryMap.get(id); + if(o !== undefined) + { + ex = new AlreadyRegisteredException(); + ex.id = id; + ex.kindOfObject = "object factory"; + throw ex; + } + this._factoryMap.set(id, factory); + }, + remove: function(id) + { + var factory, ex; + factory = this._factoryMap.get(id); + if(factory === undefined) + { + ex = new NotRegisteredException(); + ex.id = id; + ex.kindOfObject = "object factory"; + throw ex; + } + this._factoryMap.delete(id); + factory.destroy(); + }, + find: function(id) + { + return this._factoryMap.get(id); + }, + destroy: function() + { + var oldMap = this._factoryMap, + e = oldMap.entries; + this._factoryMap = new HashMap(); // Map<String, ObjectFactory> + + while(e !== null) + { + e.value.destroy(); + e = e.next; + } + } + }); + + Ice.ObjectFactoryManager = ObjectFactoryManager; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ObjectPrx.js b/js/src/Ice/ObjectPrx.js new file mode 100644 index 00000000000..e6e2e0da9aa --- /dev/null +++ b/js/src/Ice/ObjectPrx.js @@ -0,0 +1,967 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/ArrayUtil"); + require("Ice/AsyncResult"); + require("Ice/ConnectRequestHandler"); + require("Ice/Debug"); + require("Ice/FormatType"); + require("Ice/HashMap"); + require("Ice/OutgoingAsync"); + require("Ice/ProxyBatchOutgoingAsync"); + require("Ice/ReferenceMode"); + require("Ice/Current"); + require("Ice/Exception"); + require("Ice/BuiltinSequences"); + require("Ice/LocalException"); + require("Ice/Object"); + + var Ice = global.Ice || {}; + var Slice = global.Slice || {}; + + var ArrayUtil = Ice.ArrayUtil; + var AsyncResultBase = Ice.AsyncResultBase; + var AsyncResult = Ice.AsyncResult; + var ConnectRequestHandler = Ice.ConnectRequestHandler; + var Debug = Ice.Debug; + var FormatType = Ice.FormatType; + var HashMap = Ice.HashMap; + var OutgoingAsync = Ice.OutgoingAsync; + var ProxyBatchOutgoingAsync = Ice.ProxyBatchOutgoingAsync; + var RefMode = Ice.ReferenceMode; + var OperationMode = Ice.OperationMode; + + // + // Ice.ObjectPrx + // + var ObjectPrx = Ice.Class({ + __init__: function() + { + this._reference = null; + this._handler = null; + }, + hashCode: function(r) + { + return this._reference.hashCode(); + }, + ice_getCommunicator: function() + { + return this._reference.getCommunicator(); + }, + toString: function() + { + return this._reference.toString(); + }, + ice_getIdentity: function() + { + return this._reference.getIdentity().clone(); + }, + ice_identity: function(newIdentity) + { + if(newIdentity === undefined || newIdentity === null || newIdentity.name.length === 0) + { + throw new Ice.IllegalIdentityException(); + } + if(newIdentity.equals(this._reference.getIdentity())) + { + return this; + } + else + { + var proxy = new ObjectPrx(); + proxy.__setup(this._reference.changeIdentity(newIdentity)); + return proxy; + } + }, + ice_getContext: function() + { + return new HashMap(this._reference.getContext()); + }, + ice_context: function(newContext) + { + return this.__newInstance(this._reference.changeContext(newContext)); + }, + ice_getFacet: function() + { + return this._reference.getFacet(); + }, + ice_facet: function(newFacet) + { + if(newFacet === undefined || newFacet === null) + { + newFacet = ""; + } + + if(newFacet === this._reference.getFacet()) + { + return this; + } + else + { + var proxy = new ObjectPrx(); + proxy.__setup(this._reference.changeFacet(newFacet)); + return proxy; + } + }, + ice_getAdapterId: function() + { + return this._reference.getAdapterId(); + }, + ice_adapterId: function(newAdapterId) + { + if(newAdapterId === undefined || newAdapterId === null) + { + newAdapterId = ""; + } + + if(newAdapterId === this._reference.getAdapterId()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeAdapterId(newAdapterId)); + } + }, + ice_getEndpoints: function() + { + return ArrayUtil.clone(this._reference.getEndpoints()); + }, + ice_endpoints: function(newEndpoints) + { + if(newEndpoints === undefined || newEndpoints === null) + { + newEndpoints = []; + } + + if(ArrayUtil.equals(newEndpoints, this._reference.getEndpoints())) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeEndpoints(newEndpoints)); + } + }, + ice_getLocatorCacheTimeout: function() + { + return this._reference.getLocatorCacheTimeout(); + }, + ice_locatorCacheTimeout: function(newTimeout) + { + if(newTimeout === this._reference.getLocatorCacheTimeout()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeLocatorCacheTimeout(newTimeout)); + } + }, + ice_isConnectionCached: function() + { + return this._reference.getCacheConnection(); + }, + ice_connectionCached: function(newCache) + { + if(newCache === this._reference.getCacheConnection()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeCacheConnection(newCache)); + } + }, + ice_getEndpointSelection: function() + { + return this._reference.getEndpointSelection(); + }, + ice_endpointSelection: function(newType) + { + if(newType === this._reference.getEndpointSelection()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeEndpointSelection(newType)); + } + }, + ice_isSecure: function() + { + return this._reference.getSecure(); + }, + ice_secure: function(b) + { + if(b === this._reference.getSecure()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeSecure(b)); + } + }, + ice_getEncodingVersion: function() + { + return this._reference.getEncoding().clone(); + }, + ice_encodingVersion: function(e) + { + if(e.equals(this._reference.getEncoding())) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeEncoding(e)); + } + }, + ice_isPreferSecure: function() + { + return this._reference.getPreferSecure(); + }, + ice_preferSecure: function(b) + { + if(b === this._reference.getPreferSecure()) + { + return this; + } + else + { + return this.__newInstance(this._reference.changePreferSecure(b)); + } + }, + ice_getRouter: function() + { + var ri = this._reference.getRouterInfo(); + return ri !== null ? ri.getRouter() : null; + }, + ice_router: function(router) + { + var ref = this._reference.changeRouter(router); + if(ref.equals(this._reference)) + { + return this; + } + else + { + return this.__newInstance(ref); + } + }, + ice_getLocator: function() + { + var ri = this._reference.getLocatorInfo(); + return ri !== null ? ri.getLocator() : null; + }, + ice_locator: function(locator) + { + var ref = this._reference.changeLocator(locator); + if(ref.equals(this._reference)) + { + return this; + } + else + { + return this.__newInstance(ref); + } + }, + ice_isTwoway: function() + { + return this._reference.getMode() === RefMode.ModeTwoway; + }, + ice_twoway: function() + { + if(this._reference.getMode() === RefMode.ModeTwoway) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeMode(RefMode.ModeTwoway)); + } + }, + ice_isOneway: function() + { + return this._reference.getMode() === RefMode.ModeOneway; + }, + ice_oneway: function() + { + if(this._reference.getMode() === RefMode.ModeOneway) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeMode(RefMode.ModeOneway)); + } + }, + ice_isBatchOneway: function() + { + return this._reference.getMode() === RefMode.ModeBatchOneway; + }, + ice_batchOneway: function() + { + if(this._reference.getMode() === RefMode.ModeBatchOneway) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeMode(RefMode.ModeBatchOneway)); + } + }, + ice_isDatagram: function() + { + return this._reference.getMode() === RefMode.ModeDatagram; + }, + ice_datagram: function() + { + if(this._reference.getMode() === RefMode.ModeDatagram) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeMode(RefMode.ModeDatagram)); + } + }, + ice_isBatchDatagram: function() + { + return this._reference.getMode() === RefMode.ModeBatchDatagram; + }, + ice_batchDatagram: function() + { + if(this._reference.getMode() === RefMode.ModeBatchDatagram) + { + return this; + } + else + { + return this.__newInstance(this._reference.changeMode(RefMode.ModeBatchDatagram)); + } + }, + ice_compress: function(co) + { + var ref = this._reference.changeCompress(co); + if(ref.equals(this._reference)) + { + return this; + } + else + { + return this.__newInstance(ref); + } + }, + ice_timeout: function(t) + { + var ref = this._reference.changeTimeout(t); + if(ref.equals(this._reference)) + { + return this; + } + else + { + return this.__newInstance(ref); + } + }, + ice_getConnectionId: function() + { + return this._reference.getConnectionId(); + }, + ice_connectionId: function(id) + { + var ref = this._reference.changeConnectionId(id); + if(ref.equals(this._reference)) + { + return this; + } + else + { + return this.__newInstance(ref); + } + }, + ice_getConnection: function() + { + var __r = new AsyncResultBase(this._reference.getCommunicator(), "ice_getConnection", null, this, null); + this.__getRequestHandler().onConnection(__r); + return __r; + }, + ice_getCachedConnection: function() + { + return this._handler ? this._handler.getConnection() : null; + }, + ice_flushBatchRequests: function() + { + var __r = new ProxyBatchOutgoingAsync(this, "ice_flushBatchRequests"); + try + { + __r.__send(); + } + catch(__ex) + { + this.__handleLocalException(__r, __ex); + } + return __r; + }, + equals: function(r) + { + if(this === r) + { + return true; + } + + if(r instanceof ObjectPrx) + { + return this._reference.equals(r._reference); + } + + return false; + }, + __reference: function() + { + return this._reference; + }, + __copyFrom: function(from) + { + Debug.assert(this._reference === null); + Debug.assert(this._handler === null); + + this._reference = from._reference; + + if(this._reference.getCacheConnection()) + { + this._handler = from._handler; + } + }, + __handleException: function(handler, ex, interval, cnt) + { + if(this._handler !== null && handler._connection === this._handler._connection) + { + this._handler = null; + } + + if(cnt == -1) // Don't retry if the retry count is -1. + { + throw ex; + } + + try + { + return this._reference.getInstance().proxyFactory().checkRetryAfterException(ex, this._reference, + interval, cnt); + } + catch(e) + { + if(e instanceof Ice.CommunicatorDestroyedException) + { + // + // The communicator is already destroyed, so we cannot + // retry. + // + throw ex; + } + else + { + throw e; + } + } + }, + __handleExceptionWrapper: function(handler, ex) + { + if(this._handler !== null && handler._connection === this._handler._connection) + { + this._handler = null; + } + + if(!ex.retry) + { + throw ex.inner; + } + }, + __handleExceptionWrapperRelaxed: function(handler, ex, interval, cnt) + { + if(!ex.retry) + { + return this.__handleException(handler, ex.inner, interval, cnt); + } + else + { + if(this._handler !== null && handler._connection === this._handler._connection) + { + this._handler = null; + } + + return cnt; + } + }, + __checkAsyncTwowayOnly: function(name) + { + if(!this.ice_isTwoway()) + { + throw new Error("`" + name + "' can only be called with a twoway proxy"); + } + }, + ice_invoke: function(operation, mode, inParams, ctx, explicitCtx) + { + if(explicitCtx && ctx === null) + { + ctx = new Ice.HashMap(); + } + + var self = this; + + var completedFn = function(__res) + { + try + { + var results = []; + if((__r._state & AsyncResult.OK) === 0) + { + results.push(false); + } + else + { + results.push(true); + } + if(self._reference.getMode() === Ice.ReferenceMode.ModeTwoway) + { + results.push(__res._is.readEncaps(null)); + } + results.push(__res); + __res.succeed.apply(__res, results); + } + catch(ex) + { + ObjectPrx.__dispatchLocalException(__res, ex); + return; + } + }; + + var __r = new OutgoingAsync(this, operation, completedFn, completedFn); + + try + { + __r.__prepare(operation, mode, ctx); + __r.__writeParamEncaps(inParams); + __r.__send(); + } + catch(ex) + { + this.__handleLocalException(__r, ex); + } + return __r; + }, + __getRequestHandler: function() + { + if(this._reference.getCacheConnection()) + { + if(this._handler !== null) + { + return this._handler; + } + this._handler = this.__createRequestHandler(); + return this._handler; + } + else + { + return this.__createRequestHandler(); + } + }, + __setRequestHandler: function(handler) + { + if(this._reference.getCacheConnection()) + { + this._handler = handler; + } + }, + __createRequestHandler: function() + { + var handler = new ConnectRequestHandler(this._reference, this); + return handler.connect(); + }, + // + // Only for use by IceInternal.ProxyFactory + // + __setup: function(ref) + { + Debug.assert(this._reference === null); + + this._reference = ref; + }, + __newInstance: function(ref) + { + var proxy = new this.constructor(); + proxy.__setup(ref); + return proxy; + }, + __handleLocalException: function(__r, __ex) + { + if(__ex instanceof Ice.LocalException) + { + __r.__exception(__ex); + } + else + { + throw __ex; + } + }, + ice_instanceof: function(T) + { + if(T) + { + if(this instanceof T) + { + return true; + } + return this.constructor.__instanceof(T); + } + return false; + } + }); + + // + // Generic invocation for operations that have input parameters. + // + ObjectPrx.__invoke = function(p, name, mode, fmt, ctx, marshalFn, unmarshalFn, userEx, args) + { + if(unmarshalFn !== null || userEx.length > 0) + { + p.__checkAsyncTwowayOnly(name); + } + + var __r = new OutgoingAsync(p, name, + function(__res) + { + ObjectPrx.__completed(__res, unmarshalFn, userEx); + }); + + try + { + __r.__prepare(name, mode, ctx); + if(marshalFn === null) + { + __r.__writeEmptyParams(); + } + else + { + var __os = __r.__startWriteParams(fmt); + marshalFn.call(null, __os, args); + __r.__endWriteParams(); + } + __r.__send(); + } + catch(ex) + { + p.__handleLocalException(__r, ex); + } + return __r; + }; + + // + // Handles the completion of an invocation. + // + ObjectPrx.__completed = function(__r, unmarshalFn, userEx) + { + if(!ObjectPrx.__check(__r, userEx)) + { + return; + } + + try + { + if(unmarshalFn === null) + { + __r.__readEmptyParams(); + __r.succeed(__r); + } + else + { + var results = unmarshalFn(__r); + __r.succeed.apply(__r, results); + } + } + catch(ex) + { + ObjectPrx.__dispatchLocalException(__r, ex); + return; + } + }; + + // + // Unmarshal callback for operations that return a bool as the only result. + // + ObjectPrx.__returns_bool = function(__is, __results) + { + __results.push(__is.readBool()); + }; + + // + // Unmarshal callback for operations that return a byte as the only result. + // + ObjectPrx.__returns_byte = function(__is, __results) + { + __results.push(__is.readByte()); + }; + + // + // Unmarshal callback for operations that return a short as the only result. + // + ObjectPrx.__returns_short = function(__is, __results) + { + __results.push(__is.readShort()); + }; + + // + // Unmarshal callback for operations that return an int as the only result. + // + ObjectPrx.__returns_int = function(__is, __results) + { + __results.push(__is.readInt()); + }; + + // + // Unmarshal callback for operations that return a long as the only result. + // + ObjectPrx.__returns_long = function(__is, __results) + { + __results.push(__is.readLong()); + }; + + // + // Unmarshal callback for operations that return a float as the only result. + // + ObjectPrx.__returns_float = function(__is, __results) + { + __results.push(__is.readFloat()); + }; + + // + // Unmarshal callback for operations that return a double as the only result. + // + ObjectPrx.__returns_double = function(__is, __results) + { + __results.push(__is.readDouble()); + }; + + // + // Unmarshal callback for operations that return a string as the only result. + // + ObjectPrx.__returns_string = function(__is, __results) + { + __results.push(__is.readString()); + }; + + // + // Unmarshal callback for operations that return a proxy as the only result. + // + ObjectPrx.__returns_ObjectPrx = function(__is, __results) + { + __results.push(__is.readProxy()); + }; + + // + // Unmarshal callback for operations that return an object as the only result. + // + ObjectPrx.__returns_Object = function(__is, __results) + { + __is.readObject(function(obj){ __results.push(obj); }, Ice.Object); + __is.readPendingObjects(); + }; + + // + // Handles user exceptions. + // + ObjectPrx.__check = function(__r, __uex) + { + // + // If __uex is non-null, it must be an array of exception types. + // + try + { + __r.__throwUserException(); + } + catch(ex) + { + if(ex instanceof Ice.UserException) + { + if(__uex !== null) + { + for(var i = 0; i < __uex.length; ++i) + { + if(ex instanceof __uex[i]) + { + __r.fail(ex, __r); + return false; + } + } + } + __r.fail(new Ice.UnknownUserException(ex.ice_name()), __r); + return false; + } + else + { + __r.fail(ex, __r); + return false; + } + } + + return true; + }; + + ObjectPrx.__dispatchLocalException = function(__r, __ex) + { + __r.fail(__ex, __r); + }; + + ObjectPrx.ice_staticId = Ice.Object.ice_staticId; + + ObjectPrx.checkedCast = function(prx, facet, ctx) + { + var __r = null; + + if(prx === undefined || prx === null) + { + __r = new AsyncResultBase(null, "checkedCast", null, null, null); + __r.succeed(null, __r); + } + else + { + if(facet !== undefined) + { + prx = prx.ice_facet(facet); + } + + var __h = new this(); + __h.__copyFrom(prx); + __r = new AsyncResultBase(prx.ice_getCommunicator(), "checkedCast", null, __h, null); + prx.ice_isA(this.ice_staticId(), ctx).then( + function(__res, __ret) + { + __r.succeed(__ret ? __h : null, __r); + }).exception( + function(__ex) + { + if(__ex instanceof Ice.FacetNotExistException) + { + __r.succeed(null, __r); + } + else + { + __r.fail(__ex, __r); + } + }); + } + + return __r; + }; + + ObjectPrx.uncheckedCast = function(prx, facet) + { + var r = null; + if(prx !== undefined && prx !== null) + { + r = new this(); + if(facet !== undefined) + { + prx = prx.ice_facet(facet); + } + r.__copyFrom(prx); + } + return r; + }; + + Object.defineProperty(ObjectPrx, "minWireSize", { + get: function(){ return 2; } + }); + + ObjectPrx.write = function(os, v) + { + os.writeProxy(v); + }; + + ObjectPrx.read = function(is) + { + return is.readProxy(this); + }; + + ObjectPrx.writeOpt = function(os, tag, v) + { + os.writeOptProxy(tag, v); + }; + + ObjectPrx.readOpt = function(is, tag) + { + return is.readOptProxy(tag, this); + }; + + ObjectPrx.__instanceof = function(T) + { + if(T === this) + { + return true; + } + + for(var i in this.__implements) + { + if(this.__implements[i].__instanceof(T)) + { + return true; + } + } + + if(this.__parent) + { + return this.__parent.__instanceof(T); + } + return false; + }; + + Slice.defineProxy = function(base, staticId, prxInterfaces) + { + var prx = function() + { + base.call(this); + }; + prx.__parent = base; + prx.__implements = prxInterfaces; + + // All generated proxies inherit from ObjectPrx + prx.prototype = new base(); + prx.prototype.constructor = prx; + + // Static methods + prx.ice_staticId = staticId; + + // Copy static methods inherited from ObjectPrx + prx.checkedCast = ObjectPrx.checkedCast; + prx.uncheckedCast = ObjectPrx.uncheckedCast; + prx.write = ObjectPrx.write; + prx.writeOpt = ObjectPrx.writeOpt; + prx.read = ObjectPrx.read; + prx.readOpt = ObjectPrx.readOpt; + + prx.__instanceof = ObjectPrx.__instanceof; + + // Static properties + Object.defineProperty(prx, "minWireSize", { + get: function(){ return 2; } + }); + + return prx; + }; + + Ice.ObjectPrx = ObjectPrx; + + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/OpaqueEndpointI.js b/js/src/Ice/OpaqueEndpointI.js new file mode 100644 index 00000000000..32472804727 --- /dev/null +++ b/js/src/Ice/OpaqueEndpointI.js @@ -0,0 +1,446 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Base64"); + require("Ice/Debug"); + require("Ice/FormatType"); + require("Ice/HashUtil"); + require("Ice/Protocol"); + require("Ice/StringUtil"); + require("Ice/Endpoint"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var Base64 = Ice.Base64; + var Debug = Ice.Debug; + var HashUtil = Ice.HashUtil; + var Protocol = Ice.Protocol; + var StringUtil = Ice.StringUtil; + + var Class = Ice.Class; + + var OpaqueEndpointI = Class(Ice.Endpoint, { + // + // Marshal the endpoint + // + streamWrite: function(s) + { + s.writeShort(this._type); + s.startWriteEncaps(this._rawEncoding, Ice.FormatType.DefaultFormat); + s.writeBlob(this._rawBytes); + s.endWriteEncaps(); + }, + // + // Convert the endpoint to its string form + // + toString: function() + { + var val = Base64.encode(this._rawBytes); + return "opaque -t " + this._type + " -e " + Ice.encodingVersionToString(this._rawEncoding) + + " -v " + val; + }, + // + // Return the endpoint information. + // + getInfo: function() + { + return new OpaqueEndpointInfoI(-1, false, this._rawEncoding, this._rawBytes, this._type); + }, + // + // Return the endpoint type + // + type: function() + { + return this._type; + }, + protocol: function() + { + return "opaque"; + }, + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + timeout: function() + { + return -1; + }, + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + changeTimeout: function(t) + { + return this; + }, + // + // Return a new endpoint with a different connection id. + // + changeConnectionId: function(connectionId) + { + return this; + }, + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + compress: function() + { + return false; + }, + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + changeCompress: function(compress) + { + return this; + }, + // + // Return true if the endpoint is datagram-based. + // + datagram: function() + { + return false; + }, + // + // Return true if the endpoint is secure. + // + secure: function() + { + return false; + }, + // + // Get the encoded endpoint. + // + rawBytes: function() + { + return this._rawBytes; // Returns a Uint8Array + }, + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. In case a + // transceiver is created, this operation also returns a new + // "effective" endpoint, which might differ from this endpoint, + // for example, if a dynamic port number is assigned. + // + transceiver: function(endpoint) + { + endpoint.value = null; + return null; + }, + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. In case an acceptor is created, this operation + // also returns a new "effective" endpoint, which might differ + // from this endpoint, for example, if a dynamic port number is + // assigned. + // + acceptor: function(endpoint, adapterName) + { + endpoint.value = this; + return null; + }, + connect: function() + { + return null; + }, + hashCode: function() + { + return this._hashCode; + }, + // + // Compare endpoints for sorting purposes + // + equals: function(p) + { + if(!(p instanceof OpaqueEndpointI)) + { + return false; + } + + if(this === p) + { + return true; + } + + if(this._type !== p._type) + { + return false; + } + + if(!this._rawEncoding.equals(p._rawEncoding)) + { + return false; + } + + if(this._rawBytes.length !== p._rawBytes.length) + { + return false; + } + for(var i = 0; i < this._rawBytes.length; i++) + { + if(this._rawBytes[i] !== p._rawBytes[i]) + { + return false; + } + } + + return true; + }, + compareTo: function(p) + { + if(this === p) + { + return 0; + } + + if(p === null) + { + return 1; + } + + if(!(p instanceof OpaqueEndpointI)) + { + return this.type() < p.type() ? -1 : 1; + } + + if(this._type < p._type) + { + return -1; + } + else if(p._type < this._type) + { + return 1; + } + + if(this._rawEncoding.major < p._rawEncoding.major) + { + return -1; + } + else if(p._rawEncoding.major < this._rawEncoding.major) + { + return 1; + } + + if(this._rawEncoding.minor < p._rawEncoding.minor) + { + return -1; + } + else if(p._rawEncoding.minor < this._rawEncoding.minor) + { + return 1; + } + + if(this._rawBytes.length < p._rawBytes.length) + { + return -1; + } + else if(p._rawBytes.length < this._rawBytes.length) + { + return 1; + } + for(var i = 0; i < this._rawBytes.length; i++) + { + if(this._rawBytes[i] < p._rawBytes[i]) + { + return -1; + } + else if(p._rawBytes[i] < this._rawBytes[i]) + { + return 1; + } + } + + return 0; + }, + calcHashValue: function() + { + var h = 5381; + h = HashUtil.addNumber(h, this._type); + h = HashUtil.addHashable(h, this._rawEncoding); + h = HashUtil.addArray(h, this._rawBytes, HashUtil.addNumber); + this._hashCode = h; + } + }); + + OpaqueEndpointI.fromString = function(str) + { + var result = new OpaqueEndpointI(); + + result._rawEncoding = Protocol.Encoding_1_0; + + var topt = 0; + var vopt = 0; + + var arr = str.split(/[ \t\n\r]+/); + var i = 0; + while(i < arr.length) + { + if(arr[i].length === 0) + { + i++; + continue; + } + + var option = arr[i++]; + if(option.length != 2 && option.charAt(0) != '-') + { + throw new Ice.EndpointParseException("expected an endpoint option but found `" + option + + "' in endpoint `opaque " + str + "'"); + } + + var argument = null; + if(i < arr.length && arr[i].charAt(0) != '-') + { + argument = arr[i++]; + } + + switch(option.charAt(1)) + { + case 't': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -t option in endpoint `opaque " + + str + "'"); + } + + var type; + + try + { + type = StringUtil.toInt(argument); + } + catch(ex) + { + throw new Ice.EndpointParseException("invalid type value `" + argument + + "' in endpoint `opaque " + str + "'"); + } + + if(type < 0 || type > 65535) + { + throw new Ice.EndpointParseException("type value `" + argument + + "' out of range in endpoint `opaque " + str + "'"); + } + + result._type = type; + ++topt; + if(topt > 1) + { + throw new Ice.EndpointParseException("multiple -t options in endpoint `opaque " + str + "'"); + } + break; + } + + case 'v': + { + if(argument === null || argument.length === 0) + { + throw new Ice.EndpointParseException("no argument provided for -v option in endpoint `opaque " + + str + "'"); + } + for(var j = 0; j < argument.length; ++j) + { + if(!Base64.isBase64(argument.charAt(j))) + { + throw new Ice.EndpointParseException("invalid base64 character `" + argument.charAt(j) + + "' (ordinal " + argument.charCodeAt(j) + + ") in endpoint `opaque " + str + "'"); + } + } + result._rawBytes = Base64.decode(argument); + ++vopt; + if(vopt > 1) + { + throw new Ice.EndpointParseException("multiple -v options in endpoint `opaque " + str + "'"); + } + break; + } + + case 'e': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -e option in endpoint `opaque " + + str + "'"); + } + try + { + result._rawEncoding = Ice.stringToEncodingVersion(argument); + } + catch(e) + { + throw new Ice.EndpointParseException("invalid encoding version `" + argument + + "' in endpoint `opaque " + str + "':\n" + e.str); + } + break; + } + + default: + { + throw new Ice.EndpointParseException("invalid option `" + option + "' in endpoint `opaque " + + str + "'"); + } + } + } + + if(topt != 1) + { + throw new Ice.EndpointParseException("no -t option in endpoint `opaque " + str + "'"); + } + if(vopt != 1) + { + throw new Ice.EndpointParseException("no -v option in endpoint `opaque " + str + "'"); + } + result.calcHashValue(); + return result; + }; + + OpaqueEndpointI.fromStream = function(type, s) + { + var result = new OpaqueEndpointI(); + result._type = type; + result._rawEncoding = s.startReadEncaps(); + var sz = s.getReadEncapsSize(); + result._rawBytes = s.readBlob(sz); + s.endReadEncaps(); + result.calcHashValue(); + return result; + }; + + Ice.OpaqueEndpointI = OpaqueEndpointI; + + var OpaqueEndpointInfoI = Class(Ice.OpaqueEndpointInfo, { + __init__: function(timeout, compress, rawEncoding, rawBytes, type) + { + Ice.OpaqueEndpointInfo.call(this, -1, false, rawEncoding, rawBytes); + this._type = type; + }, + type: function() + { + return this._type; + }, + datagram: function() + { + return false; + }, + secure: function() + { + return false; + } + }); + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Operation.js b/js/src/Ice/Operation.js new file mode 100644 index 00000000000..11a130f542f --- /dev/null +++ b/js/src/Ice/Operation.js @@ -0,0 +1,773 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Current"); + require("Ice/DispatchStatus"); + require("Ice/Exception"); + require("Ice/FormatType"); + require("Ice/Object"); + require("Ice/ObjectPrx"); + require("Ice/OptionalFormat"); + require("Ice/StreamHelpers"); + + var Ice = global.Ice || {}; + var Slice = global.Slice || {}; + + /*jshint -W069 */ + + var Class = Ice.Class; + + var builtinHelpers = + [ + Ice.ByteHelper, + Ice.BoolHelper, + Ice.ShortHelper, + Ice.IntHelper, + Ice.LongHelper, + Ice.FloatHelper, + Ice.DoubleHelper, + Ice.StringHelper, + Ice.Object, + Ice.ObjectPrx + ]; + + function parseParam(p) + { + var type = p[0]; + var t = typeof(type); + if(t === 'number') + { + type = builtinHelpers[p[0]]; + } + else if(t === 'string') + { + /*jshint -W061 */ + type = eval(type); + /*jshint +W061 */ + } + + return { + "type": type, + "isObject": (p[1] === true), + "tag": p[2] // Optional tag, which may not be present - an undefined tag means "not optional". + }; + } + + // + // Each operation descriptor is a property. The key is the "on-the-wire" + // name, and the value is an array consisting of the following elements: + // + // 0: native method name in case of a keyword conflict (e.g., "_while"), + // otherwise an empty string + // 1: mode (undefined == Normal or int) + // 2: sendMode (undefined == Normal or int) + // 3: amd (undefined or 1) + // 4: format (undefined == Default or int) + // 5: return type (undefined if void, or [type, tag]) + // 6: in params (undefined if none, or array of [type, tag]) + // 7: out params (undefined if none, or array of [type, tag]) + // 8: exceptions (undefined if none, or array of types) + // 9: sends classes (true or undefined) + // 10: returns classes (true or undefined) + // + function parseOperation(name, arr) + { + var r = {}; + var i; + var p; + + r["name"] = name; + r["mode"] = arr[1] ? Ice.OperationMode.valueOf(arr[1]) : Ice.OperationMode.Normal; + r["sendMode"] = arr[2] ? Ice.OperationMode.valueOf(arr[2]) : Ice.OperationMode.Normal; + r["amd"] = arr[3] ? true : false; + r["format"] = arr[4] ? Ice.FormatType.valueOf(arr[4]) : Ice.FormatType.DefaultFormat; + + if(r["amd"]) + { + r["servantMethod"] = name + "_async"; + } + else + { + r["servantMethod"] = arr[0] ? arr[0] : name; + } + + var ret; + if(arr[5]) + { + ret = parseParam(arr[5]); + ret["pos"] = 0; + } + r["returns"] = ret; + + var inParams = []; + var inParamsOpt = []; + if(arr[6]) + { + for(i = 0; i < arr[6].length; ++i) + { + p = parseParam(arr[6][i]); + p["pos"] = i; + inParams.push(p); + if(p.tag) + { + inParamsOpt.push(p); + } + } + } + inParamsOpt.sort(function(p1, p2) { return p1.tag - p2.tag; }); // Sort by tag. + r["inParams"] = inParams; + r["inParamsOpt"] = inParamsOpt; + + var outParams = []; + var outParamsOpt = []; + if(arr[7]) + { + var offs = ret ? 1 : 0; + for(i = 0; i < arr[7].length; ++i) + { + p = parseParam(arr[7][i]); + p["pos"] = i + offs; + outParams.push(p); + if(p.tag) + { + outParamsOpt.push(p); + } + } + } + if(ret && ret.tag) + { + outParamsOpt.push(ret); + } + outParamsOpt.sort(function(p1, p2) { return p1.tag - p2.tag; }); // Sort by tag. + r["outParams"] = outParams; + r["outParamsOpt"] = outParamsOpt; + + var exceptions = []; + if(arr[8]) + { + for(i = 0; i < arr[8].length; ++i) + { + exceptions.push(arr[8][i]); + } + } + r["exceptions"] = exceptions; + + r["sendsClasses"] = arr[9] === true; + r["returnsClasses"] = arr[10] === true; + + return r; + } + + var OpTable = Class({ + __init__: function(ops) + { + this.raw = ops; + this.parsed = {}; + }, + find: function(name) + { + // + // Check if we've already parsed the operation. + // + var op = this.parsed[name]; + if(op === undefined && this.raw[name] !== undefined) + { + // + // We haven't parsed it yet, but we found a match for the name, so parse it now. + // + op = parseOperation(name, this.raw[name]); + this.parsed[name] = op; + } + return op; + } + }); + + function unmarshalParams(is, retvalInfo, allParamInfo, optParamInfo, usesClasses, params, offset) + { + var i, p, v; + // + // First read all required params. + // + for(i = 0; i < allParamInfo.length; ++i) + { + p = allParamInfo[i]; + if(!p.tag) + { + v = p.type.read(is); + params[p.pos + offset] = v; + } + } + + // + // Then read a required return value (if any). + // + if(retvalInfo) + { + v = retvalInfo.type.read(is); + params[retvalInfo.pos + offset] = v; + } + + // + // Then read all optional params. + // + for(i = 0; i < optParamInfo.length; ++i) + { + p = optParamInfo[i]; + v = p.type.readOpt(is, p.tag); + params[p.pos + offset] = v; + } + + if(usesClasses) + { + is.readPendingObjects(); + } + } + + function marshalParams(os, params, retvalInfo, paramInfo, optParamInfo, usesClasses) + { + var i, p; + + // + // Write the required params. + // + for(i = 0; i < paramInfo.length; ++i) + { + p = paramInfo[i]; + if(!p.tag) + { + p.type.write(os, params[p.pos]); + } + } + + // + // retvalInfo should only be provided if there is a non-void required return value. + // + if(retvalInfo) + { + retvalInfo.type.write(os, params[retvalInfo.pos]); + } + + // + // Write the optional params. + // + for(i = 0; i < optParamInfo.length; ++i) + { + p = optParamInfo[i]; + p.type.writeOpt(os, p.tag, params[p.pos]); + } + + if(usesClasses) + { + os.writePendingObjects(); + } + } + + var Upcall = Class({ + __init__: function(incomingAsync, op) + { + this.incomingAsync = incomingAsync; + this.op = op; + }, + ice_response: function() + { + var args = arguments; + + if(this.incomingAsync.__validateResponse(true)) + { + try + { + this.__sendResponse(args); + this.incomingAsync.__response(); + } + catch(ex) + { + this.incomingAsync.__exception(ex); + } + } + }, + ice_exception: function(ex) + { + if(this.__checkException(ex)) + { + if(this.incomingAsync.__validateResponse(false)) + { + this.__sendException(ex); + this.incomingAsync.__response(); + } + } + else + { + this.incomingAsync.ice_exception(ex); + } + }, + __sendResponse: function(results) + { + if(this.op.returns === undefined && this.op.outParams.length === 0) + { + if(results && results.length > 0) + { + // + // No results expected. + // + throw new Error("ice_response called with invalid arguments"); + } + else + { + this.incomingAsync.__writeEmptyParams(); + } + } + else + { + var __os = this.incomingAsync.__startWriteParams(this.op.format); + var retvalInfo; + if(this.op.returns && !this.op.returns.tag) + { + retvalInfo = this.op.returns; + } + marshalParams(__os, results, retvalInfo, this.op.outParams, this.op.outParamsOpt, + this.op.returnsClasses); + this.incomingAsync.__endWriteParams(true); + } + }, + __checkException: function(ex) + { + // + // Make sure the given exception is an instance of one of the declared user exceptions + // for this operation. + // + for(var i = 0; i < this.op.exceptions.length; ++i) + { + if(ex instanceof this.op.exceptions[i]) + { + // + // User exception is valid. + // + return true; + } + } + + return false; + }, + __sendException: function(ex) + { + // + // User exception is valid, now marshal it. + // + this.incomingAsync.__writeUserException(ex, this.op.format); + } + }); + + var __dispatchImpl = function(servant, op, incomingAsync, current) + { + // + // Check to make sure the servant implements the operation. + // + var method = servant[op.servantMethod]; + if(method === undefined || typeof(method) !== "function") + { + var comm = current.adapter.getCommunicator(); + var msg = "servant for identity " + comm.identityToString(current.id) + + " does not define operation `" + op.servantMethod + "'"; + console.log(msg); + throw new Ice.UnknownException(msg); + } + + var up = new Upcall(incomingAsync, op); + + try + { + // + // Unmarshal the in params (if any). + // + var params = op.amd ? [null] : []; + if(op.inParams.length === 0) + { + incomingAsync.readEmptyParams(); + } + else + { + var __is = incomingAsync.startReadParams(); + var offset = op.amd ? 1 : 0; + unmarshalParams(__is, undefined, op.inParams, op.inParamsOpt, op.sendsClasses, params, offset); + incomingAsync.endReadParams(); + + // + // When unmarshaling objects, the ObjectHelper returns a wrapper object + // and eventually stores the unmarshaled object into its "value" member. + // Here we scan the parameter array and replace the wrappers with the + // actual object references. + // + if(op.inParams.length > 0 && (op.sendsClasses || op.inParamsOpt.length > 0)) + { + for(var i = 0; i < op.inParams.length; ++i) + { + var p = op.inParams[i]; + // + // Optional parameters may be undefined. + // + if(p.isObject && params[p.pos + offset] !== undefined) + { + params[p.pos + offset] = params[p.pos + offset].value; + } + } + } + } + + params.push(current); + + if(op.amd) + { + params[0] = up; // The AMD callback object. + try + { + method.apply(servant, params); + } + catch(ex) + { + up.ice_exception(ex); + } + return Ice.DispatchStatus.DispatchAsync; + } + else + { + // + // Determine how many out parameters to expect. + // + var numExpectedResults = op.outParams.length; + if(op.returns) + { + ++numExpectedResults; + } + + var results = method.apply(servant, params); + + // + // Complain if we expect more than out parameter and the servant doesn't return an array. + // + if(numExpectedResults > 1 && !(results instanceof Array)) + { + var msg = "operation `" + op.servantMethod + "' should return an array of length " + numParams; + console.log(msg); + throw new Ice.MarshalException(msg); + } + else if(numExpectedResults === 1) + { + // + // Wrap a single out parameter in an array. + // + results = [results]; + } + + up.__sendResponse(results); + return Ice.DispatchStatus.DispatchOK; + } + } + catch(ex) + { + if(up.__checkException(ex)) + { + up.__sendException(ex); + return Ice.DispatchStatus.DispatchUserException; + } + else + { + throw ex; + } + } + }; + + function getServantMethodFromInterfaces(interfaces, methodName, all) + { + var method; + for(var i = 0; method === undefined && i < interfaces.length; ++i) + { + var intf = interfaces[i]; + method = intf[methodName]; + if(method === undefined) + { + if(all.indexOf(intf) === -1) + { + all.push(intf); + } + if(intf.__implements) + { + method = getServantMethodFromInterfaces(intf.__implements, methodName, all); + } + } + } + return method; + } + + var dispatchPrefix = "__op_"; + + function getServantMethod(servantType, name) + { + // + // The dispatch method is named __op_<Slice name> and is stored in the type (not the prototype). + // + var methodName = dispatchPrefix + name; + + // + // First check the servant type. + // + var method = servantType[methodName]; + + var allInterfaces; + + if(method === undefined) + { + allInterfaces = []; + + // + // Now check the prototypes of the implemented interfaces. + // + var curr = servantType; + while(curr && method === undefined) + { + if(curr.__implements) + { + method = getServantMethodFromInterfaces(curr.__implements, methodName, allInterfaces); + } + curr = curr.__parent; + } + + if(method !== undefined) + { + // + // Add the method to the servant's type. + // + servantType[methodName] = method; + } + } + + if(method === undefined) + { + // + // Next check the op table for the servant's type. + // + var op; + if(servantType.__ops) + { + op = servantType.__ops.find(name); + } + + var source; + if(op === undefined) + { + // + // Now check the op tables of the base types. + // + var parent = servantType.__parent; + while(op === undefined && parent) + { + if(parent.__ops) + { + if((op = parent.__ops.find(name)) !== undefined) + { + source = parent; + } + } + parent = parent.__parent; + } + + // + // Now check the op tables of all base interfaces. + // + for(var i = 0; op === undefined && i < allInterfaces.length; ++i) + { + var intf = allInterfaces[i]; + if(intf.__ops) + { + if((op = intf.__ops.find(name)) !== undefined) + { + source = intf; + } + } + } + } + + if(op !== undefined) + { + method = function(servant, incomingAsync, current) + { + return __dispatchImpl(servant, op, incomingAsync, current); + }; + + // + // Add the method to the servant type. + // + servantType[methodName] = method; + + // + // Also add the method to the type in which the operation was found. + // + if(source) + { + source[methodName] = method; + } + } + } + + return method; + } + + function addProxyOperation(proxyType, name, data) + { + var method = data[0] ? data[0] : name; + + var op = null; + + proxyType.prototype[method] = function() + { + var args = arguments; + + // + // Parse the operation data on the first invocation of a proxy method. + // + if(op === null) + { + op = parseOperation(name, data); + } + + var ctx = args[op.inParams.length]; // The request context is the last argument (if present). + + var marshalFn = null; + if(op.inParams.length > 0) + { + marshalFn = function(os, params) + { + marshalParams(os, params, undefined, op.inParams, op.inParamsOpt, op.sendsClasses); + }; + } + + var unmarshalFn = null; + if(op.returns || op.outParams.length > 0) + { + unmarshalFn = function(asyncResult) + { + // + // The results array holds the out parameters in the following format: + // + // [retval, out1, out2, ..., asyncResult] + // + var results = []; + + var is = asyncResult.__startReadParams(); + + var retvalInfo; + if(op.returns && !op.returns.tag) + { + retvalInfo = op.returns; + } + unmarshalParams(is, retvalInfo, op.outParams, op.outParamsOpt, op.returnsClasses, results, 0); + + asyncResult.__endReadParams(); + + // + // When unmarshaling objects, the ObjectHelper returns a wrapper object + // and eventually stores the unmarshaled object into its "value" member. + // Here we scan the results array and replace the wrappers with the + // actual object references. + // + if(op.returnsClasses || op.outParamsOpt.length > 0) + { + var offset = 0; // Skip asyncResult in results. + if(op.returns && op.returns.isObject && results[op.returns.pos + offset] !== undefined) + { + results[op.returns.pos + offset] = results[op.returns.pos + offset].value; + } + for(var i = 0; i < op.outParams.length; ++i) + { + var p = op.outParams[i]; + // + // Optional parameters may be undefined. + // + if(p.isObject && results[p.pos + offset] !== undefined) + { + results[p.pos + offset] = results[p.pos + offset].value; + } + } + } + results.push(asyncResult); + return results; + }; + } + + return Ice.ObjectPrx.__invoke(this, op.name, op.sendMode, op.format, ctx, marshalFn, unmarshalFn, + op.exceptions, Array.prototype.slice.call(args)); + }; + } + + Slice.defineOperations = function(classType, proxyType, ops) + { + if(ops) + { + classType.__ops = new OpTable(ops); + } + + classType.prototype.__dispatch = function(incomingAsync, current) + { + // + // Retrieve the dispatch method for this operation. + // + var method = getServantMethod(classType, current.operation); + + if(method === undefined || typeof(method) !== 'function') + { + throw new Ice.OperationNotExistException(current.id, current.facet, current.operation); + } + + return method.call(method, this, incomingAsync, current); + }; + + if(ops) + { + for(var name in ops) + { + addProxyOperation(proxyType, name, ops[name]); + } + } + + // + // Copy proxy methods from super-interfaces. + // + if(proxyType.__implements) + { + for(var intf in proxyType.__implements) + { + var proto = proxyType.__implements[intf].prototype; + for(var f in proto) + { + if(typeof proto[f] == "function" && proxyType.prototype[f] === undefined) + { + proxyType.prototype[f] = proto[f]; + } + } + } + } + }; + + // + // Define the "built-in" operations for all Ice objects. + // + Slice.defineOperations(Ice.Object, Ice.ObjectPrx, + { + "ice_ping": [, 1, 1, , , , , , ], + "ice_isA": [, 1, 1, , , [1], [[7]], , ], + "ice_id": [, 1, 1, , , [7], , , ], + "ice_ids": [, 1, 1, , , ["Ice.StringSeqHelper"], , , ] + }); + + global.Slice = Slice; + global.Ice = Ice; + + /*jshint +W069 */ +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/OptionalFormat.js b/js/src/Ice/OptionalFormat.js new file mode 100644 index 00000000000..6e3f866ee66 --- /dev/null +++ b/js/src/Ice/OptionalFormat.js @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/EnumBase"); + + var Ice = global.Ice || {}; + + Ice.OptionalFormat = Slice.defineEnum( + {'F1':0, 'F2':1, 'F4':2, 'F8':3, 'Size':4, 'VSize':5, 'FSize':6, 'Class':7}); + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/OutgoingAsync.js b/js/src/Ice/OutgoingAsync.js new file mode 100644 index 00000000000..ec72d35e436 --- /dev/null +++ b/js/src/Ice/OutgoingAsync.js @@ -0,0 +1,572 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_FOR_ACTIONSCRIPT_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncStatus"); + require("Ice/AsyncResult"); + require("Ice/BasicStream"); + require("Ice/Debug"); + require("Ice/HashMap"); + require("Ice/LocalExceptionWrapper"); + require("Ice/Current"); + require("Ice/Protocol"); + require("Ice/BuiltinSequences"); + require("Ice/Exception"); + require("Ice/LocalException"); + require("Ice/Identity"); + + var Ice = global.Ice || {}; + + var AsyncStatus = Ice.AsyncStatus; + var AsyncResult = Ice.AsyncResult; + var BasicStream = Ice.BasicStream; + var Debug = Ice.Debug; + var HashMap = Ice.HashMap; + var LocalExceptionWrapper = Ice.LocalExceptionWrapper; + var OperationMode = Ice.OperationMode; + var Protocol = Ice.Protocol; + var Identity = Ice.Identity; + + var OutgoingAsync = Ice.Class(AsyncResult, { + __init__: function(prx, operation, completed, sent) + { + // + // OutgoingAsync can be constructed by a sub-type's prototype, in which case the + // arguments are undefined. + // + if(prx !== undefined) + { + AsyncResult.call(this, prx.ice_getCommunicator(), operation, null, prx, null, completed, sent); + this._batch = this._proxy.ice_isBatchOneway() || this._proxy.ice_isBatchDatagram(); + + this._batchStarted = false; + + this._timerToken = -1; + this._timerConnection = null; + + this._handler = null; + this._encoding = Protocol.getCompatibleEncoding(this._proxy.__reference().getEncoding()); + this._cnt = 0; + this._mode = null; + } + else + { + AsyncResult.call(this); + } + }, + __prepare: function(op, mode, ctx) + { + this._handler = null; + this._cnt = 0; + this._mode = mode; + + Protocol.checkSupportedProtocol(Protocol.getCompatibleProtocol(this._proxy.__reference().getProtocol())); + + if(ctx === null) + { + ctx = OutgoingAsync._emptyContext; + } + + if(this._batch) + { + while(true) + { + try + { + this._handler = this._proxy.__getRequestHandler(); + this._handler.prepareBatchRequest(this._os); + this._batchStarted = true; + break; + } + catch(ex) + { + if(ex instanceof LocalExceptionWrapper) + { + this.handleExceptionWrapper(ex); + } + else if(ex instanceof Ice.LocalException) + { + this.handleException(ex, false); + } + else + { + throw ex; + } + } + } + } + else + { + this._os.writeBlob(Protocol.requestHdr); + } + + var ref = this._proxy.__reference(); + + ref.getIdentity().__write(this._os); + + // + // For compatibility with the old FacetPath. + // + var facet = ref.getFacet(); + if(facet === null || facet.length === 0) + { + Ice.StringSeqHelper.write(this._os, null); + } + else + { + Ice.StringSeqHelper.write(this._os, [ facet ]); + } + + this._os.writeString(this._operation); + + this._os.writeByte(mode.value); + + if(ctx !== undefined) + { + if(ctx !== null && !(ctx instanceof HashMap)) + { + throw new Error("illegal context value, expecting null or HashMap"); + } + + // + // Explicit context + // + Ice.ContextHelper.write(this._os, ctx); + } + else + { + // + // Implicit context + // + var implicitContext = ref.getInstance().getImplicitContext(); + var prxContext = ref.getContext(); + + if(implicitContext === null) + { + Ice.ContextHelper.write(this._os, prxContext); + } + else + { + implicitContext.write(prxContext, this._os); + } + } + }, + __sent: function(connection) + { + this._state |= AsyncResult.Sent; + + if((this._state & AsyncResult.Done) === 0) + { + if(!this._proxy.ice_isTwoway()) + { + this._state |= AsyncResult.Done | AsyncResult.OK; + this._os.resize(0); + if(this._sent) + { + this._sent.call(null, this); + } + else + { + this.succeed(this); + } + } + else if(connection.timeout() > 0) + { + Debug.assert(this._timerToken === -1); + this._timerConnection = connection; + var self = this; + this._timerToken = this._instance.timer().schedule( + function() { self.__runTimerTask(); }, connection.timeout()); + } + } + }, + __finishedEx: function(exc, sent) + { + Debug.assert((this._state & AsyncResult.Done) === 0); + if(this._timerConnection !== null) + { + this._instance.timer().cancel(this._timerToken); + this._timerConnection = null; + this._timerToken = -1; + } + + try + { + var interval = this.handleException(exc, sent); // This will throw if the invocation can't be retried. + if(interval > 0) + { + this._instance.retryQueue().add(this, interval); + } + else + { + this.__send(); + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.__exception(ex); + } + else + { + this.fail(ex, this); + } + } + }, + __finishedWrapper: function(exc) + { + // + // The LocalExceptionWrapper exception is only called before the invocation is sent. + // + + try + { + var interval = this.handleExceptionWrapper(exc); // This will throw if the invocation can't be retried. + if(interval > 0) + { + this._instance.retryQueue().add(this, interval); + } + else + { + this.__send(); + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.__exception(ex); + } + else + { + this.fail(ex, this); + } + } + }, + __finished: function(istr) + { + Debug.assert(this._proxy.ice_isTwoway()); // Can only be called for twoways. + + var replyStatus; + try + { + Debug.assert(this._exception === null && (this._state & AsyncResult.Done) === 0); + + if(this._timerConnection !== null) + { + Debug.assert(this._timerToken !== -1); + this._instance.timer().cancel(this._timerToken); + this._timerConnection = null; + this._timerToken = -1; + } + + if(this._is === null) // _is can already be initialized if the invocation is retried + { + this._is = new BasicStream(this._instance, Protocol.currentProtocolEncoding, false); + } + this._is.swap(istr); + replyStatus = this._is.readByte(); + + switch(replyStatus) + { + case Protocol.replyOK: + case Protocol.replyUserException: + { + break; + } + + case Protocol.replyObjectNotExist: + case Protocol.replyFacetNotExist: + case Protocol.replyOperationNotExist: + { + var id = new Identity(); + id.__read(this._is); + + // + // For compatibility with the old FacetPath. + // + var facetPath = Ice.StringSeqHelper.read(this._is); + var facet; + if(facetPath.length > 0) + { + if(facetPath.length > 1) + { + throw new Ice.MarshalException(); + } + facet = facetPath[0]; + } + else + { + facet = ""; + } + + var operation = this._is.readString(); + + var rfe = null; + switch(replyStatus) + { + case Protocol.replyObjectNotExist: + { + rfe = new Ice.ObjectNotExistException(); + break; + } + + case Protocol.replyFacetNotExist: + { + rfe = new Ice.FacetNotExistException(); + break; + } + + case Protocol.replyOperationNotExist: + { + rfe = new Ice.OperationNotExistException(); + break; + } + + default: + { + Debug.assert(false); + break; + } + } + + rfe.id = id; + rfe.facet = facet; + rfe.operation = operation; + throw rfe; + } + + case Protocol.replyUnknownException: + case Protocol.replyUnknownLocalException: + case Protocol.replyUnknownUserException: + { + var unknown = this._is.readString(); + + var ue = null; + switch(replyStatus) + { + case Protocol.replyUnknownException: + { + ue = new Ice.UnknownException(); + break; + } + + case Protocol.replyUnknownLocalException: + { + ue = new Ice.UnknownLocalException(); + break; + } + + case Protocol.replyUnknownUserException: + { + ue = new Ice.UnknownUserException(); + break; + } + + default: + { + Debug.assert(false); + break; + } + } + + ue.unknown = unknown; + throw ue; + } + + default: + { + throw new Ice.UnknownReplyStatusException(); + } + } + + this._state |= AsyncResult.Done; + if(replyStatus == Protocol.replyOK) + { + this._state |= AsyncResult.OK; + } + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + this.__finishedEx(ex, true); + return; + } + else + { + this.fail(ex, this); + return; + } + } + + Debug.assert(replyStatus === Protocol.replyOK || replyStatus === Protocol.replyUserException); + this.__response(); + }, + __send: function() + { + if(this._batch) + { + Debug.assert(this._handler !== null); + this._handler.finishBatchRequest(this._os); + this.succeed(this); + } + else + { + while(true) + { + var interval = 0; + try + { + this._handler = this._proxy.__getRequestHandler(); + this._handler.sendAsyncRequest(this); + break; + } + catch(ex) + { + if(ex instanceof LocalExceptionWrapper) + { + interval = this.handleExceptionWrapper(ex); + } + else if(ex instanceof Ice.LocalException) + { + interval = this.handleException(ex, false); + } + else + { + throw ex; + } + } + + if(interval > 0) + { + this._instance.retryQueue().add(this, interval); + return false; + } + } + } + }, + __startWriteParams: function(format) + { + this._os.startWriteEncaps(this._encoding, format); + return this._os; + }, + __endWriteParams: function() + { + this._os.endWriteEncaps(); + }, + __writeEmptyParams: function() + { + this._os.writeEmptyEncaps(this._encoding); + }, + __writeParamEncaps: function(encaps) + { + if(encaps === null || encaps.length === 0) + { + this._os.writeEmptyEncaps(this._encoding); + } + else + { + this._os.writeEncaps(encaps); + } + }, + __exception: function(ex) + { + AsyncResult.prototype.__exception.call(this, ex); + + if(this._batchStarted) + { + Debug.assert(this._handler !== null); + this._handler.abortBatchRequest(); + } + }, + handleException: function(exc, sent) + { + var interval = { value: 0 }; + try + { + // + // A CloseConnectionException indicates graceful server shutdown, and is therefore + // always repeatable without violating "at-most-once". That's because by sending a + // close connection message, the server guarantees that all outstanding requests + // can safely be repeated. + // + // An ObjectNotExistException can always be retried as well without violating + // "at-most-once" (see the implementation of the checkRetryAfterException method of + // the ProxyFactory class for the reasons why it can be useful). + // + if(!sent || exc instanceof Ice.CloseConnectionException || exc instanceof Ice.ObjectNotExistException) + { + throw exc; + } + + // + // Throw the exception wrapped in a LocalExceptionWrapper, to indicate that the + // request cannot be resent without potentially violating the "at-most-once" + // principle. + // + throw new LocalExceptionWrapper(exc, false); + } + catch(ex) + { + if(ex instanceof LocalExceptionWrapper) + { + if(this._mode === OperationMode.Nonmutating || this._mode === OperationMode.Idempotent) + { + this._cnt = this._proxy.__handleExceptionWrapperRelaxed(this._handler, ex, interval, this._cnt); + } + else + { + this._proxy.__handleExceptionWrapper(this._handler, ex); + } + } + else if(ex instanceof Ice.LocalException) + { + this._cnt = this._proxy.__handleException(this._handler, ex, interval, this._cnt); + } + else + { + throw ex; + } + } + return interval.value; + }, + handleExceptionWrapper: function(ex) + { + var interval = { value: 0 }; + if(this._mode === OperationMode.Nonmutating || this._mode === OperationMode.Idempotent) + { + this._cnt = this._proxy.__handleExceptionWrapperRelaxed(this._handler, ex, interval, this._cnt); + } + else + { + this._proxy.__handleExceptionWrapper(this._handler, ex); + } + return interval.value; + }, + __runTimerTask: function() + { + var connection = this._timerConnection; + this._timerConnection = null; + this._timerToken = -1; + + if(connection !== null) + { + connection.exception(new Ice.TimeoutException()); + } + } + }); + + OutgoingAsync._emptyContext = new HashMap(); + + Ice.OutgoingAsync = OutgoingAsync; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/OutgoingConnectionFactory.js b/js/src/Ice/OutgoingConnectionFactory.js new file mode 100644 index 00000000000..6399b6ea5b4 --- /dev/null +++ b/js/src/Ice/OutgoingConnectionFactory.js @@ -0,0 +1,956 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/ArrayUtil"); + require("Ice/AsyncResultBase"); + require("Ice/ConnectionI"); + require("Ice/ConnectionReaper"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/HashMap"); + require("Ice/Promise"); + require("Ice/EndpointTypes"); + require("Ice/LocalException"); + require("Ice/Exception"); + + var Ice = global.Ice || {}; + + var ArrayUtil = Ice.ArrayUtil; + var AsyncResultBase = Ice.AsyncResultBase; + var ConnectionI = Ice.ConnectionI; + var ConnectionReaper = Ice.ConnectionReaper; + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var HashMap = Ice.HashMap; + var Promise = Ice.Promise; + var EndpointSelectionType = Ice.EndpointSelectionType; + + var Class = Ice.Class; + // + // Only for use by Instance. + // + var OutgoingConnectionFactory = Class({ + __init__: function(communicator, instance) + { + this._communicator = communicator; + this._instance = instance; + this._destroyed = false; + + this._reaper = new ConnectionReaper(); + + this._connectionsByEndpoint = new ConnectionListMap(); // map<EndpointI, Array<Ice.ConnectionI>> + this._pending = new HashMap(); // map<EndpointI, Array<ConnectCallback>> + this._pending.keyComparator = HashMap.compareEquals; + this._pendingConnectCount = 0; + + this._waitPromise = null; + }, + destroy: function() + { + if(this._destroyed) + { + return; + } + + this._connectionsByEndpoint.forEach(function(connection) + { + connection.destroy(ConnectionI.CommunicatorDestroyed); + }); + + this._destroyed = true; + this._communicator = null; + this.checkFinished(); + }, + waitUntilFinished: function() + { + this._waitPromise = new Promise(); + this.checkFinished(); + return this._waitPromise; + }, + // + // Returns a promise, success callback receives (connection, compress) + // + create: function(endpts, hasMore, selType) + { + Debug.assert(endpts.length > 0); + + // + // Apply the overrides. + // + var endpoints = this.applyOverrides(endpts); + + // + // Try to find a connection to one of the given endpoints. + // + try + { + var compress = { value: false }; + var connection = this.findConnectionByEndpoint(endpoints, compress); + if(connection !== null) + { + return new Promise().succeed(connection, compress.value); + } + } + catch(ex) + { + return new Promise().fail(ex); + } + + var cb = new ConnectCallback(this, endpoints, hasMore, selType); + return cb.start(); + }, + setRouterInfo: function(routerInfo) + { + var self = this; + return Ice.Promise.try( + function() + { + if(self._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + return routerInfo.getClientEndpoints(); + } + ).then( + function(endpoints) + { + // + // Search for connections to the router's client proxy + // endpoints, and update the object adapter for such + // connections, so that callbacks from the router can be + // received over such connections. + // + var adapter = routerInfo.getAdapter(); + var defaultsAndOverrides = self._instance.defaultsAndOverrides(); + for(var i = 0; i < endpoints.length; ++i) + { + var endpoint = endpoints[i]; + + // + // Modify endpoints with overrides. + // + if(defaultsAndOverrides.overrideTimeout) + { + endpoint = endpoint.changeTimeout(defaultsAndOverrides.overrideTimeoutValue); + } + + // + // The Connection object does not take the compression flag of + // endpoints into account, but instead gets the information + // about whether messages should be compressed or not from + // other sources. In order to allow connection sharing for + // endpoints that differ in the value of the compression flag + // only, we always set the compression flag to false here in + // this connection factory. + // + endpoint = endpoint.changeCompress(false); + + self._connectionsByEndpoint.forEach(function(connection) + { + if(connection.endpoint().equals(endpoint)) + { + connection.setAdapter(adapter); + } + }); + } + } + ); + }, + removeAdapter: function(adapter) + { + if(this._destroyed) + { + return; + } + this._connectionsByEndpoint.forEach(function(connection) + { + if(connection.getAdapter() === adapter) + { + connection.setAdapter(null); + } + }); + }, + flushAsyncBatchRequests: function() + { + var promise = new AsyncResultBase(this._communicator, "flushBatchRequests", null, null, null); + if(this._destroyed) + { + promise.succeed(); + return; + } + + Promise.all( + this._connectionsByEndpoint.map( + function(connection) + { + if(connection.isActiveOrHolding()) + { + return connection.flushBatchRequests().exception( + function(ex) + { + if(ex instanceof Ice.LocalException) + { + // Ignore + } + else + { + throw ex; + } + }); + } + }) + ).then( + function() + { + promise.succeed(promise); + }, + function(ex) + { + promise.fail(ex, promise); + } + ); + return promise; + }, + applyOverrides: function(endpts) + { + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + var endpoints = []; + for(var i = 0; i < endpts.length; ++i) + { + var endpoint = endpts[i]; + + // + // Modify endpoints with overrides. + // + if(defaultsAndOverrides.overrideTimeout) + { + endpoints.push(endpoint.changeTimeout(defaultsAndOverrides.overrideTimeoutValue)); + } + else + { + endpoints.push(endpoint); + } + } + + return endpoints; + }, + findConnectionByEndpoint: function(endpoints, compress) + { + if(this._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + Debug.assert(endpoints.length > 0); + + for(var i = 0; i < endpoints.length; ++i) + { + var endpoint = endpoints[i]; + + if(this._pending.has(endpoint)) + { + continue; + } + + var connectionList = this._connectionsByEndpoint.get(endpoint); + if(connectionList === undefined) + { + continue; + } + + for(var j = 0; j < connectionList.length; ++j) + { + if(connectionList[j].isActiveOrHolding()) // Don't return destroyed or un-validated connections + { + if(defaultsAndOverrides.overrideCompress) + { + compress.value = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress.value = endpoint.compress(); + } + return connectionList[j]; + } + } + } + + return null; + }, + incPendingConnectCount: function() + { + // + // Keep track of the number of pending connects. The outgoing connection factory + // waitUntilFinished() method waits for all the pending connects to terminate before + // to return. This ensures that the communicator client thread pool isn't destroyed + // too soon and will still be available to execute the ice_exception() callbacks for + // the asynchronous requests waiting on a connection to be established. + // + + if(this._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + ++this._pendingConnectCount; + }, + decPendingConnectCount: function() + { + --this._pendingConnectCount; + Debug.assert(this._pendingConnectCount >= 0); + if(this._destroyed && this._pendingConnectCount === 0) + { + this.checkFinished(); + } + }, + getConnection: function(endpoints, cb, compress) + { + if(this._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // Reap closed connections + // + var cons = this._reaper.swapConnections(); + if(cons !== null) + { + for(var i = 0; i < cons.length; ++i) + { + var c = cons[i]; + this._connectionsByEndpoint.removeConnection(c.endpoint(), c); + this._connectionsByEndpoint.removeConnection(c.endpoint().changeCompress(true), c); + } + } + + // + // Try to get the connection. + // + while(true) + { + if(this._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // Search for a matching connection. If we find one, we're done. + // + var connection = this.findConnectionByEndpoint(endpoints, compress); + if(connection !== null) + { + return connection; + } + + if(this.addToPending(cb, endpoints)) + { + // + // A connection is already pending. + // + return null; + } + else + { + // + // No connection is currently pending to one of our endpoints, so we + // get out of this loop and start the connection establishment to one of the + // given endpoints. + // + break; + } + } + + // + // At this point, we're responsible for establishing the connection to one of + // the given endpoints. If it's a non-blocking connect, calling nextEndpoint + // will start the connection establishment. Otherwise, we return null to get + // the caller to establish the connection. + // + cb.nextEndpoint(); + + return null; + }, + createConnection: function(transceiver, endpoint) + { + Debug.assert(this._pending.has(endpoint) && transceiver !== null); + + // + // Create and add the connection to the connection map. Adding the connection to the map + // is necessary to support the interruption of the connection initialization and validation + // in case the communicator is destroyed. + // + var connection = null; + try + { + if(this._destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + connection = new ConnectionI(this._communicator, this._instance, this._reaper, transceiver, + endpoint.changeCompress(false), false, null); + } + catch(ex) + { + if(ex instanceof Ice.LocalException) + { + try + { + transceiver.close(); + } + catch(exc) + { + // Ignore + } + } + throw ex; + } + + this._connectionsByEndpoint.set(connection.endpoint(), connection); + this._connectionsByEndpoint.set(connection.endpoint().changeCompress(true), connection); + return connection; + }, + finishGetConnection: function(endpoints, endpoint, connection, cb) + { + // cb is-a ConnectCallback + + var connectionCallbacks = []; + if(cb !== null) + { + connectionCallbacks.push(cb); + } + + var i; + var cc; + var callbacks = []; + for(i = 0; i < endpoints.length; ++i) + { + var endpt = endpoints[i]; + var cbs = this._pending.get(endpt); + if(cbs !== undefined) + { + this._pending.delete(endpt); + for(var j = 0; j < cbs.length; ++j) + { + cc = cbs[j]; + if(cc.hasEndpoint(endpoint)) + { + if(connectionCallbacks.indexOf(cc) === -1) + { + connectionCallbacks.push(cc); + } + } + else + { + if(callbacks.indexOf(cc) === -1) + { + callbacks.push(cc); + } + } + } + } + } + + for(i = 0; i < connectionCallbacks.length; ++i) + { + cc = connectionCallbacks[i]; + cc.removeFromPending(); + var idx = callbacks.indexOf(cc); + if(idx !== -1) + { + callbacks.splice(idx, 1); + } + } + for(i = 0; i < callbacks.length; ++i) + { + cc = callbacks[i]; + cc.removeFromPending(); + } + + var compress; + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress = endpoint.compress(); + } + + for(i = 0; i < callbacks.length; ++i) + { + cc = callbacks[i]; + cc.getConnection(); + } + for(i = 0; i < connectionCallbacks.length; ++i) + { + cc = connectionCallbacks[i]; + cc.setConnection(connection, compress); + } + + this.checkFinished(); + }, + finishGetConnectionEx: function(endpoints, ex, cb) + { + // cb is-a ConnectCallback + + var failedCallbacks = []; + if(cb !== null) + { + failedCallbacks.push(cb); + } + var i; + var cc; + var callbacks = []; + for(i = 0; i < endpoints.length; ++i) + { + var endpt = endpoints[i]; + var cbs = this._pending.get(endpt); + if(cbs !== undefined) + { + this._pending.delete(endpt); + for(var j = 0; j < cbs.length; ++j) + { + cc = cbs[j]; + if(cc.removeEndpoints(endpoints)) + { + if(failedCallbacks.indexOf(cc) === -1) + { + failedCallbacks.push(cc); + } + } + else + { + if(callbacks.indexOf(cc) === -1) + { + callbacks.push(cc); + } + } + } + } + } + + for(i = 0; i < callbacks.length; ++i) + { + cc = callbacks[i]; + Debug.assert(failedCallbacks.indexOf(cc) === -1); + cc.removeFromPending(); + } + this.checkFinished(); + + for(i = 0; i < callbacks.length; ++i) + { + cc = callbacks[i]; + cc.getConnection(); + } + for(i = 0; i < failedCallbacks.length; ++i) + { + cc = failedCallbacks[i]; + cc.setException(ex); + } + }, + addToPending: function(cb, endpoints) + { + // cb is-a ConnectCallback + + // + // Add the callback to each pending list. + // + var found = false; + var p; + var i; + if(cb !== null) + { + for(i = 0; i < endpoints.length; ++i) + { + p = endpoints[i]; + var cbs = this._pending.get(p); + if(cbs !== undefined) + { + found = true; + if(cbs.indexOf(cb) === -1) + { + cbs.push(cb); // Add the callback to each pending endpoint. + } + } + } + } + + if(found) + { + return true; + } + + // + // If there's no pending connection for the given endpoints, we're + // responsible for its establishment. We add empty pending lists, + // other callbacks to the same endpoints will be queued. + // + for(i = 0; i < endpoints.length; ++i) + { + p = endpoints[i]; + if(!this._pending.has(p)) + { + this._pending.set(p, []); + } + } + + return false; + }, + removeFromPending: function(cb, endpoints) + { + // cb is-a ConnectCallback + + for(var i = 0; i < endpoints.length; ++i) + { + var p = endpoints[i]; + var cbs = this._pending.get(p); + if(cbs !== undefined) + { + var idx = cbs.indexOf(cb); + if(idx !== -1) + { + cbs.splice(idx, 1); + } + } + } + }, + handleConnectionException: function(ex, hasMore) + { + var traceLevels = this._instance.traceLevels(); + if(traceLevels.retry >= 2) + { + var s = []; + s.push("connection to endpoint failed"); + if(ex instanceof Ice.CommunicatorDestroyedException) + { + s.push("\n"); + } + else + { + if(hasMore) + { + s.push(", trying next endpoint\n"); + } + else + { + s.push(" and no more endpoints to try\n"); + } + } + s.push(ExUtil.toString(ex)); + this._instance.initializationData().logger.trace(traceLevels.retryCat, s.join("")); + } + }, + handleException: function(ex, hasMore) + { + var traceLevels = this._instance.traceLevels(); + if(traceLevels.retry >= 2) + { + var s = []; + s.push("couldn't resolve endpoint host"); + if(ex instanceof Ice.CommunicatorDestroyedException) + { + s.push("\n"); + } + else + { + if(hasMore) + { + s.push(", trying next endpoint\n"); + } + else + { + s.push(" and no more endpoints to try\n"); + } + } + s.push(ExUtil.toString(ex)); + this._instance.initializationData().logger.trace(traceLevels.retryCat, s.join("")); + } + }, + checkFinished: function() + { + // + // Can't continue until the factory is destroyed and there are no pending connections. + // + if(!this._waitPromise || !this._destroyed || this._pending.size > 0 || this._pendingConnectCount > 0) + { + return; + } + + var self = this; + Promise.all( + self._connectionsByEndpoint.map( + function(connection) + { + return connection.waitUntilFinished().exception(function(ex) + { + Debug.assert(false); + }); + } + ) + ).then( + function() + { + var cons = self._reaper.swapConnections(); + if(cons !== null) + { + var arr = []; + for(var e = self._connectionsByEndpoint.entries; e !== null; e = e.next) + { + var connectionList = e.value; + for(var i = 0; i < connectionList.length; ++i) + { + if(arr.indexOf(connectionList[i]) === -1) + { + arr.push(connectionList[i]); + } + } + } + Debug.assert(cons.length === arr.length); + self._connectionsByEndpoint.clear(); + } + else + { + Debug.assert(self._connectionsByEndpoint.size === 0); + } + + Debug.assert(self._waitPromise !== null); + self._waitPromise.succeed(); + } + ); + } + }); + + Ice.OutgoingConnectionFactory = OutgoingConnectionFactory; + global.Ice = Ice; + + // + // Value is a Vector<Ice.ConnectionI> + // + var ConnectionListMap = Class(HashMap, { + __init__: function(h) + { + HashMap.call(this, h); + this.keyComparator = HashMap.compareEquals; + }, + set: function(key, value) + { + var list = this.get(key); + if(list === undefined) + { + list = []; + HashMap.prototype.set.call(this, key, list); + } + Debug.assert(value instanceof ConnectionI); + list.push(value); + return undefined; + }, + removeConnection: function(key, conn) + { + var list = this.get(key); + Debug.assert(list !== null); + var idx = list.indexOf(conn); + Debug.assert(idx !== -1); + list.splice(idx, 1); + if(list.length === 0) + { + this.delete(key); + } + }, + map: function(fn) + { + var arr = []; + this.forEach(function(c) { arr.push(fn(c)); }); + return arr; + }, + forEach: function(fn) + { + for(var e = this._head; e !== null; e = e._next) + { + for(var i = 0; i < e.value.length; ++i) + { + fn(e.value[i]); + } + } + } + }); + + var ConnectCallback = Class({ + __init__: function(f, endpoints, more, selType) + { + this._factory = f; + this._endpoints = endpoints; + this._hasMore = more; + this._selType = selType; + this._promise = new Promise(); + this._index = 0; + this._current = null; + }, + // + // Methods from ConnectionI_StartCallback + // + connectionStartCompleted: function(connection) + { + connection.activate(); + this._factory.finishGetConnection(this._endpoints, this._current, connection, this); + }, + connectionStartFailed: function(connection, ex) + { + Debug.assert(this._current !== null); + + if(ex instanceof Ice.LocalException) + { + this._factory.handleConnectionException(ex, this._hasMore || this._index < this._endpoints.length); + if(ex instanceof Ice.CommunicatorDestroyedException) // No need to continue. + { + this._factory.finishGetConnectionEx(this._endpoints, ex, this); + } + else if(this._index < this._endpoints.length) // Try the next endpoint. + { + this.nextEndpoint(); + } + else + { + this._factory.finishGetConnectionEx(this._endpoints, ex, this); + } + } + else + { + this._factory.finishGetConnectionEx(this._endpoints, ex, this); + } + }, + setConnection: function(connection, compress) + { + // + // Callback from the factory: the connection to one of the callback + // connectors has been established. + // + this._promise.succeed(connection, compress); + this._factory.decPendingConnectCount(); // Must be called last. + }, + setException: function(ex) + { + // + // Callback from the factory: connection establishment failed. + // + this._promise.fail(ex); + this._factory.decPendingConnectCount(); // Must be called last. + }, + hasEndpoint: function(endpt) + { + return this.findEndpoint(endpt) !== -1; + }, + findEndpoint: function(endpt) + { + for(var index = 0; index < this._endpoints.length; ++index) + { + if(endpt.equals(this._endpoints[index])) + { + return index; + } + } + return -1; + }, + removeEndpoints: function(endpoints) + { + for(var i = 0; i < endpoints.length; ++i) + { + var idx = this.findEndpoint(endpoints[i]); + if(idx !== -1) + { + this._endpoints.splice(idx, 1); + } + } + this._index = 0; + return this._endpoints.length === 0; + }, + removeFromPending: function() + { + this._factory.removeFromPending(this, this._endpoints); + }, + start: function() + { + try + { + // + // Notify the factory that there's an async connect pending. This is necessary + // to prevent the outgoing connection factory to be destroyed before all the + // pending asynchronous connects are finished. + // + this._factory.incPendingConnectCount(); + } + catch(ex) + { + this._promise.fail(ex); + return; + } + + this.getConnection(); + return this._promise; + }, + getConnection: function() + { + try + { + // + // Ask the factory to get a connection. + // + var compress = { value: false }; + var connection = this._factory.getConnection(this._endpoints, this, compress); + if(connection === null) + { + // + // A null return value from getConnection indicates that the connection + // is being established and that everthing has been done to ensure that + // the callback will be notified when the connection establishment is + // done. + // + return; + } + + this._promise.succeed(connection, compress.value); + this._factory.decPendingConnectCount(); // Must be called last. + } + catch(ex) + { + this._promise.fail(ex); + this._factory.decPendingConnectCount(); // Must be called last. + } + }, + nextEndpoint: function() + { + var connection = null; + try + { + Debug.assert(this._index < this._endpoints.length); + this._current = this._endpoints[this._index++]; + connection = this._factory.createConnection(this._current.connect(), this._current); + var self = this; + connection.start().then( + function() + { + self.connectionStartCompleted(connection); + }, + function(ex) + { + self.connectionStartFailed(connection, ex); + }); + } + catch(ex) + { + this.connectionStartFailed(connection, ex); + } + } + }); + +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ProcessLogger.js b/js/src/Ice/ProcessLogger.js new file mode 100644 index 00000000000..b414d183adc --- /dev/null +++ b/js/src/Ice/ProcessLogger.js @@ -0,0 +1,39 @@ + +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Logger"); + var Ice = global.Ice || {}; + var Logger = Ice.Logger; + + var processLogger = null; + + var getProcessLogger = function() + { + if(processLogger === null) + { + // + // TODO: Would be nice to be able to use process name as prefix by default. + // + processLogger = new Logger("", ""); + } + + return processLogger; + }; + + var setProcessLogger = function(logger) + { + processLogger = logger; + }; + + Ice.getProcessLogger = getProcessLogger; + Ice.setProcessLogger = setProcessLogger; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Promise.js b/js/src/Ice/Promise.js new file mode 100644 index 00000000000..91608d13873 --- /dev/null +++ b/js/src/Ice/Promise.js @@ -0,0 +1,297 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + + var Ice = global.Ice || {}; + + // + // Promise State + // + var State = {Pending: 0, Success: 1, Failed: 2}; + + var resolveImp = function(self, listener) + { + var callback = self.__state === State.Success ? listener.onResponse : listener.onException; + try + { + if(typeof callback !== "function") + { + listener.promise.setState(self.__state, self._args); + } + else + { + var result = callback.apply(null, self._args); + + // + // Callback can return a new promise. + // + if(result && typeof result.then == "function") + { + result.then( + function() + { + var args = arguments; + listener.promise.succeed.apply(listener.promise, args); + }, + function() + { + var args = arguments; + listener.promise.fail.apply(listener.promise, args); + }); + } + else + { + listener.promise.succeed(result); + } + } + } + catch(e) + { + listener.promise.fail.call(listener.promise, e); + } + }; + + var Promise = Ice.Class({ + __init__: function() + { + this.__state = State.Pending; + this.__listeners = []; + }, + then: function(onResponse, onException) + { + var promise = new Promise(); + var self = this; + // + // Use setTimeout so the listeners are not resolved until the call stack is empty. + // + setTimeout( + function() + { + self.__listeners.push( + { + promise:promise, + onResponse:onResponse, + onException:onException + }); + self.resolve(); + }, 0); + return promise; + }, + exception: function(onException) + { + return this.then(null, onException); + }, + finally: function(cb) + { + var p = new Promise(); + var self = this; + + var finallyHandler = function(method) + { + return function() + { + var args = arguments; + try + { + var result = cb.apply(null, args); + if(result && typeof result.then == "function") + { + var handler = function(){ method.apply(p, args); }; + result.then(handler).exception(handler); + } + else + { + method.apply(p, args); + } + } + catch(e) + { + method.apply(p, args); + } + }; + }; + + setTimeout( + function(){ + self.then(finallyHandler(p.succeed), finallyHandler(p.fail)); + }); + return p; + }, + delay: function(ms) + { + var p = new Promise(); + + var self = this; + + var delayHandler = function(promise, method) + { + return function() + { + var args = arguments; + setTimeout( + function() + { + method.apply(promise, args); + }, + ms); + }; + }; + + setTimeout( + function() + { + self.then(delayHandler(p, p.succeed), + delayHandler(p, p.fail)); + }); + + return p; + }, + resolve: function() + { + if(this.__state === State.Pending) + { + return; + } + + var obj; + while((obj = this.__listeners.pop())) + { + // + // We use a separate function here to capture the listeners + // in the loop. + // + resolveImp(this, obj); + } + }, + setState: function(state, args) + { + if(this.__state === State.Pending && state !== State.Pending) + { + this.__state = state; + this._args = args; + // + // Use setTimeout so the listeners are not resolved until the call stack is empty. + // + var self = this; + setTimeout(function(){ self.resolve(); }, 0); + } + }, + succeed: function() + { + var args = arguments; + this.setState(State.Success, args); + return this; + }, + fail: function() + { + var args = arguments; + this.setState(State.Failed, args); + return this; + }, + succeeded: function() + { + return this.__state === State.Success; + }, + failed: function() + { + return this.__state === State.Failed; + }, + completed: function() + { + return this.__state !== State.Pending; + } + }); + + // + // Create a new promise object that is fulfilled when all the promise arguments + // are fulfilled or is rejected when one of the promises is rejected. + // + Promise.all = function() + { + // If only one argument is provided, check if the argument is an array + if(arguments.length === 1 && arguments[0] instanceof Array) + { + return Promise.all.apply(this, arguments[0]); + } + + var promise = new Promise(); + var promises = Array.prototype.slice.call(arguments); + var results = new Array(arguments.length); + + var pending = promises.length; + if(pending === 0) + { + promise.succeed.apply(promise, results); + } + for(var i = 0; i < promises.length; ++i) + { + // + // Create an anonymous function to capture the loop index + // + + /*jshint -W083 */ + (function(j) + { + if(promises[j] && typeof promises[j].then == "function") + { + promises[j].then( + function() + { + results[j] = arguments; + pending--; + if(pending === 0) + { + promise.succeed.apply(promise, results); + } + }, + function() + { + promise.fail.apply(promise, arguments); + }); + } + else + { + results[j] = promises[j]; + pending--; + if(pending === 0) + { + promise.succeed.apply(promise, results); + } + } + }(i)); + /*jshint +W083 */ + } + return promise; + }; + + Promise.try = function(onResponse) + { + return new Promise().succeed().then(onResponse); + }; + + Promise.delay = function(ms) + { + if(arguments.length > 1) + { + var p = new Promise(); + var args = Array.prototype.slice.call(arguments); + ms = args.pop(); + return p.succeed.apply(p, args).delay(ms); + } + else + { + return new Promise().succeed().delay(ms); + } + }; + + Ice.Promise = Promise; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Properties.js b/js/src/Ice/Properties.js new file mode 100644 index 00000000000..3f63204a7fa --- /dev/null +++ b/js/src/Ice/Properties.js @@ -0,0 +1,504 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/StringUtil"); + require("Ice/HashMap"); + require("Ice/Promise"); + require("Ice/PropertyNames"); + require("Ice/Debug"); + require("Ice/ProcessLogger"); + require("Ice/ProcessLogger"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var StringUtil = Ice.StringUtil; + var HashMap = Ice.HashMap; + var Promise = Ice.Promise; + var PropertyNames = Ice.PropertyNames; + var Debug = Ice.Debug; + var ProcessLogger = Ice.ProcessLogger; + var getProcessLogger = Ice.getProcessLogger; + var InitializationException = Ice.InitializationException; + + var ParseStateKey = 0; + var ParseStateValue = 1; + // + // Ice.Properties + // + var Properties = Ice.Class({ + __init__: function(args, defaults) + { + this._properties = new HashMap(); + + if(defaults !== undefined && defaults !== null) + { + // + // NOTE: we can't just do a shallow copy of the map as the map values + // would otherwise be shared between the two PropertiesI object. + // + //_properties = new HashMap(pi._properties); + for(var e = defaults._properties.entries; e !== null; e = e.next) + { + this._properties.set(e.key, { 'value': e.value.value, 'used': false }); + } + } + + if(args !== undefined && args !== null) + { + var v = this.parseIceCommandLineOptions(args); + args.length = 0; + for(var i = 0; i < v.length; ++i) + { + args.push(v[i]); + } + } + }, + getProperty: function(key) + { + return this.getPropertyWithDefault(key, ""); + }, + getPropertyWithDefault: function(key, value) + { + var pv = this._properties.get(key); + if(pv !== undefined) + { + pv.used = true; + return pv.value; + } + else + { + return value; + } + }, + getPropertyAsInt: function(key) + { + return this.getPropertyAsIntWithDefault(key, 0); + }, + getPropertyAsIntWithDefault: function(key, value) + { + var pv = this._properties.get(key); + if(pv !== undefined) + { + pv.used = true; + return parseInt(pv.value); + } + else + { + return value; + } + }, + getPropertyAsList: function(key) + { + return this.getPropertyAsListWithDefault(key, 0); + }, + getPropertyAsListWithDefault: function(key, value) + { + if(value === undefined || value === null) + { + value = []; + } + + var pv = this._properties.get(key); + if(pv !== undefined) + { + pv.used = true; + + var result = StringUtil.splitString(pv.value, ", \t\r\n"); + if(result === null) + { + getProcessLogger().warning("mismatched quotes in property " + key + "'s value, returning default value"); + return value; + } + if(result.length === 0) + { + result = value; + } + return result; + } + else + { + return value; + } + }, + getPropertiesForPrefix: function(prefix) + { + var result = new HashMap(); + for(var e = this._properties.entries; e !== null; e = e.next) + { + if(prefix === undefined || prefix === null || e.key.indexOf(prefix) === 0) + { + e.value.used = true; + result.set(e.key, e.value.value); + } + } + return result; + }, + setProperty: function(key, value) + { + // + // Trim whitespace + // + if(key !== null && key !== undefined) + { + key = key.trim(); + } + + // + // Check if the property is legal. + // + var logger = getProcessLogger(); + if(key === null || key === undefined || key.length === 0) + { + throw new InitializationException("Attempt to set property with empty key"); + } + + var dotPos = key.indexOf("."); + if(dotPos !== -1) + { + var prefix = key.substr(0, dotPos); + for(var i = 0; i < PropertyNames.validProps.length; ++i) + { + var pattern = PropertyNames.validProps[i][0].pattern; + dotPos = pattern.indexOf("."); + // + // Each top level prefix describes a non-empty namespace. Having a string without a + // prefix followed by a dot is an error. + // + Debug.assert(dotPos != -1); + var propPrefix = pattern.substring(0, dotPos - 1); + if(propPrefix != prefix) + { + continue; + } + + var found = false; + var mismatchCase = false; + var otherKey; + for(var j = 0; j < PropertyNames.validProps[i][j].length && !found; ++j) + { + pattern = PropertyNames.validProps[i][j].pattern(); + var pComp = new RegExp(pattern); + found = pComp.test(key); + + if(found && PropertyNames.validProps[i][j].deprecated) + { + logger.warning("deprecated property: " + key); + if(PropertyNames.validProps[i][j].deprecatedBy !== null) + { + key = PropertyNames.validProps[i][j].deprecatedBy; + } + } + + if(found) + { + break; + } + else + { + pComp = new RegExp(pattern.toUpperCase()); + found = pComp.test(key.toUpperCase()); + if(found) + { + mismatchCase = true; + otherKey = pattern.substr(2); + otherKey = otherKey.substr(0, otherKey.length -1); + otherKey = otherKey.replace(/\\/g, ""); + break; + } + } + } + + if(!found) + { + logger.warning("unknown property: " + key); + } + else if(mismatchCase) + { + logger.warning("unknown property: `" + key + "'; did you mean `" + otherKey + "'"); + } + } + } + + // + // Set or clear the property. + // + if(value !== undefined && value !== null && value.length > 0) + { + var pv = this._properties.get(key); + if(pv !== undefined) + { + pv.value = value; + } + else + { + this._properties.set(key, { 'value': value, 'used': false }); + } + } + else + { + this._properties.delete(key); + } + }, + getCommandLineOptions: function() + { + var result = []; + for(var e = this._properties.entries; e !== null; e = e.next) + { + result.push("--" + e.key + "=" + e.pv.value); + } + return result; + }, + parseCommandLineOptions: function(pfx, options) + { + if(pfx.length > 0 && pfx.charAt(pfx.length - 1) != ".") + { + pfx += "."; + } + pfx = "--" + pfx; + + var result = []; + + var self = this; + options.forEach( + function(opt) + { + if(opt.indexOf(pfx) === 0) + { + if(opt.indexOf('=') === -1) + { + opt += "=1"; + } + + self.parseLine(opt.substring(2)); + } + else + { + result.push(opt); + } + }); + return result; + }, + parseIceCommandLineOptions: function(options) + { + var args = options.slice(); + for(var i = 0; i < PropertyNames.clPropNames.length; ++i) + { + args = this.parseCommandLineOptions(PropertyNames.clPropNames[i], args); + } + return args; + }, + parse: function(data) + { + var lines = data.match(/[^\r\n]+/g); + + var line; + + while((line = lines.shift())) + { + this.parseLine(line); + } + }, + parseLine: function(line) + { + var key = ""; + var value = ""; + + var state = ParseStateKey; + + var whitespace = ""; + var escapedspace = ""; + var finished = false; + + for(var i = 0; i < line.length; ++i) + { + var c = line.charAt(i); + switch(state) + { + case ParseStateKey: + { + switch(c) + { + case '\\': + if(i < line.length - 1) + { + c = line.charAt(++i); + switch(c) + { + case '\\': + case '#': + case '=': + key += whitespace; + whitespace = ""; + key += c; + break; + + case ' ': + if(key.length !== 0) + { + whitespace += c; + } + break; + + default: + key += whitespace; + whitespace = ""; + key += '\\'; + key += c; + break; + } + } + else + { + key += whitespace; + key += c; + } + break; + + case ' ': + case '\t': + case '\r': + case '\n': + if(key.length !== 0) + { + whitespace += c; + } + break; + + case '=': + whitespace = ""; + state = ParseStateValue; + break; + + case '#': + finished = true; + break; + + default: + key += whitespace; + whitespace = ""; + key += c; + break; + } + break; + } + + case ParseStateValue: + { + switch(c) + { + case '\\': + if(i < line.length - 1) + { + c = line.charAt(++i); + switch(c) + { + case '\\': + case '#': + case '=': + value += value.length === 0 ? escapedspace : whitespace; + whitespace = ""; + escapedspace = ""; + value += c; + break; + + case ' ': + whitespace += c; + escapedspace += c; + break; + + default: + value += value.length === 0 ? escapedspace : whitespace; + whitespace = ""; + escapedspace = ""; + value += '\\'; + value += c; + break; + } + } + else + { + value += value.length === 0 ? escapedspace : whitespace; + value += c; + } + break; + + case ' ': + case '\t': + case '\r': + case '\n': + if(value.length !== 0) + { + whitespace += c; + } + break; + + case '#': + value += escapedspace; + finished = true; + break; + + default: + value += value.length === 0 ? escapedspace : whitespace; + whitespace = ""; + escapedspace = ""; + value += c; + break; + } + break; + } + } + if(finished) + { + break; + } + } + value += escapedspace; + + if((state === ParseStateKey && key.length !== 0) || + (state == ParseStateValue && key.length === 0)) + { + getProcessLogger().warning("invalid config file entry: \"" + line + "\""); + return; + } + else if(key.length === 0) + { + return; + } + + this.setProperty(key, value); + }, + clone: function() + { + return new Properties(null, this); + }, + getUnusedProperties: function() + { + var unused = []; + for(var e = this._properties.entries; e !== null; e = e.next) + { + if(!e.pv.used) + { + unused.push(e.key); + } + } + return unused; + } + }); + + Properties.createProperties = function(args, defaults) + { + return new Properties(args, defaults); + }; + + Ice.Properties = Properties; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Property.js b/js/src/Ice/Property.js new file mode 100644 index 00000000000..3bea3d09caf --- /dev/null +++ b/js/src/Ice/Property.js @@ -0,0 +1,34 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var Property = function Property(pattern, deprecated, deprecatedBy) + { + this._pattern = pattern; + this._deprecated = deprecated; + this._deprecatedBy = deprecatedBy; + }; + + Object.defineProperty(Property.prototype, "pattern",{ + get: function() { return this._pattern; } + }); + + Object.defineProperty(Property.prototype, "deprecated",{ + get: function() { return this._deprecated; } + }); + + Object.defineProperty(Property.prototype, "deprecatedBy",{ + get: function() { return this._deprecatedBy; } + }); + + Ice.Property = Property; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/PropertyNames.js b/js/src/Ice/PropertyNames.js new file mode 100644 index 00000000000..5537b8c9d14 --- /dev/null +++ b/js/src/Ice/PropertyNames.js @@ -0,0 +1,177 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +///* jshint -W044*/ +// Generated by makeprops.py from file PropertyNames.xml, Tue Feb 4 16:00:18 2014 + +// IMPORTANT: Do not edit this file -- any edits made here will be lost! + +(function(global){ + var Ice = global.Ice || Ice; + require("Ice/Property"); + var PropertyNames = {}; + var Property = Ice.Property; + /* jshint -W044*/ + + PropertyNames.IceProps = + [ + new Property("/^Ice\.ACM\.Client/", false, null), + new Property("/^Ice\.ACM\.Server/", false, null), + new Property("/^Ice\.Admin\.ACM/", false, null), + new Property("/^Ice\.Admin\.AdapterId/", false, null), + new Property("/^Ice\.Admin\.Endpoints/", false, null), + new Property("/^Ice\.Admin\.Locator\.EndpointSelection/", false, null), + new Property("/^Ice\.Admin\.Locator\.ConnectionCached/", false, null), + new Property("/^Ice\.Admin\.Locator\.PreferSecure/", false, null), + new Property("/^Ice\.Admin\.Locator\.LocatorCacheTimeout/", false, null), + new Property("/^Ice\.Admin\.Locator\.Locator/", false, null), + new Property("/^Ice\.Admin\.Locator\.Router/", false, null), + new Property("/^Ice\.Admin\.Locator\.CollocationOptimized/", false, null), + new Property("/^Ice\.Admin\.Locator/", false, null), + new Property("/^Ice\.Admin\.PublishedEndpoints/", false, null), + new Property("/^Ice\.Admin\.ReplicaGroupId/", false, null), + new Property("/^Ice\.Admin\.Router\.EndpointSelection/", false, null), + new Property("/^Ice\.Admin\.Router\.ConnectionCached/", false, null), + new Property("/^Ice\.Admin\.Router\.PreferSecure/", false, null), + new Property("/^Ice\.Admin\.Router\.LocatorCacheTimeout/", false, null), + new Property("/^Ice\.Admin\.Router\.Locator/", false, null), + new Property("/^Ice\.Admin\.Router\.Router/", false, null), + new Property("/^Ice\.Admin\.Router\.CollocationOptimized/", false, null), + new Property("/^Ice\.Admin\.Router/", false, null), + new Property("/^Ice\.Admin\.ProxyOptions/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.Size/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.SizeMax/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.SizeWarn/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.StackSize/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.Serialize/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.ThreadIdleTime/", false, null), + new Property("/^Ice\.Admin\.ThreadPool\.ThreadPriority/", false, null), + new Property("/^Ice\.Admin\.DelayCreation/", false, null), + new Property("/^Ice\.Admin\.Facets/", false, null), + new Property("/^Ice\.Admin\.InstanceName/", false, null), + new Property("/^Ice\.Admin\.ServerId/", false, null), + new Property("/^Ice\.BackgroundLocatorCacheUpdates/", false, null), + new Property("/^Ice\.BatchAutoFlush/", false, null), + new Property("/^Ice\.ChangeUser/", false, null), + new Property("/^Ice\.ClientAccessPolicyProtocol/", false, null), + new Property("/^Ice\.Compression\.Level/", false, null), + new Property("/^Ice\.Config/", false, null), + new Property("/^Ice\.ConsoleListener/", false, null), + new Property("/^Ice\.Default\.CollocationOptimized/", false, null), + new Property("/^Ice\.Default\.EncodingVersion/", false, null), + new Property("/^Ice\.Default\.EndpointSelection/", false, null), + new Property("/^Ice\.Default\.Host/", false, null), + new Property("/^Ice\.Default\.Locator\.EndpointSelection/", false, null), + new Property("/^Ice\.Default\.Locator\.ConnectionCached/", false, null), + new Property("/^Ice\.Default\.Locator\.PreferSecure/", false, null), + new Property("/^Ice\.Default\.Locator\.LocatorCacheTimeout/", false, null), + new Property("/^Ice\.Default\.Locator\.Locator/", false, null), + new Property("/^Ice\.Default\.Locator\.Router/", false, null), + new Property("/^Ice\.Default\.Locator\.CollocationOptimized/", false, null), + new Property("/^Ice\.Default\.Locator/", false, null), + new Property("/^Ice\.Default\.LocatorCacheTimeout/", false, null), + new Property("/^Ice\.Default\.Package/", false, null), + new Property("/^Ice\.Default\.PreferSecure/", false, null), + new Property("/^Ice\.Default\.Protocol/", false, null), + new Property("/^Ice\.Default\.Router\.EndpointSelection/", false, null), + new Property("/^Ice\.Default\.Router\.ConnectionCached/", false, null), + new Property("/^Ice\.Default\.Router\.PreferSecure/", false, null), + new Property("/^Ice\.Default\.Router\.LocatorCacheTimeout/", false, null), + new Property("/^Ice\.Default\.Router\.Locator/", false, null), + new Property("/^Ice\.Default\.Router\.Router/", false, null), + new Property("/^Ice\.Default\.Router\.CollocationOptimized/", false, null), + new Property("/^Ice\.Default\.Router/", false, null), + new Property("/^Ice\.Default\.SlicedFormat/", false, null), + new Property("/^Ice\.IPv4/", false, null), + new Property("/^Ice\.IPv6/", false, null), + new Property("/^Ice\.EventLog\.Source/", false, null), + new Property("/^Ice\.FactoryAssemblies/", false, null), + new Property("/^Ice\.GC\.Interval/", false, null), + new Property("/^Ice\.ImplicitContext/", false, null), + new Property("/^Ice\.InitPlugins/", false, null), + new Property("/^Ice\.LogFile/", false, null), + new Property("/^Ice\.MessageSizeMax/", false, null), + new Property("/^Ice\.MonitorConnections/", false, null), + new Property("/^Ice\.Nohup/", false, null), + new Property("/^Ice\.NullHandleAbort/", false, null), + new Property("/^Ice\.Override\.CloseTimeout/", false, null), + new Property("/^Ice\.Override\.Compress/", false, null), + new Property("/^Ice\.Override\.ConnectTimeout/", false, null), + new Property("/^Ice\.Override\.Timeout/", false, null), + new Property("/^Ice\.Override\.Secure/", false, null), + new Property("/^Ice\.Package\../", false, null), + new Property("/^Ice\.Plugin\../", false, null), + new Property("/^Ice\.PluginLoadOrder/", false, null), + new Property("/^Ice\.PreferIPv6Address/", false, null), + new Property("/^Ice\.PrintAdapterReady/", false, null), + new Property("/^Ice\.PrintProcessId/", false, null), + new Property("/^Ice\.PrintStackTraces/", false, null), + new Property("/^Ice\.ProgramName/", false, null), + new Property("/^Ice\.RetryIntervals/", false, null), + new Property("/^Ice\.ServerIdleTime/", false, null), + new Property("/^Ice\.SOCKSProxyHost/", false, null), + new Property("/^Ice\.SOCKSProxyPort/", false, null), + new Property("/^Ice\.StdErr/", false, null), + new Property("/^Ice\.StdOut/", false, null), + new Property("/^Ice\.SyslogFacility/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.Size/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.SizeMax/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.SizeWarn/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.StackSize/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.Serialize/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.ThreadIdleTime/", false, null), + new Property("/^Ice\.ThreadPool\.Client\.ThreadPriority/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.Size/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.SizeMax/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.SizeWarn/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.StackSize/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.Serialize/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.ThreadIdleTime/", false, null), + new Property("/^Ice\.ThreadPool\.Server\.ThreadPriority/", false, null), + new Property("/^Ice\.ThreadPriority/", false, null), + new Property("/^Ice\.Trace\.Admin\.Properties/", false, null), + new Property("/^Ice\.Trace\.GC/", false, null), + new Property("/^Ice\.Trace\.Locator/", false, null), + new Property("/^Ice\.Trace\.Network/", false, null), + new Property("/^Ice\.Trace\.Protocol/", false, null), + new Property("/^Ice\.Trace\.Retry/", false, null), + new Property("/^Ice\.Trace\.Slicing/", false, null), + new Property("/^Ice\.Trace\.ThreadPool/", false, null), + new Property("/^Ice\.UDP\.RcvSize/", false, null), + new Property("/^Ice\.UDP\.SndSize/", false, null), + new Property("/^Ice\.TCP\.Backlog/", false, null), + new Property("/^Ice\.TCP\.RcvSize/", false, null), + new Property("/^Ice\.TCP\.SndSize/", false, null), + new Property("/^Ice\.UseApplicationClassLoader/", false, null), + new Property("/^Ice\.UseSyslog/", false, null), + new Property("/^Ice\.Warn\.AMICallback/", false, null), + new Property("/^Ice\.Warn\.Connections/", false, null), + new Property("/^Ice\.Warn\.Datagrams/", false, null), + new Property("/^Ice\.Warn\.Dispatch/", false, null), + new Property("/^Ice\.Warn\.Endpoints/", false, null), + new Property("/^Ice\.Warn\.UnknownProperties/", false, null), + new Property("/^Ice\.Warn\.UnusedProperties/", false, null), + new Property("/^Ice\.CacheMessageBuffers/", false, null), + ]; + + /* jshint +W044*/ + + PropertyNames.validProps = + [ + PropertyNames.IceProps, + ]; + + PropertyNames.clPropNames = + [ + "Ice", + ]; + + Ice.PropertyNames = PropertyNames; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Protocol.js b/js/src/Ice/Protocol.js new file mode 100644 index 00000000000..12d71fdcdae --- /dev/null +++ b/js/src/Ice/Protocol.js @@ -0,0 +1,316 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/StringUtil"); + require("Ice/LocalException"); + require("Ice/Version"); + require("Ice/Buffer"); + + var Ice = global.Ice || {}; + + var StringUtil = Ice.StringUtil; + + var Protocol = {}; + + Ice.Encoding_1_0 = new Ice.EncodingVersion(1, 0); + Ice.Encoding_1_1 = new Ice.EncodingVersion(1, 1); + + Ice.Protocol_1_0 = new Ice.ProtocolVersion(1, 0); + + // + // Size of the Ice protocol header + // + // Magic number (4 bytes) + // Protocol version major (Byte) + // Protocol version minor (Byte) + // Encoding version major (Byte) + // Encoding version minor (Byte) + // Message type (Byte) + // Compression status (Byte) + // Message size (Int) + // + Protocol.headerSize = 14; + + // + // The magic number at the front of each message + // + //Protocol.magic = [ 0x49, 0x63, 0x65, 0x50 ]; // 'I', 'c', 'e', 'P' + Protocol.magic = Ice.Buffer.createNative([ 0x49, 0x63, 0x65, 0x50 ]); // 'I', 'c', 'e', 'P' + + // + // The current Ice protocol and encoding version + // + Protocol.protocolMajor = 1; + Protocol.protocolMinor = 0; + Protocol.protocolEncodingMajor = 1; + Protocol.protocolEncodingMinor = 0; + + Protocol.encodingMajor = 1; + Protocol.encodingMinor = 1; + + // + // The Ice protocol message types + // + Protocol.requestMsg = 0; + Protocol.requestBatchMsg = 1; + Protocol.replyMsg = 2; + Protocol.validateConnectionMsg = 3; + Protocol.closeConnectionMsg = 4; + + // + // Reply status + // + Protocol.replyOK = 0; + Protocol.replyUserException = 1; + Protocol.replyObjectNotExist = 2; + Protocol.replyFacetNotExist = 3; + Protocol.replyOperationNotExist = 4; + Protocol.replyUnknownLocalException = 5; + Protocol.replyUnknownUserException = 6; + Protocol.replyUnknownException = 7; + + Protocol.requestHdr = Ice.Buffer.createNative([ + Protocol.magic[0], + Protocol.magic[1], + Protocol.magic[2], + Protocol.magic[3], + Protocol.protocolMajor, + Protocol.protocolMinor, + Protocol.protocolEncodingMajor, + Protocol.protocolEncodingMinor, + Protocol.requestMsg, + 0, // Compression status. + 0, 0, 0, 0, // Message size (placeholder). + 0, 0, 0, 0 // Request ID (placeholder). + ]); + + Protocol.requestBatchHdr = Ice.Buffer.createNative([ + Protocol.magic[0], + Protocol.magic[1], + Protocol.magic[2], + Protocol.magic[3], + Protocol.protocolMajor, + Protocol.protocolMinor, + Protocol.protocolEncodingMajor, + Protocol.protocolEncodingMinor, + Protocol.requestBatchMsg, + 0, // Compression status. + 0, 0, 0, 0, // Message size (placeholder). + 0, 0, 0, 0 // Number of requests in batch (placeholder). + ]); + + Protocol.replyHdr = Ice.Buffer.createNative([ + Protocol.magic[0], + Protocol.magic[1], + Protocol.magic[2], + Protocol.magic[3], + Protocol.protocolMajor, + Protocol.protocolMinor, + Protocol.protocolEncodingMajor, + Protocol.protocolEncodingMinor, + Protocol.replyMsg, + 0, // Compression status. + 0, 0, 0, 0 // Message size (placeholder). + ]); + + Protocol.currentProtocol = new Ice.ProtocolVersion(Protocol.protocolMajor, Protocol.protocolMinor); + Protocol.currentProtocolEncoding = new Ice.EncodingVersion(Protocol.protocolEncodingMajor, + Protocol.protocolEncodingMinor); + + Protocol.currentEncoding = new Ice.EncodingVersion(Protocol.encodingMajor, Protocol.encodingMinor); + + Protocol.checkSupportedProtocol = function(v) + { + if(v.major !== Protocol.currentProtocol.major || v.minor > Protocol.currentProtocol.minor) + { + throw new Ice.UnsupportedProtocolException("", v, Protocol.currentProtocol); + } + }; + + Protocol.checkSupportedProtocolEncoding = function(v) + { + if(v.major !== Protocol.currentProtocolEncoding.major || + v.minor > Protocol.currentProtocolEncoding.minor) + { + throw new Ice.UnsupportedEncodingException("", v, Protocol.currentProtocolEncoding); + } + }; + + Protocol.checkSupportedEncoding = function(v) + { + if(v.major !== Protocol.currentEncoding.major || v.minor > Protocol.currentEncoding.minor) + { + throw new Ice.UnsupportedEncodingException("", v, Protocol.currentEncoding); + } + }; + + // + // Either return the given protocol if not compatible, or the greatest + // supported protocol otherwise. + // + Protocol.getCompatibleProtocol = function(v) + { + if(v.major !== Protocol.currentProtocol.major) + { + return v; // Unsupported protocol, return as is. + } + else if(v.minor < Protocol.currentProtocol.minor) + { + return v; // Supported protocol. + } + else + { + // + // Unsupported but compatible, use the currently supported + // protocol, that's the best we can do. + // + return Protocol.currentProtocol; + } + }; + + // + // Either return the given encoding if not compatible, or the greatest + // supported encoding otherwise. + // + Protocol.getCompatibleEncoding = function(v) + { + if(v.major !== Protocol.currentEncoding.major) + { + return v; // Unsupported encoding, return as is. + } + else if(v.minor < Protocol.currentEncoding.minor) + { + return v; // Supported encoding. + } + else + { + // + // Unsupported but compatible, use the currently supported + // encoding, that's the best we can do. + // + return Protocol.currentEncoding; + } + }; + + Protocol.isSupported = function(version, supported) + { + return version.major === supported.major && version.minor <= supported.minor; + }; + + /** + * Converts a string to a protocol version. + * + * @param version The string to convert. + * + * @return The converted protocol version. + **/ + Ice.stringToProtocolVersion = function(version) + { + return new Ice.ProtocolVersion(stringToMajor(version), stringToMinor(version)); + }; + + /** + * Converts a string to an encoding version. + * + * @param version The string to convert. + * + * @return The converted object identity. + **/ + Ice.stringToEncodingVersion = function(version) + { + return new Ice.EncodingVersion(stringToMajor(version), stringToMinor(version)); + }; + + /** + * Converts a protocol version to a string. + * + * @param v The protocol version to convert. + * + * @return The converted string. + **/ + Ice.protocolVersionToString = function(v) + { + return majorMinorToString(v.major, v.minor); + }; + + /** + * Converts an encoding version to a string. + * + * @param v The encoding version to convert. + * + * @return The converted string. + **/ + Ice.encodingVersionToString = function(v) + { + return majorMinorToString(v.major, v.minor); + }; + + Ice.Protocol = Protocol; + global.Ice = Ice; + + function stringToMajor(str) + { + var pos = str.indexOf('.'); + if(pos === -1) + { + throw new Ice.VersionParseException("malformed version value `" + str + "'"); + } + + var majStr = str.substring(0, pos); + var majVersion; + try + { + majVersion = StringUtil.toInt(majStr); + } + catch(ex) + { + throw new Ice.VersionParseException("invalid version value `" + str + "'"); + } + + if(majVersion < 1 || majVersion > 255) + { + throw new Ice.VersionParseException("range error in version `" + str + "'"); + } + + return majVersion; + } + + function stringToMinor(str) + { + var pos = str.indexOf('.'); + if(pos === -1) + { + throw new Ice.VersionParseException("malformed version value `" + str + "'"); + } + + var minStr = str.substring(pos + 1); + var minVersion; + try + { + minVersion = StringUtil.toInt(minStr); + } + catch(ex) + { + throw new Ice.VersionParseException("invalid version value `" + str + "'"); + } + + if(minVersion < 0 || minVersion > 255) + { + throw new Ice.VersionParseException("range error in version `" + str + "'"); + } + + return minVersion; + } + + function majorMinorToString(major, minor) + { + return major + "." + minor; + } +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ProxyBatchOutgoingAsync.js b/js/src/Ice/ProxyBatchOutgoingAsync.js new file mode 100644 index 00000000000..372ee9dcc3e --- /dev/null +++ b/js/src/Ice/ProxyBatchOutgoingAsync.js @@ -0,0 +1,54 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_FOR_ACTIONSCRIPT_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/AsyncResult"); + require("Ice/AsyncStatus"); + require("Ice/BatchOutgoingAsync"); + require("Ice/Protocol"); + + var Ice = global.Ice || {}; + + var AsyncResult = Ice.AsyncResult; + var AsyncStatus = Ice.AsyncStatus; + var BatchOutgoingAsync = Ice.BatchOutgoingAsync; + var Protocol = Ice.Protocol; + + var ProxyBatchOutgoingAsync = Ice.Class(BatchOutgoingAsync, { + __init__: function(prx, operation) + { + BatchOutgoingAsync.call(this, prx.ice_getCommunicator(), operation); + this._proxy = prx; + }, + __send: function() + { + Protocol.checkSupportedProtocol(this._proxy.__reference().getProtocol()); + + // + // We don't automatically retry if ice_flushBatchRequests fails. Otherwise, if some batch + // requests were queued with the connection, they would be lost without being noticed. + // + var handler = null; + var cnt = -1; // Don't retry. + try + { + handler = this._proxy.__getRequestHandler(); + handler.flushAsyncBatchRequests(this); + } + catch(__ex) + { + cnt = this._proxy.__handleException(handler, __ex, 0, cnt); + } + } + }); + + Ice.ProxyBatchOutgoingAsync = ProxyBatchOutgoingAsync; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ProxyFactory.js b/js/src/Ice/ProxyFactory.js new file mode 100644 index 00000000000..5c62625a772 --- /dev/null +++ b/js/src/Ice/ProxyFactory.js @@ -0,0 +1,291 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/HashMap"); + require("Ice/ObjectPrx"); + require("Ice/StringUtil"); + require("Ice/Identity"); + require("Ice/Reference"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var HashMap = Ice.HashMap; + var ObjectPrx = Ice.ObjectPrx; + var StringUtil = Ice.StringUtil; + var Identity = Ice.Identity; + + // + // Only for use by Instance. + // + var ProxyFactory = Ice.Class({ + __init__: function(instance) + { + this._instance = instance; + + var arr = this._instance.initializationData().properties.getPropertyAsList("Ice.RetryIntervals"); + + if(arr.length > 0) + { + this._retryIntervals = []; + + for(var i = 0; i < arr.length; i++) + { + var v; + + try + { + v = StringUtil.toInt(arr[i]); + } + catch(ex) + { + v = 0; + } + + // + // If -1 is the first value, no retry and wait intervals. + // + if(i === 0 && v === -1) + { + this._retryIntervals = [ 0 ]; + break; + } + + this._retryIntervals[i] = v > 0 ? v : 0; + } + } + else + { + this._retryIntervals = [ 0 ]; + } + }, + stringToProxy: function(str) + { + var ref = this._instance.referenceFactory().createFromString(str, null); + return this.referenceToProxy(ref); + }, + proxyToString: function(proxy) + { + if(proxy !== null) + { + return proxy.__reference().toString(); + } + else + { + return ""; + } + }, + propertyToProxy: function(prefix) + { + var proxy = this._instance.initializationData().properties.getProperty(prefix); + var ref = this._instance.referenceFactory().createFromString(proxy, prefix); + return this.referenceToProxy(ref); + }, + proxyToProperty: function(proxy, prefix) + { + if(proxy !== null) + { + return proxy.__reference().toProperty(prefix); + } + else + { + return new HashMap(); + } + }, + streamToProxy: function(s, type) + { + var ident = new Identity(); + ident.__read(s); + + var ref = this._instance.referenceFactory().createFromStream(ident, s); + return this.referenceToProxy(ref, type); + }, + referenceToProxy: function(ref, type) + { + if(ref !== null) + { + var proxy = type ? new type() : new ObjectPrx(); + proxy.__setup(ref); + return proxy; + } + else + { + return null; + } + }, + proxyToStream: function(proxy, s) + { + if(proxy !== null) + { + var ref = proxy.__reference(); + ref.getIdentity().__write(s); + ref.streamWrite(s); + } + else + { + var ident = new Identity("", ""); + ident.__write(s); + } + }, + checkRetryAfterException: function(ex, ref, sleepInterval, cnt) + { + var traceLevels = this._instance.traceLevels(); + var logger = this._instance.initializationData().logger; + + // + // We don't retry batch requests because the exception might have caused + // the all the requests batched with the connection to be aborted and we + // want the application to be notified. + // + if(ref.getMode() === Ice.Reference.ModeBatchOneway || ref.getMode() === Ice.Reference.ModeBatchDatagram) + { + throw ex; + } + + if(ex instanceof Ice.ObjectNotExistException) + { + var one = ex; + + if(ref.getRouterInfo() !== null && one.operation === "ice_add_proxy") + { + // + // If we have a router, an ObjectNotExistException with an + // operation name "ice_add_proxy" indicates to the client + // that the router isn't aware of the proxy (for example, + // because it was evicted by the router). In this case, we + // must *always* retry, so that the missing proxy is added + // to the router. + // + + ref.getRouterInfo().clearCache(ref); + + if(traceLevels.retry >= 1) + { + logger.trace(traceLevels.retryCat, "retrying operation call to add proxy to router\n" + + ExUtil.toString(ex)); + } + + if(sleepInterval !== null) + { + sleepInterval.value = 0; + } + return cnt; // We must always retry, so we don't look at the retry count. + } + else if(ref.isIndirect()) + { + // + // We retry ObjectNotExistException if the reference is + // indirect. + // + + if(ref.isWellKnown()) + { + var li = ref.getLocatorInfo(); + if(li !== null) + { + li.clearCache(ref); + } + } + } + else + { + // + // For all other cases, we don't retry ObjectNotExistException. + // + throw ex; + } + } + else if(ex instanceof Ice.RequestFailedException) + { + // + // For all other cases, we don't retry ObjectNotExistException + // + throw ex; + } + + // + // There is no point in retrying an operation that resulted in a + // MarshalException. This must have been raised locally (because + // if it happened in a server it would result in an + // UnknownLocalException instead), which means there was a problem + // in this process that will not change if we try again. + // + // The most likely cause for a MarshalException is exceeding the + // maximum message size, which is represented by the the subclass + // MemoryLimitException. For example, a client can attempt to send + // a message that exceeds the maximum memory size, or accumulate + // enough batch requests without flushing that the maximum size is + // reached. + // + // This latter case is especially problematic, because if we were + // to retry a batch request after a MarshalException, we would in + // fact silently discard the accumulated requests and allow new + // batch requests to accumulate. If the subsequent batched + // requests do not exceed the maximum message size, it appears to + // the client that all of the batched requests were accepted, when + // in reality only the last few are actually sent. + // + if(ex instanceof Ice.MarshalException) + { + throw ex; + } + + ++cnt; + Debug.assert(cnt > 0); + + var interval; + if(cnt === (this._retryIntervals.length + 1) && ex instanceof Ice.CloseConnectionException) + { + // + // A close connection exception is always retried at least once, even if the retry + // limit is reached. + // + interval = 0; + } + else if(cnt > this._retryIntervals.length) + { + if(traceLevels.retry >= 1) + { + logger.trace(traceLevels.retryCat, "cannot retry operation call because retry limit has been exceeded\n" + + ExUtil.toString(ex)); + } + throw ex; + } + else + { + interval = this._retryIntervals[cnt - 1]; + } + + if(traceLevels.retry >= 1) + { + var msg = "retrying operation call"; + if(interval > 0) + { + msg += " in " + interval + "ms"; + } + msg += " because of exception\n" + ExUtil.toString(ex); + logger.trace(traceLevels.retryCat, msg); + } + + Debug.assert(sleepInterval !== null); + sleepInterval.value = interval; + + return cnt; + } + }); + + Ice.ProxyFactory = ProxyFactory; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Reference.js b/js/src/Ice/Reference.js new file mode 100644 index 00000000000..54f0927a49d --- /dev/null +++ b/js/src/Ice/Reference.js @@ -0,0 +1,2345 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/ArrayUtil"); + require("Ice/Debug"); + require("Ice/HashMap"); + require("Ice/HashUtil"); + require("Ice/ExUtil"); + require("Ice/OpaqueEndpointI"); + require("Ice/Promise"); + require("Ice/Protocol"); + require("Ice/ReferenceMode"); + require("Ice/StringUtil"); + require("Ice/BuiltinSequences"); + require("Ice/EndpointTypes"); + require("Ice/Identity"); + require("Ice/Router"); + require("Ice/Locator"); + require("Ice/LocalException"); + require("Ice/Version"); + require("Ice/PropertyNames"); + + var Ice = global.Ice || {}; + + var ArrayUtil = Ice.ArrayUtil; + var Debug = Ice.Debug; + var HashMap = Ice.HashMap; + var HashUtil = Ice.HashUtil; + var ExUtil = Ice.ExUtil; + var OpaqueEndpointI = Ice.OpaqueEndpointI; + var Promise = Ice.Promise; + var Protocol = Ice.Protocol; + var RefMode = Ice.ReferenceMode; + var StringUtil = Ice.StringUtil; + var StringSeqHelper = Ice.StringSeqHelper; + var EndpointSelectionType = Ice.EndpointSelectionType; + var Identity = Ice.Identity; + var RouterPrx = Ice.RouterPrx; + var LocatorPrx = Ice.LocatorPrx; + var PropertyNames = Ice.PropertyNames; + + var Class = Ice.Class; + + var suffixes = + [ + "EndpointSelection", + "ConnectionCached", + "PreferSecure", + "EncodingVersion", + "LocatorCacheTimeout", + "Locator", + "Router", + "CollocationOptimized" + ]; + + // + // Only for use by Instance + // + var ReferenceFactory = Class({ + __init__: function(instance, communicator) + { + this._instance = instance; + this._communicator = communicator; + this._defaultRouter = null; + this._defaultLocator = null; + }, + create: function(ident, facet, tmpl, endpoints) + { + if(ident.name.length === 0 && ident.category.length === 0) + { + return null; + } + + return this.createImpl(ident, facet, tmpl.getMode(), tmpl.getSecure(), tmpl.getProtocol(), tmpl.getEncoding(), + endpoints, null, null); + }, + createWithAdapterId: function(ident, facet, tmpl, adapterId) + { + if(ident.name.length === 0 && ident.category.length === 0) + { + return null; + } + + return this.createImpl(ident, facet, tmpl.getMode(), tmpl.getSecure(), tmpl.getProtocol(), tmpl.getEncoding(), + null, adapterId, null); + }, + createFixed: function(ident, fixedConnection) + { + if(ident.name.length === 0 && ident.category.length === 0) + { + return null; + } + + // + // Create new reference + // + var ref = new FixedReference( + this._instance, + this._communicator, + ident, + "", // Facet + fixedConnection.endpoint().datagram() ? RefMode.ModeDatagram : RefMode.ModeTwoway, + fixedConnection.endpoint().secure(), + this._instance.defaultsAndOverrides().defaultEncoding, + fixedConnection); + return ref; + }, + copy: function(r) + { + var ident = r.getIdentity(); + if(ident.name.length === 0 && ident.category.length === 0) + { + return null; + } + return r.clone(); + }, + createFromString: function(s, propertyPrefix) + { + if(s === undefined || s === null || s.length === 0) + { + return null; + } + + var delim = " \t\n\r"; + + var beg; + var end = 0; + + beg = StringUtil.findFirstNotOf(s, delim, end); + if(beg == -1) + { + throw new Ice.ProxyParseException("no non-whitespace characters found in `" + s + "'"); + } + + // + // Extract the identity, which may be enclosed in single + // or double quotation marks. + // + var idstr = null; + end = StringUtil.checkQuote(s, beg); + if(end === -1) + { + throw new Ice.ProxyParseException("mismatched quotes around identity in `" + s + "'"); + } + else if(end === 0) + { + end = StringUtil.findFirstOf(s, delim + ":@", beg); + if(end === -1) + { + end = s.length; + } + idstr = s.substring(beg, end); + } + else + { + beg++; // Skip leading quote + idstr = s.substring(beg, end); + end++; // Skip trailing quote + } + + if(beg === end) + { + throw new Ice.ProxyParseException("no identity in `" + s + "'"); + } + + // + // Parsing the identity may raise IdentityParseException. + // + var ident = this._instance.stringToIdentity(idstr); + + if(ident.name.length === 0) + { + // + // An identity with an empty name and a non-empty + // category is illegal. + // + if(ident.category.length > 0) + { + throw new Ice.IllegalIdentityException(ident); + } + // + // Treat a stringified proxy containing two double + // quotes ("") the same as an empty string, i.e., + // a null proxy, but only if nothing follows the + // quotes. + // + else if(StringUtil.findFirstNotOf(s, delim, end) != -1) + { + throw new Ice.ProxyParseException("invalid characters after identity in `" + s + "'"); + } + else + { + return null; + } + } + + var facet = ""; + var mode = RefMode.ModeTwoway; + var secure = false; + var encoding = this._instance.defaultsAndOverrides().defaultEncoding; + var protocol = Ice.Protocol_1_0; + var adapter = ""; + + while(true) + { + beg = StringUtil.findFirstNotOf(s, delim, end); + if(beg === -1) + { + break; + } + + if(s.charAt(beg) == ':' || s.charAt(beg) == '@') + { + break; + } + + end = StringUtil.findFirstOf(s, delim + ":@", beg); + if(end == -1) + { + end = s.length; + } + + if(beg == end) + { + break; + } + + var option = s.substring(beg, end); + if(option.length != 2 || option.charAt(0) != '-') + { + throw new Ice.ProxyParseException("expected a proxy option but found `" + option + "' in `" + s + "'"); + } + + // + // Check for the presence of an option argument. The + // argument may be enclosed in single or double + // quotation marks. + // + var argument = null; + var argumentBeg = StringUtil.findFirstNotOf(s, delim, end); + if(argumentBeg != -1) + { + var ch = s.charAt(argumentBeg); + if(ch != "@" && ch != ":" && ch != "-") + { + beg = argumentBeg; + end = StringUtil.checkQuote(s, beg); + if(end == -1) + { + throw new Ice.ProxyParseException("mismatched quotes around value for " + option + + " option in `" + s + "'"); + } + else if(end === 0) + { + end = StringUtil.findFirstOf(s, delim + ":@", beg); + if(end === -1) + { + end = s.length; + } + argument = s.substring(beg, end); + } + else + { + beg++; // Skip leading quote + argument = s.substring(beg, end); + end++; // Skip trailing quote + } + } + } + + // + // If any new options are added here, + // IceInternal::Reference::toString() and its derived classes must be updated as well. + // + switch(option.charAt(1)) + { + case 'f': + { + if(argument === null) + { + throw new Ice.ProxyParseException("no argument provided for -f option in `" + s + "'"); + } + + try + { + facet = StringUtil.unescapeString(argument, 0, argument.length); + } + catch(ex) + { + throw new Ice.ProxyParseException("invalid facet in `" + s + "': " + ex.message); + } + + break; + } + + case 't': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -t option in `" + s + "'"); + } + mode = RefMode.ModeTwoway; + break; + } + + case 'o': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -o option in `" + s + "'"); + } + mode = RefMode.ModeOneway; + break; + } + + case 'O': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -O option in `" + s + "'"); + } + mode = RefMode.ModeBatchOneway; + break; + } + + case 'd': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -d option in `" + s + "'"); + } + mode = RefMode.ModeDatagram; + break; + } + + case 'D': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -D option in `" + s + "'"); + } + mode = RefMode.ModeBatchDatagram; + break; + } + + case 's': + { + if(argument !== null) + { + throw new Ice.ProxyParseException("unexpected argument `" + argument + + "' provided for -s option in `" + s + "'"); + } + secure = true; + break; + } + + case 'e': + { + if(argument === null) + { + throw new Ice.ProxyParseException("no argument provided for -e option in `" + s + "'"); + } + + try + { + encoding = Ice.stringToEncodingVersion(argument); + } + catch(e) // VersionParseException + { + throw new Ice.ProxyParseException("invalid encoding version `" + argument + "' in `" + s + + "':\n" + e.str); + } + break; + } + + case 'p': + { + if(argument === null) + { + throw new Ice.ProxyParseException("no argument provided for -p option in `" + s + "'"); + } + + try + { + protocol = Ice.stringToProtocolVersion(argument); + } + catch(e) // VersionParseException + { + throw new Ice.ProxyParseException("invalid protocol version `" + argument + "' in `" + s + + "':\n" + e.str); + } + break; + } + + default: + { + throw new Ice.ProxyParseException("unknown option `" + option + "' in `" + s + "'"); + } + } + } + + if(beg === -1) + { + return this.createImpl(ident, facet, mode, secure, protocol, encoding, null, null, propertyPrefix); + } + + var endpoints = []; + + if(s.charAt(beg) == ':') + { + var unknownEndpoints = []; + end = beg; + + while(end < s.length && s.charAt(end) == ':') + { + beg = end + 1; + + end = beg; + while(true) + { + end = s.indexOf(':', end); + if(end == -1) + { + end = s.length; + break; + } + else + { + var quoted = false; + var quote = beg; + while(true) + { + quote = s.indexOf("\"", quote); + if(quote == -1 || end < quote) + { + break; + } + else + { + quote = s.indexOf('\"', ++quote); + if(quote == -1) + { + break; + } + else if(end < quote) + { + quoted = true; + break; + } + ++quote; + } + } + if(!quoted) + { + break; + } + ++end; + } + } + + var es = s.substring(beg, end); + var endp = this._instance.endpointFactoryManager().create(es, false); + if(endp !== null) + { + endpoints.push(endp); + } + else + { + unknownEndpoints.push(es); + } + } + if(endpoints.length === 0) + { + Debug.assert(unknownEndpoints.length > 0); + throw new Ice.EndpointParseException("invalid endpoint `" + unknownEndpoints[0] + "' in `" + s + "'"); + } + else if(unknownEndpoints.length !== 0 && + this._instance.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Endpoints", 1) > 0) + { + var msg = []; + msg.push("Proxy contains unknown endpoints:"); + for(var i = 0; i < unknownEndpoints.length; ++i) + { + msg.push(" `"); + msg.push(unknownEndpoints[i]); + msg.push("'"); + } + this._instance.initializationData().logger.warning(msg.join("")); + } + + return this.createImpl(ident, facet, mode, secure, protocol, encoding, endpoints, null, propertyPrefix); + } + else if(s.charAt(beg) == '@') + { + beg = StringUtil.findFirstNotOf(s, delim, beg + 1); + if(beg == -1) + { + throw new Ice.ProxyParseException("missing adapter id in `" + s + "'"); + } + + var adapterstr = null; + end = StringUtil.checkQuote(s, beg); + if(end === -1) + { + throw new Ice.ProxyParseException("mismatched quotes around adapter id in `" + s + "'"); + } + else if(end === 0) + { + end = StringUtil.findFirstOf(s, delim, beg); + if(end === -1) + { + end = s.length; + } + adapterstr = s.substring(beg, end); + } + else + { + beg++; // Skip leading quote + adapterstr = s.substring(beg, end); + end++; // Skip trailing quote + } + + if(end !== s.length && StringUtil.findFirstNotOf(s, delim, end) !== -1) + { + throw new Ice.ProxyParseException("invalid trailing characters after `" + s.substring(0, end + 1) + + "' in `" + s + "'"); + } + + try + { + adapter = StringUtil.unescapeString(adapterstr, 0, adapterstr.length); + } + catch(ex) + { + throw new Ice.ProxyParseException("invalid adapter id in `" + s + "': " + ex.message); + } + if(adapter.length === 0) + { + throw new Ice.ProxyParseException("empty adapter id in `" + s + "'"); + } + return this.createImpl(ident, facet, mode, secure, protocol, encoding, null, adapter, propertyPrefix); + } + + throw new Ice.ProxyParseException("malformed proxy `" + s + "'"); + }, + createFromStream: function(ident, s) + { + // + // Don't read the identity here. Operations calling this + // constructor read the identity, and pass it as a parameter. + // + + if(ident.name.length === 0 && ident.category.length === 0) + { + return null; + } + + // + // For compatibility with the old FacetPath. + // + var facetPath = StringSeqHelper.read(s); // String[] + var facet; + if(facetPath.length > 0) + { + if(facetPath.length > 1) + { + throw new Ice.ProxyUnmarshalException(); + } + facet = facetPath[0]; + } + else + { + facet = ""; + } + + var mode = s.readByte(); + if(mode < 0 || mode > RefMode.ModeLast) + { + throw new Ice.ProxyUnmarshalException(); + } + + var secure = s.readBool(); + + var protocol = null; + var encoding = null; + if(!s.getReadEncoding().equals(Ice.Encoding_1_0)) + { + protocol = new Ice.ProtocolVersion(); + protocol.__read(s); + encoding = new Ice.EncodingVersion(); + encoding.__read(s); + } + else + { + protocol = Ice.Protocol_1_0; + encoding = Ice.Encoding_1_0; + } + + var endpoints = null; // EndpointI[] + var adapterId = null; + + var sz = s.readSize(); + if(sz > 0) + { + endpoints = []; + for(var i = 0; i < sz; i++) + { + endpoints[i] = this._instance.endpointFactoryManager().read(s); + } + } + else + { + adapterId = s.readString(); + } + + return this.createImpl(ident, facet, mode, secure, protocol, encoding, endpoints, adapterId, null); + }, + setDefaultRouter: function(defaultRouter) + { + if(this._defaultRouter === null ? defaultRouter === null : this._defaultRouter.equals(defaultRouter)) + { + return this; + } + + var factory = new ReferenceFactory(this._instance, this._communicator); + factory._defaultLocator = this._defaultLocator; + factory._defaultRouter = defaultRouter; + return factory; + }, + getDefaultRouter: function() + { + return this._defaultRouter; + }, + setDefaultLocator: function(defaultLocator) + { + if(this._defaultLocator === null ? defaultLocator === null : this._defaultLocator.equals(defaultLocator)) + { + return this; + } + + var factory = new ReferenceFactory(this._instance, this._communicator); + factory._defaultRouter = this._defaultRouter; + factory._defaultLocator = defaultLocator; + return factory; + }, + getDefaultLocator: function() + { + return this._defaultLocator; + }, + checkForUnknownProperties: function(prefix) + { + var unknownProps = [], i, length; + // + // Do not warn about unknown properties for Ice prefixes (Ice, Glacier2, etc.) + // + for(i = 0; i < PropertyNames.clPropNames.length; ++i) + { + if(prefix.indexOf(PropertyNames.clPropNames[i] + ".") === 0) + { + return; + } + } + + var props = this._instance.initializationData().properties.getPropertiesForPrefix(prefix + "."); + for(var e = props.entries; e !== null; e = e.next) + { + var valid = false; + for(i = 0, length = suffixes.length; i < length; ++i) + { + if(e.key === prefix + "." + suffixes[i]) + { + valid = true; + break; + } + } + + if(!valid) + { + unknownProps.push(e.key); + } + } + + if(unknownProps.length > 0) + { + var message = []; + message.push("found unknown properties for proxy '"); + message.push(prefix); + message.push("':"); + for(i = 0, length = unknownProps.length; i < length; ++i) + { + message.push("\n "); + message.push(unknownProps[i]); + } + this._instance.initializationData().logger.warning(message.join("")); + } + }, + createImpl: function(ident, facet, mode, secure, protocol, encoding, endpoints, adapterId, + propertyPrefix) + { + var defaultsAndOverrides = this._instance.defaultsAndOverrides(); + + // + // Default local proxy options. + // + var locatorInfo = null; + if(this._defaultLocator !== null) + { + if(!this._defaultLocator.__reference().getEncoding().equals(encoding)) + { + locatorInfo = this._instance.locatorManager().find(this._defaultLocator.ice_encodingVersion(encoding)); + } + else + { + locatorInfo = this._instance.locatorManager().find(this._defaultLocator); + } + } + var routerInfo = this._instance.routerManager().find(this._defaultRouter); + var cacheConnection = true; + var preferSecure = defaultsAndOverrides.defaultPreferSecure; + var endpointSelection = defaultsAndOverrides.defaultEndpointSelection; + var locatorCacheTimeout = defaultsAndOverrides.defaultLocatorCacheTimeout; + + // + // Override the defaults with the proxy properties if a property prefix is defined. + // + if(propertyPrefix !== null && propertyPrefix.length > 0) + { + var properties = this._instance.initializationData().properties; + + // + // Warn about unknown properties. + // + if(properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0) + { + this.checkForUnknownProperties(propertyPrefix); + } + + var property; + + property = propertyPrefix + ".Locator"; + var locator = LocatorPrx.uncheckedCast(this._communicator.propertyToProxy(property)); + if(locator !== null) + { + if(!locator.__reference().getEncoding().equals(encoding)) + { + locatorInfo = this._instance.locatorManager().find(locator.ice_encodingVersion(encoding)); + } + else + { + locatorInfo = this._instance.locatorManager().find(locator); + } + } + + property = propertyPrefix + ".Router"; + var router = RouterPrx.uncheckedCast(this._communicator.propertyToProxy(property)); + if(router !== null) + { + var match = ".Router"; + if(propertyPrefix.lastIndexOf(match) == propertyPrefix.length - match.length) + { + var s = "`" + property + "=" + properties.getProperty(property) + + "': cannot set a router on a router; setting ignored"; + this._instance.initializationData().logger.warning(s); + } + else + { + routerInfo = this._instance.routerManager().find(router); + } + } + + property = propertyPrefix + ".ConnectionCached"; + cacheConnection = properties.getPropertyAsIntWithDefault(property, cacheConnection ? 1 : 0) > 0; + + property = propertyPrefix + ".PreferSecure"; + preferSecure = properties.getPropertyAsIntWithDefault(property, preferSecure ? 1 : 0) > 0; + + property = propertyPrefix + ".EndpointSelection"; + if(properties.getProperty(property).length > 0) + { + var type = properties.getProperty(property); + if(type == "Random") + { + endpointSelection = EndpointSelectionType.Random; + } + else if(type == "Ordered") + { + endpointSelection = EndpointSelectionType.Ordered; + } + else + { + throw new Ice.EndpointSelectionTypeParseException("illegal value `" + type + + "'; expected `Random' or `Ordered'"); + } + } + + property = propertyPrefix + ".LocatorCacheTimeout"; + locatorCacheTimeout = properties.getPropertyAsIntWithDefault(property, locatorCacheTimeout); + } + + // + // Create new reference + // + return new RoutableReference(this._instance, + this._communicator, + ident, + facet, + mode, + secure, + protocol, + encoding, + endpoints, + adapterId, + locatorInfo, + routerInfo, + cacheConnection, + preferSecure, + endpointSelection, + locatorCacheTimeout); + } + }); + + Ice.ReferenceFactory = ReferenceFactory; + + var Reference = Class({ + __init__: function(instance, communicator, identity, facet, mode, secure, protocol, encoding) + { + // + // Validate string arguments. + // + Debug.assert(identity === undefined || identity.name !== null); + Debug.assert(identity === undefined || identity.category !== null); + Debug.assert(facet === undefined || facet !== null); + + this._instance = instance; + this._communicator = communicator; + this._mode = mode; + this._secure = secure; + this._identity = identity; + this._context = Reference._emptyContext; + this._facet = facet; + this._protocol = protocol; + this._encoding = encoding; + this._hashInitialized = false; + this._overrideCompress = false; + this._compress = false; // Only used if _overrideCompress == true + }, + getMode: function() + { + return this._mode; + }, + getSecure: function() + { + return this._secure; + }, + getProtocol: function() + { + return this._protocol; + }, + getEncoding: function() + { + return this._encoding; + }, + getIdentity: function() + { + return this._identity; + }, + getFacet: function() + { + return this._facet; + }, + getInstance: function() + { + return this._instance; + }, + getContext: function() + { + return this._context; // HashMap + }, + getCommunicator: function() + { + return this._communicator; + }, + getEndpoints: function() + { + // Abstract + Debug.assert(false); + return null; + }, + getAdapterId: function() + { + // Abstract + Debug.assert(false); + return ""; + }, + getRouterInfo: function() + { + // Abstract + Debug.assert(false); + return null; + }, + getLocatorInfo: function() + { + // Abstract + Debug.assert(false); + return null; + }, + getCacheConnection: function() + { + // Abstract + Debug.assert(false); + return false; + }, + getPreferSecure: function() + { + // Abstract + Debug.assert(false); + return false; + }, + getEndpointSelection: function() + { + // Abstract + Debug.assert(false); + return null; + }, + getLocatorCacheTimeout: function() + { + // Abstract + Debug.assert(false); + return 0; + }, + getConnectionId: function() + { + // Abstract + Debug.assert(false); + return ""; + }, + // + // The change* methods (here and in derived classes) create + // a new reference based on the existing one, with the + // corresponding value changed. + // + changeContext: function(newContext) + { + if(newContext === undefined || newContext === null) + { + newContext = Reference._emptyContext; + } + var r = this._instance.referenceFactory().copy(this); + if(newContext.size === 0) + { + r._context = Reference._emptyContext; + } + else + { + r._context = new HashMap(newContext); + } + return r; + }, + changeMode: function(newMode) + { + if(newMode === this._mode) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._mode = newMode; + return r; + }, + changeSecure: function(newSecure) + { + if(newSecure === this._secure) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._secure = newSecure; + return r; + }, + changeIdentity: function(newIdentity) + { + if(newIdentity.equals(this._identity)) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._identity = new Identity(newIdentity.name, newIdentity.category); + return r; + }, + changeFacet: function(newFacet) + { + if(newFacet === this._facet) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._facet = newFacet; + return r; + }, + changeEncoding: function(newEncoding) + { + if(newEncoding.equals(this._encoding)) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._encoding = newEncoding; + return r; + }, + changeCompress: function(newCompress) + { + if(this._overrideCompress && this._compress === newCompress) + { + return this; + } + var r = this._instance.referenceFactory().copy(this); + r._compress = newCompress; + r._overrideCompress = true; + return r; + }, + changeAdapterId: function(newAdapterId) + { + // Abstract + Debug.assert(false); + return null; + }, + changeEndpoints: function(newEndpoints) + { + // Abstract + Debug.assert(false); + return null; + }, + changeLocator: function(newLocator) + { + // Abstract + Debug.assert(false); + return null; + }, + changeRouter: function(newRouter) + { + // Abstract + Debug.assert(false); + return null; + }, + changeCacheConnection: function(newCache) + { + // Abstract + Debug.assert(false); + return null; + }, + changePreferSecure: function(newPreferSecure) + { + // Abstract + Debug.assert(false); + return null; + }, + changeEndpointSelection: function(newType) + { + // Abstract + Debug.assert(false); + return null; + }, + changeLocatorCacheTimeout: function(newTimeout) + { + // Abstract + Debug.assert(false); + return null; + }, + changeTimeout: function(newTimeout) + { + // Abstract + Debug.assert(false); + return null; + }, + changeConnectionId: function(connectionId) + { + // Abstract + Debug.assert(false); + return null; + }, + hashCode: function() + { + if(this._hashInitialized) + { + return this._hashValue; + } + + var h = 5381; + h = HashUtil.addNumber(h, this._mode); + h = HashUtil.addBoolean(h, this._secure); + h = HashUtil.addHashable(h, this._identity); + if(this._context !== null && this._context !== undefined) + { + for(var e = this._context.entries; e !== null; e = e.next) + { + h = HashUtil.addString(h, e.key); + h = HashUtil.addString(h, e.value); + } + } + h = HashUtil.addString(h, this._facet); + h = HashUtil.addBoolean(h, this._overrideCompress); + if(this._overrideCompress) + { + h = HashUtil.addBoolean(h, this._compress); + } + h = HashUtil.addHashable(h, this._protocol); + h = HashUtil.addHashable(h, this._encoding); + + this._hashValue = h; + this._hashInitialized = true; + + return this._hashValue; + }, + // + // Utility methods + // + isIndirect: function() + { + // Abstract + Debug.assert(false); + return false; + }, + isWellKnown: function() + { + // Abstract + Debug.assert(false); + return false; + }, + // + // Marshal the reference. + // + streamWrite: function(s) + { + // + // Don't write the identity here. Operations calling streamWrite + // write the identity. + // + + // + // For compatibility with the old FacetPath. + // + if(this._facet.length === 0) + { + s.writeSize(0); // Empty string sequence + } + else + { + s.writeSize(1); // String sequence with one element + s.writeString(this._facet); + } + + s.writeByte(this._mode); + + s.writeBool(this._secure); + + if(!s.getWriteEncoding().equals(Ice.Encoding_1_0)) + { + this._protocol.__write(s); + this._encoding.__write(s); + } + + // Derived class writes the remainder of the reference. + }, + // + // Convert the reference to its string form. + // + toString: function() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + var s = []; + + // + // If the encoded identity string contains characters which + // the reference parser uses as separators, then we enclose + // the identity string in quotes. + // + var id = this._instance.identityToString(this._identity); + if(id.search(/[ :@]/) != -1) + { + s.push('"'); + s.push(id); + s.push('"'); + } + else + { + s.push(id); + } + + if(this._facet.length > 0) + { + // + // If the encoded facet string contains characters which + // the reference parser uses as separators, then we enclose + // the facet string in quotes. + // + s.push(" -f "); + var fs = StringUtil.escapeString(this._facet, ""); + if(fs.search(/[ :@]/) != -1) + { + s.push('"'); + s.push(fs); + s.push('"'); + } + else + { + s.push(fs); + } + } + + switch(this._mode) + { + case RefMode.ModeTwoway: + { + s.push(" -t"); + break; + } + + case RefMode.ModeOneway: + { + s.push(" -o"); + break; + } + + case RefMode.ModeBatchOneway: + { + s.push(" -O"); + break; + } + + case RefMode.ModeDatagram: + { + s.push(" -d"); + break; + } + + case RefMode.ModeBatchDatagram: + { + s.push(" -D"); + break; + } + } + + if(this._secure) + { + s.push(" -s"); + } + + if(!this._protocol.equals(Ice.Protocol_1_0)) + { + // + // We only print the protocol if it's not 1.0. It's fine as + // long as we don't add Ice.Default.ProtocolVersion, a + // stringified proxy will convert back to the same proxy with + // stringToProxy. + // + s.push(" -p "); + s.push(Ice.protocolVersionToString(this._protocol)); + } + + // + // Always print the encoding version to ensure a stringified proxy + // will convert back to a proxy with the same encoding with + // stringToProxy (and won't use Ice.Default.EncodingVersion). + // + s.push(" -e "); + s.push(Ice.encodingVersionToString(this._encoding)); + + return s.join(""); + + // Derived class writes the remainder of the string. + }, + // + // Convert the reference to its property form. + // + toProperty: function(prefix) + { + // Abstract + Debug.assert(false); + return null; + }, + getConnection: function() + { + // Abstract + Debug.assert(false); + }, + equals: function(r) + { + // + // Note: if(this === r) and type test are performed by each non-abstract derived class. + // + + if(this._mode !== r._mode) + { + return false; + } + + if(this._secure !== r._secure) + { + return false; + } + + if(!this._identity.equals(r._identity)) + { + return false; + } + + if(!this._context.equals(r._context)) + { + return false; + } + + if(this._facet !== r._facet) + { + return false; + } + + if(this._overrideCompress !== r._overrideCompress) + { + return false; + } + if(this._overrideCompress && this._compress !== r._compress) + { + return false; + } + + if(!this._protocol.equals(r._protocol)) + { + return false; + } + + if(!this._encoding.equals(r._encoding)) + { + return false; + } + + return true; + }, + clone: function() + { + // Abstract + Debug.assert(false); + return null; + }, + copyMembers: function(r) + { + // + // Copy the members that are not passed to the constructor. + // + r._context = this._context; + r._overrideCompress = this._overrideCompress; + r._compress = this._compress; + } + }); + + Reference._emptyContext = new HashMap(); + Reference._emptyEndpoints = []; + + Ice.Reference = Reference; + + var FixedReference = Class(Reference, { + __init__: function(instance, communicator, identity, facet, mode, secure, encoding, connection) + { + Reference.call(this, instance, communicator, identity, facet, mode, secure, Ice.Protocol_1_0, encoding); + this._fixedConnection = connection; + }, + getEndpoints: function() + { + return Reference._emptyEndpoints; + }, + getAdapterId: function() + { + return ""; + }, + getRouterInfo: function() + { + return null; + }, + getLocatorInfo: function() + { + return null; + }, + getCacheConnection: function() + { + return false; + }, + getPreferSecure: function() + { + return false; + }, + getEndpointSelection: function() + { + return EndpointSelectionType.Random; + }, + getLocatorCacheTimeout: function() + { + return 0; + }, + getConnectionId: function() + { + return ""; + }, + changeAdapterId: function(newAdapterId) + { + throw new Ice.FixedProxyException(); + }, + changeEndpoints: function(newEndpoints) + { + throw new Ice.FixedProxyException(); + }, + changeLocator: function(newLocator) + { + throw new Ice.FixedProxyException(); + }, + changeRouter: function(newRouter) + { + throw new Ice.FixedProxyException(); + }, + changeCacheConnection: function(newCache) + { + throw new Ice.FixedProxyException(); + }, + changePreferSecure: function(prefSec) + { + throw new Ice.FixedProxyException(); + }, + changeEndpointSelection: function(newType) + { + throw new Ice.FixedProxyException(); + }, + changeLocatorCacheTimeout: function(newTimeout) + { + throw new Ice.FixedProxyException(); + }, + changeTimeout: function(newTimeout) + { + throw new Ice.FixedProxyException(); + }, + changeConnectionId: function(connectionId) + { + throw new Ice.FixedProxyException(); + }, + isIndirect: function() + { + return false; + }, + isWellKnown: function() + { + return false; + }, + streamWrite: function(s) + { + throw new Ice.FixedProxyException(); + }, + toString: function() + { + throw new Ice.FixedProxyException(); + }, + toProperty: function(prefix) + { + throw new Ice.FixedProxyException(); + }, + clone: function() + { + var r = new FixedReference(this.getInstance(), this.getCommunicator(), this.getIdentity(), this.getFacet(), + this.getMode(), this.getSecure(), this.getEncoding(), this._fixedConnection); + this.copyMembers(r); + return r; + }, + getConnectionInternal: function(compress) + { + switch(this.getMode()) + { + case RefMode.ModeTwoway: + case RefMode.ModeOneway: + case RefMode.ModeBatchOneway: + { + if(this._fixedConnection.endpoint().datagram()) + { + throw new Ice.NoEndpointException(""); + } + break; + } + + case RefMode.ModeDatagram: + case RefMode.ModeBatchDatagram: + { + if(!this._fixedConnection.endpoint().datagram()) + { + throw new Ice.NoEndpointException(""); + } + break; + } + } + + // + // If a secure connection is requested or secure overrides is set, + // check if the connection is secure. + // + var secure; + var defaultsAndOverrides = this.getInstance().defaultsAndOverrides(); + if(defaultsAndOverrides.overrideSecure) + { + secure = defaultsAndOverrides.overrideSecureValue; + } + else + { + secure = this.getSecure(); + } + if(secure && !this._fixedConnection.endpoint().secure()) + { + throw new Ice.NoEndpointException(""); + } + + this._fixedConnection.throwException(); // Throw in case our connection is already destroyed. + + if(defaultsAndOverrides.overrideCompress) + { + compress.value = defaultsAndOverrides.overrideCompressValue; + } + else if(this._overrideCompress) + { + compress.value = this._compress; + } + else + { + compress.value = this._fixedConnection.endpoint().compress(); + } + return this._fixedConnection; + }, + getConnection: function() + { + var promise = new Promise(); // success callback receives (connection, compress) + try + { + var compress = { 'value': false }; + var connection = this.getConnectionInternal(compress); + promise.succeed(connection, compress.value); + } + catch(ex) + { + promise.fail(ex); + } + return promise; + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + if(!(rhs instanceof FixedReference)) + { + return false; + } + if(!Reference.prototype.equals.call(this, rhs)) + { + return false; + } + return this._fixedConnection.equals(rhs._fixedConnection); + } + }); + + Ice.FixedReference = FixedReference; + + var RoutableReference = Class(Reference, { + __init__: function(instance, communicator, identity, facet, mode, secure, protocol, encoding, endpoints, + adapterId, locatorInfo, routerInfo, cacheConnection, preferSecure, endpointSelection, + locatorCacheTimeout) + { + Reference.call(this, instance, communicator, identity, facet, mode, secure, protocol, encoding); + this._endpoints = endpoints; + this._adapterId = adapterId; + this._locatorInfo = locatorInfo; + this._routerInfo = routerInfo; + this._cacheConnection = cacheConnection; + this._preferSecure = preferSecure; + this._endpointSelection = endpointSelection; + this._locatorCacheTimeout = locatorCacheTimeout; + this._overrideTimeout = false; + this._timeout = -1; + + if(this._endpoints === null) + { + this._endpoints = Reference._emptyEndpoints; + } + if(this._adapterId === null) + { + this._adapterId = ""; + } + this._connectionId = ""; + Debug.assert(this._adapterId.length === 0 || this._endpoints.length === 0); + }, + getEndpoints: function() + { + return this._endpoints; + }, + getAdapterId: function() + { + return this._adapterId; + }, + getRouterInfo: function() + { + return this._routerInfo; + }, + getLocatorInfo: function() + { + return this._locatorInfo; + }, + getCacheConnection: function() + { + return this._cacheConnection; + }, + getPreferSecure: function() + { + return this._preferSecure; + }, + getEndpointSelection: function() + { + return this._endpointSelection; + }, + getLocatorCacheTimeout: function() + { + return this._locatorCacheTimeout; + }, + getConnectionId: function() + { + return this._connectionId; + }, + changeEncoding: function(newEncoding) + { + var r = Reference.prototype.changeEncoding.call(this, newEncoding); + if(r !== this) + { + var locInfo = r._locatorInfo; + if(locInfo !== null && !locInfo.getLocator().ice_getEncodingVersion().equals(newEncoding)) + { + r._locatorInfo = this.getInstance().locatorManager().find( + locInfo.getLocator().ice_encodingVersion(newEncoding)); + } + } + return r; + }, + changeCompress: function(newCompress) + { + var r = Reference.prototype.changeCompress.call(this, newCompress); + if(r !== this && this._endpoints.length > 0) // Also override the compress flag on the endpoints if it was updated. + { + var newEndpoints = []; + for(var i = 0; i < this._endpoints.length; i++) + { + newEndpoints[i] = this._endpoints[i].changeCompress(newCompress); + } + r._endpoints = newEndpoints; + } + return r; + }, + changeAdapterId: function(newAdapterId) + { + if(this._adapterId === newAdapterId) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._adapterId = newAdapterId; + r._endpoints = Reference._emptyEndpoints; + return r; + }, + changeEndpoints: function(newEndpoints) + { + if(ArrayUtil.equals(newEndpoints, this._endpoints, function(e1, e2) { return e1.equals(e2); })) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._endpoints = newEndpoints; + r._adapterId = ""; + r.applyOverrides(r._endpoints); + return r; + }, + changeLocator: function(newLocator) + { + var newLocatorInfo = this.getInstance().locatorManager().find(newLocator); + if(newLocatorInfo !== null && this._locatorInfo !== null && newLocatorInfo.equals(this._locatorInfo)) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._locatorInfo = newLocatorInfo; + return r; + }, + changeRouter: function(newRouter) + { + var newRouterInfo = this.getInstance().routerManager().find(newRouter); + if(newRouterInfo !== null && this._routerInfo !== null && newRouterInfo.equals(this._routerInfo)) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._routerInfo = newRouterInfo; + return r; + }, + changeCacheConnection: function(newCache) + { + if(newCache === this._cacheConnection) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._cacheConnection = newCache; + return r; + }, + changePreferSecure: function(newPreferSecure) + { + if(newPreferSecure === this._preferSecure) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._preferSecure = newPreferSecure; + return r; + }, + changeEndpointSelection: function(newType) + { + if(newType === this._endpointSelection) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._endpointSelection = newType; + return r; + }, + changeLocatorCacheTimeout: function(newTimeout) + { + if(this._locatorCacheTimeout === newTimeout) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._locatorCacheTimeout = newTimeout; + return r; + }, + changeTimeout: function(newTimeout) + { + if(this._overrideTimeout && this._timeout === newTimeout) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._timeout = newTimeout; + r._overrideTimeout = true; + if(this._endpoints.length > 0) + { + var newEndpoints = []; + for(var i = 0; i < this._endpoints.length; i++) + { + newEndpoints[i] = this._endpoints[i].changeTimeout(newTimeout); + } + r._endpoints = newEndpoints; + } + return r; + }, + changeConnectionId: function(id) + { + if(this._connectionId === id) + { + return this; + } + var r = this.getInstance().referenceFactory().copy(this); + r._connectionId = id; + if(this._endpoints.length > 0) + { + var newEndpoints = []; + for(var i = 0; i < this._endpoints.length; i++) + { + newEndpoints[i] = this._endpoints[i].changeConnectionId(id); + } + r._endpoints = newEndpoints; + } + return r; + }, + isIndirect: function() + { + return this._endpoints.length === 0; + }, + isWellKnown: function() + { + return this._endpoints.length === 0 && this._adapterId.length === 0; + }, + streamWrite: function(s) + { + Reference.prototype.streamWrite.call(this, s); + + s.writeSize(this._endpoints.length); + if(this._endpoints.length > 0) + { + Debug.assert(this._adapterId.length === 0); + for(var i = 0; i < this._endpoints.length; ++i) + { + this._endpoints[i].streamWrite(s); + } + } + else + { + s.writeString(this._adapterId); // Adapter id. + } + }, + toString: function() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + var s = []; + s.push(Reference.prototype.toString.call(this)); + if(this._endpoints.length > 0) + { + for(var i = 0; i < this._endpoints.length; ++i) + { + var endp = this._endpoints[i].toString(); + if(endp !== null && endp.length > 0) + { + s.push(':'); + s.push(endp); + } + } + } + else if(this._adapterId.length > 0) + { + s.push(" @ "); + + // + // If the encoded adapter id string contains characters which + // the reference parser uses as separators, then we enclose + // the adapter id string in quotes. + // + var a = StringUtil.escapeString(this._adapterId, null); + if(a.search(/[ :@]/) != -1) + { + s.push('"'); + s.push(a); + s.push('"'); + } + else + { + s.push(a); + } + } + return s.join(""); + }, + toProperty: function(prefix) + { + var properties = new HashMap(), e; + + properties.set(prefix, this.toString()); + properties.set(prefix + ".CollocationOptimized", "0"); + properties.set(prefix + ".ConnectionCached", this._cacheConnection ? "1" : "0"); + properties.set(prefix + ".PreferSecure", this._preferSecure ? "1" : "0"); + properties.set(prefix + ".EndpointSelection", + this._endpointSelection === EndpointSelectionType.Random ? "Random" : "Ordered"); + + properties.set(prefix + ".LocatorCacheTimeout", "" + this._locatorCacheTimeout); + + if(this._routerInfo !== null) + { + var h = this._routerInfo.getRouter(); + var routerProperties = h.__reference().toProperty(prefix + ".Router"); + for(e = routerProperties.entries; e !== null; e = e.next) + { + properties.set(e.key, e.value); + } + } + + if(this._locatorInfo !== null) + { + var p = this._locatorInfo.getLocator(); + var locatorProperties = p.__reference().toProperty(prefix + ".Locator"); + for(e = locatorProperties.entries; e !== null; e = e.next) + { + properties.set(e.key, e.value); + } + } + + return properties; + }, + hashCode: function() + { + if(!this._hashInitialized) + { + Reference.prototype.hashCode.call(this); // Initializes _hashValue. + this._hashValue = HashUtil.addString(this._hashValue, this._adapterId); + } + return this._hashValue; + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + if(!(rhs instanceof RoutableReference)) + { + return false; + } + + if(!Reference.prototype.equals.call(this, rhs)) + { + return false; + } + + if(this._locatorInfo === null ? rhs._locatorInfo !== null : !this._locatorInfo.equals(rhs._locatorInfo)) + { + return false; + } + if(this._routerInfo === null ? rhs._routerInfo !== null : !this._routerInfo.equals(rhs._routerInfo)) + { + return false; + } + if(this._cacheConnection !== rhs._cacheConnection) + { + return false; + } + if(this._preferSecure !== rhs._preferSecure) + { + return false; + } + if(this._endpointSelection !== rhs._endpointSelection) + { + return false; + } + if(this._locatorCacheTimeout !== rhs._locatorCacheTimeout) + { + return false; + } + if(this._connectionId !== rhs._connectionId) + { + return false; + } + if(this._overrideTimeout !== rhs._overrideTimeout) + { + return false; + } + if(this._overrideTimeout && this._timeout !== rhs._timeout) + { + return false; + } + if(!ArrayUtil.equals(this._endpoints, rhs._endpoints, function(e1, e2) { return e1.equals(e2); })) + { + return false; + } + if(this._adapterId !== rhs._adapterId) + { + return false; + } + return true; + }, + getConnection: function() + { + var promise = new Promise(); // success callback receives (connection, compress) + + if(this._routerInfo !== null) + { + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + var self = this; + this._routerInfo.getClientEndpoints().then( + function(endpts) + { + if(endpts.length > 0) + { + self.applyOverrides(endpts); + self.createConnection(endpts).then( + function(connection, compress) + { + promise.succeed(connection, compress); + }, + function(ex) + { + promise.fail(ex); + }); + } + else + { + self.getConnectionNoRouterInfo(promise); + } + }).exception( + function(ex) + { + promise.fail(ex); + }); + } + else + { + this.getConnectionNoRouterInfo(promise); + } + + return promise; + }, + getConnectionNoRouterInfo: function(promise) + { + if(this._endpoints.length > 0) + { + this.createConnection(this._endpoints).then( + function(connection, compress) + { + promise.succeed(connection, compress); + }).exception( + function(ex) + { + promise.fail(ex); + }); + return; + } + + var self = this; + if(this._locatorInfo !== null) + { + this._locatorInfo.getEndpoints(this, null, this._locatorCacheTimeout).then( + function(endpoints, cached) + { + if(endpoints.length === 0) + { + promise.fail(new Ice.NoEndpointException(self.toString())); + return; + } + + self.applyOverrides(endpoints); + self.createConnection(endpoints).then( + function(connection, compress) + { + promise.succeed(connection, compress); + }, + function(ex) + { + if(ex instanceof Ice.NoEndpointException) + { + // + // No need to retry if there's no endpoints. + // + promise.fail(ex); + } + else + { + Debug.assert(self._locatorInfo !== null); + self.getLocatorInfo().clearCache(self); + if(cached) + { + var traceLevels = self.getInstance().traceLevels(); + if(traceLevels.retry >= 2) + { + var s = "connection to cached endpoints failed\n" + + "removing endpoints from cache and trying one more time\n" + + ExUtil.toString(ex); + self.getInstance().initializationData().logger.trace( + traceLevels.retryCat, s); + } + self.getConnectionNoRouterInfo(promise); // Retry. + return; + } + promise.fail(ex); + } + }); + }).exception( + function(ex) + { + promise.fail(ex); + }); + } + else + { + promise.fail(new Ice.NoEndpointException(this.toString())); + } + }, + clone: function() + { + var r = new RoutableReference(this.getInstance(), this.getCommunicator(), this.getIdentity(), this.getFacet(), + this.getMode(), this.getSecure(), this.getProtocol(), this.getEncoding(), + this._endpoints, this._adapterId, this._locatorInfo, this._routerInfo, + this._cacheConnection, this._preferSecure, this._endpointSelection, + this._locatorCacheTimeout); + this.copyMembers(r); + return r; + }, + copyMembers: function(rhs) + { + // + // Copy the members that are not passed to the constructor. + // + Reference.prototype.copyMembers.call(this, rhs); + rhs._overrideTimeout = this._overrideTimeout; + rhs._timeout = this._timeout; + rhs._connectionId = this._connectionId; + }, + applyOverrides: function(endpts) + { + // + // Apply the endpoint overrides to each endpoint. + // + for(var i = 0; i < endpts.length; ++i) + { + endpts[i] = endpts[i].changeConnectionId(this._connectionId); + if(this._overrideCompress) + { + endpts[i] = endpts[i].changeCompress(this._compress); + } + if(this._overrideTimeout) + { + endpts[i] = endpts[i].changeTimeout(this._timeout); + } + } + }, + filterEndpoints: function(allEndpoints) + { + var endpoints = []; + + // + // Filter out opaque endpoints. + // + for(var i = 0; i < allEndpoints.length; ++i) + { + if(!(allEndpoints[i] instanceof OpaqueEndpointI)) + { + endpoints.push(allEndpoints[i]); + } + } + + // + // Filter out endpoints according to the mode of the reference. + // + switch(this.getMode()) + { + case RefMode.ModeTwoway: + case RefMode.ModeOneway: + case RefMode.ModeBatchOneway: + { + // + // Filter out datagram endpoints. + // + endpoints = ArrayUtil.filter(endpoints, function(e, index, arr) { return !e.datagram(); }); + break; + } + + case RefMode.ModeDatagram: + case RefMode.ModeBatchDatagram: + { + // + // Filter out non-datagram endpoints. + // + endpoints = ArrayUtil.filter(endpoints, function(e, index, arr) { return e.datagram(); }); + break; + } + } + + // + // Sort the endpoints according to the endpoint selection type. + // + switch(this.getEndpointSelection()) + { + case EndpointSelectionType.Random: + { + // + // Shuffle the endpoints. + // + ArrayUtil.shuffle(endpoints); + break; + } + case EndpointSelectionType.Ordered: + { + // Nothing to do. + break; + } + default: + { + Debug.assert(false); + break; + } + } + + // + // If a secure connection is requested or secure overrides is + // set, remove all non-secure endpoints. Otherwise if preferSecure is set + // make secure endpoints prefered. By default make non-secure + // endpoints preferred over secure endpoints. + // + var overrides = this.getInstance().defaultsAndOverrides(); + if(overrides.overrideSecure ? overrides.overrideSecureValue : this.getSecure()) + { + endpoints = ArrayUtil.filter(endpoints, function(e, index, arr) { return e.secure(); }); + } + else + { + var preferSecure = this.getPreferSecure(); + var compare = function(e1, e2) + { + var ls = e1.secure(); + var rs = e2.secure(); + if((ls && rs) || (!ls && !rs)) + { + return 0; + } + else if(!ls && rs) + { + return preferSecure ? 1 : -1; + } + else + { + return preferSecure ? -1 : 1; + } + }; + endpoints.sort(compare); + } + return endpoints; + }, + createConnection: function(allEndpoints) + { + var endpoints = this.filterEndpoints(allEndpoints); + if(endpoints.length === 0) + { + return new Promise().fail(new Ice.NoEndpointException(this.toString())); + } + + // + // Finally, create the connection. + // + var promise = new Promise(); + var factory = this.getInstance().outgoingConnectionFactory(); + var cb; + if(this.getCacheConnection() || endpoints.length == 1) + { + // + // Get an existing connection or create one if there's no + // existing connection to one of the given endpoints. + // + cb = new CreateConnectionCallback(this, null, promise); + factory.create(endpoints, false, this.getEndpointSelection()).then( + function(connection, compress) + { + cb.setConnection(connection, compress); + }).exception( + function(ex) + { + cb.setException(ex); + }); + } + else + { + // + // Go through the list of endpoints and try to create the + // connection until it succeeds. This is different from just + // calling create() with the given endpoints since this might + // create a new connection even if there's an existing + // connection for one of the endpoints. + // + var v = [ endpoints[0] ]; + cb = new CreateConnectionCallback(this, endpoints, promise); + factory.create(v, true, this.getEndpointSelection()).then( + function(connection, compress) + { + cb.setConnection(connection, compress); + }).exception( + function(ex) + { + cb.setException(ex); + }); + } + + return promise; + } + }); + + Ice.RoutableReference = RoutableReference; + global.Ice = Ice; + + var CreateConnectionCallback = Class({ + __init__: function(r, endpoints, promise) + { + this.ref = r; + this.endpoints = endpoints; + this.promise = promise; + this.i = 0; + this.exception = null; + }, + setConnection: function(connection, compress) + { + // + // If we have a router, set the object adapter for this router + // (if any) to the new connection, so that callbacks from the + // router can be received over this new connection. + // + if(this.ref.getRouterInfo() !== null && this.ref.getRouterInfo().getAdapter() !== null) + { + connection.setAdapter(this.ref.getRouterInfo().getAdapter()); + } + this.promise.succeed(connection, compress); + }, + setException: function(ex) + { + if(this.exception === null) + { + this.exception = ex; + } + + if(this.endpoints === null || ++this.i === this.endpoints.length) + { + this.promise.fail(this.exception); + return; + } + + var more = this.i != this.endpoints.length - 1; + var arr = [ this.endpoints[this.i] ]; + var self = this; + this.ref.getInstance().outgoingConnectionFactory().create(arr, more, this.ref.getEndpointSelection()).then( + function(connection, compress) + { + self.setConnection(connection, compress); + }).exception( + function(ex) + { + self.setException(ex); + }); + } + }); +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ReferenceMode.js b/js/src/Ice/ReferenceMode.js new file mode 100644 index 00000000000..4a6de00fd7a --- /dev/null +++ b/js/src/Ice/ReferenceMode.js @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + // + // Using a separate module for these constants so that ObjectPrx does + // not need to include Reference. + // + var ReferenceMode = + { + ModeTwoway: 0, + ModeOneway: 1, + ModeBatchOneway: 2, + ModeDatagram: 3, + ModeBatchDatagram: 4, + ModeLast: 4 + }; + + Ice.ReferenceMode = ReferenceMode; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/RetryQueue.js b/js/src/Ice/RetryQueue.js new file mode 100644 index 00000000000..5462933c15f --- /dev/null +++ b/js/src/Ice/RetryQueue.js @@ -0,0 +1,83 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + + var Class = Ice.Class; + + var RetryQueue = Class({ + __init__: function(instance) + { + this._instance = instance; + this._requests = []; + }, + add: function(outAsync, interval) + { + var task = new RetryTask(this, outAsync); + this._instance.timer().schedule(function() + { + task.run(); + }, interval); + this._requests.push(task); + }, + destroy: function() + { + for(var i = 0; i < this._requests.length; ++i) + { + this._requests[i].destroy(); + } + this._requests = []; + }, + remove: function(task) + { + var idx = this._requests.indexOf(task); + if(idx >= 0) + { + this._requests.splice(idx, 1); + return true; + } + return false; + } + }); + Ice.RetryQueue = RetryQueue; + + var RetryTask = Class({ + __init__: function(queue, outAsync, interval) + { + this.queue = queue; + this.outAsync = outAsync; + }, + run: function() + { + if(this.queue.remove(this)) + { + try + { + this.outAsync.__send(); + } + catch(ex) + { + this.outAsync.__exception(ex); + } + } + }, + destroy: function() + { + this.outAsync.__exception(new Ice.CommunicatorDestroyedException()); + } + }); + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/RouterInfo.js b/js/src/Ice/RouterInfo.js new file mode 100644 index 00000000000..1a15ee3c414 --- /dev/null +++ b/js/src/Ice/RouterInfo.js @@ -0,0 +1,229 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/ArrayUtil"); + require("Ice/Debug"); + require("Ice/HashMap"); + require("Ice/Promise"); + require("Ice/LocalException"); + require("Ice/Exception"); + + var Ice = global.Ice || {}; + + var ArrayUtil = Ice.ArrayUtil; + var Debug = Ice.Debug; + var HashMap = Ice.HashMap; + var Promise = Ice.Promise; + + var RouterInfo = Ice.Class({ + __init__: function(router) + { + this._router = router; + + Debug.assert(this._router !== null); + + this._clientEndpoints = null; + this._serverEndpoints = null; + this._adapter = null; + this._identities = new HashMap(); // Set<Identity> = Map<Identity, 1> + this._identities.keyComparator = HashMap.compareEquals; + this._evictedIdentities = []; + }, + destroy: function() + { + this._clientEndpoints = []; + this._serverEndpoints = []; + this._adapter = null; + this._identities.clear(); + }, + equals: function(rhs) + { + if(this === rhs) + { + return true; + } + + if(rhs instanceof RouterInfo) + { + return this._router.equals(rhs._router); + } + + return false; + }, + hashCode: function() + { + return this._router.hashCode(); + }, + getRouter: function() + { + // + // No mutex lock necessary, _router is immutable. + // + return this._router; + }, + getClientEndpoints: function() + { + var promise = new Promise(); + + if(this._clientEndpoints !== null) + { + promise.succeed(this._clientEndpoints); + } + else + { + var self = this; + this._router.getClientProxy().then( + function(clientProxy) + { + self.setClientEndpoints(clientProxy, promise); + }).exception( + function(ex) + { + promise.fail(ex); + }); + } + + return promise; + }, + getServerEndpoints: function() + { + if(this._serverEndpoints !== null) // Lazy initialization. + { + return new Promise().succeed(this._serverEndpoints); + } + else + { + var self = this; + return this._router.getServerProxy().then( + function(proxy) + { + return self.setServerEndpoints(proxy); + }); + } + }, + addProxy: function(proxy) + { + Debug.assert(proxy !== null); + + if(this._identities.has(proxy.ice_getIdentity())) + { + // + // Only add the proxy to the router if it's not already in our local map. + // + return new Promise().succeed(); + } + else + { + var self = this; + return this._router.addProxies([ proxy ]).then( + function(evictedProxies) + { + self.addAndEvictProxies(proxy, evictedProxies); + }); + } + }, + setAdapter: function(adapter) + { + this._adapter = adapter; + }, + getAdapter: function() + { + return this._adapter; + }, + clearCache: function(ref) + { + this._identities.delete(ref.getIdentity()); + }, + setClientEndpoints: function(clientProxy, promise) + { + if(this._clientEndpoints === null) + { + if(clientProxy === null) + { + // + // If getClientProxy() return nil, use router endpoints. + // + this._clientEndpoints = this._router.__reference().getEndpoints(); + promise.succeed(this._clientEndpoints); + } + else + { + clientProxy = clientProxy.ice_router(null); // The client proxy cannot be routed. + + // + // In order to avoid creating a new connection to the + // router, we must use the same timeout as the already + // existing connection. + // + var self = this; + this._router.ice_getConnection().then( + function(con) + { + var proxy = clientProxy.ice_timeout(con.timeout()); + self._clientEndpoints = proxy.__reference().getEndpoints(); + promise.succeed(self._clientEndpoints); + }).exception( + function(ex) + { + promise.fail(ex); + }); + } + } + else + { + promise.succeed(this._clientEndpoints); + } + }, + setServerEndpoints: function(serverProxy) + { + if(serverProxy === null) + { + throw new Ice.NoEndpointException(); + } + + serverProxy = serverProxy.ice_router(null); // The server proxy cannot be routed. + this._serverEndpoints = serverProxy.__reference().getEndpoints(); + return this._serverEndpoints; + }, + addAndEvictProxies: function(proxy, evictedProxies) + { + // + // Check if the proxy hasn't already been evicted by a + // concurrent addProxies call. If it's the case, don't + // add it to our local map. + // + var index = ArrayUtil.indexOf(this._evictedIdentities, proxy.ice_getIdentity(), + function(i1, i2) { return i1.equals(i2); }); + if(index >= 0) + { + this._evictedIdentities.splice(index, 1); + } + else + { + // + // If we successfully added the proxy to the router, + // we add it to our local map. + // + this._identities.set(proxy.ice_getIdentity(), 1); + } + + // + // We also must remove whatever proxies the router evicted. + // + for(var i = 0; i < evictedProxies.length; ++i) + { + this._identities.delete(evictedProxies[i].ice_getIdentity()); + } + } + }); + Ice.RouterInfo = RouterInfo; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/RouterManager.js b/js/src/Ice/RouterManager.js new file mode 100644 index 00000000000..74156908bdd --- /dev/null +++ b/js/src/Ice/RouterManager.js @@ -0,0 +1,77 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/HashMap"); + require("Ice/RouterInfo"); + require("Ice/Router"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var RouterInfo = Ice.RouterInfo; + var RouterPrx = Ice.RouterPrx; + + var RouterManager = Ice.Class({ + __init__: function() + { + this._table = new HashMap(); // Map<Ice.RouterPrx, RouterInfo> + this._table.keyComparator = HashMap.compareEquals; + }, + destroy: function() + { + for(var e = this._table.entries; e !== null; e = e.next) + { + e.value.destroy(); + } + this._table.clear(); + }, + // + // Returns router info for a given router. Automatically creates + // the router info if it doesn't exist yet. + // + find: function(rtr) + { + if(rtr === null) + { + return null; + } + + // + // The router cannot be routed. + // + var router = RouterPrx.uncheckedCast(rtr.ice_router(null)); + + var info = this._table.get(router); + if(info === undefined) + { + info = new RouterInfo(router); + this._table.set(router, info); + } + + return info; + }, + erase: function(rtr) + { + var info = null; + if(rtr !== null) + { + // The router cannot be routed. + var router = RouterPrx.uncheckedCast(rtr.ice_router(null)); + + info = this._table.get(router); + this._table.delete(router); + } + return info; + } + }); + Ice.RouterManager = RouterManager; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/ServantManager.js b/js/src/Ice/ServantManager.js new file mode 100644 index 00000000000..fef9f26c0e2 --- /dev/null +++ b/js/src/Ice/ServantManager.js @@ -0,0 +1,297 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/HashMap"); + require("Ice/LocalException"); + require("Ice/StringUtil"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var HashMap = Ice.HashMap; + var StringUtil = Ice.StringUtil; + + // + // Only for use by Ice.ObjectAdatperI. + // + var ServantManager = Ice.Class({ + __init__: function(instance, adapterName) + { + this._instance = instance; + this._adapterName = adapterName; + this._servantMapMap = new HashMap(); // Map<Ice.Identity, Map<String, Ice.Object> > + this._servantMapMap.keyComparator = HashMap.compareEquals; + this._defaultServantMap = new HashMap(); // Map<String, Ice.Object> + this._locatorMap = new HashMap(); // Map<String, Ice.ServantLocator> + }, + addServant: function(servant, ident, facet) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + if(facet === null) + { + facet = ""; + } + + var m = this._servantMapMap.get(ident); + if(m === undefined) + { + m = new HashMap(); + this._servantMapMap.set(ident, m); + } + else + { + if(m.has(facet)) + { + var ex = new Ice.AlreadyRegisteredException(); + ex.id = this._instance.identityToString(ident); + ex.kindOfObject = "servant"; + if(facet.length > 0) + { + ex.id += " -f " + StringUtil.escapeString(facet, ""); + } + throw ex; + } + } + + m.set(facet, servant); + }, + addDefaultServant: function(servant, category) + { + Debug.assert(this._instance !== null); // Must not be called after destruction + + var obj = this._defaultServantMap.get(category); + if(obj !== undefined) + { + var ex = new Ice.AlreadyRegisteredException(); + ex.kindOfObject = "default servant"; + ex.id = category; + throw ex; + } + + this._defaultServantMap.set(category, servant); + }, + removeServant: function(ident, facet) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + if(facet === null) + { + facet = ""; + } + + var m = this._servantMapMap.get(ident); + if(m === undefined || !m.has(facet)) + { + var ex = new Ice.NotRegisteredException(); + ex.id = this._instance.identityToString(ident); + ex.kindOfObject = "servant"; + if(facet.length > 0) + { + ex.id += " -f " + StringUtil.escapeString(facet, ""); + } + throw ex; + } + + var obj = m.get(facet); + m.delete(facet); + + if(m.size === 0) + { + this._servantMapMap.delete(ident); + } + + return obj; + }, + removeDefaultServant: function(category) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var obj = this._defaultServantMap.get(category); + if(obj === undefined) + { + var ex = new Ice.NotRegisteredException(); + ex.kindOfObject = "default servant"; + ex.id = category; + throw ex; + } + + this._defaultServantMap.delete(category); + return obj; + }, + removeAllFacets: function(ident) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var m = this._servantMapMap.get(ident); + if(m === undefined) + { + var ex = new Ice.NotRegisteredException(); + ex.id = this._instance.identityToString(ident); + ex.kindOfObject = "servant"; + throw ex; + } + + this._servantMapMap.delete(ident); + + return m; + }, + findServant: function(ident, facet) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + //Debug.assert(this._instance !== null); // Must not be called after destruction. + + if(facet === null) + { + facet = ""; + } + + var m = this._servantMapMap.get(ident); + var obj = null; + if(m === undefined) + { + obj = this._defaultServantMap.get(ident.category); + if(obj === undefined) + { + obj = this._defaultServantMap.get(""); + } + } + else + { + obj = m.get(facet); + } + + return obj === undefined ? null : obj; + }, + findDefaultServant: function(category) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var ds = this._defaultServantMap.get(category); + return ds === undefined ? null : ds; + }, + findAllFacets: function(ident) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var m = this._servantMapMap.get(ident); + if(m !== undefined) + { + return m.clone(); + } + + return new HashMap(); + }, + hasServant: function(ident) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + //Debug.assert(this._instance !== null); // Must not be called after destruction. + + var m = this._servantMapMap.get(ident); + if(m === undefined) + { + return false; + } + else + { + Debug.assert(m.size > 0); + return true; + } + }, + addServantLocator: function(locator, category) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var l = this._locatorMap.get(category); + if(l !== undefined) + { + var ex = new Ice.AlreadyRegisteredException(); + ex.id = StringUtil.escapeString(category, ""); + ex.kindOfObject = "servant locator"; + throw ex; + } + + this._locatorMap.set(category, locator); + }, + removeServantLocator: function(category) + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + + var l = this._locatorMap.get(category); + if(l === undefined) + { + var ex = new Ice.NotRegisteredException(); + ex.id = StringUtil.escapeString(category, ""); + ex.kindOfObject = "servant locator"; + throw ex; + } + this._locatorMap.delete(category); + return l; + }, + findServantLocator: function(category) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + //Debug.assert(this._instance !== null); // Must not be called after destruction. + + var l = this._locatorMap.get(category); + return l === undefined ? null : l; + }, + // + // Only for use by Ice.ObjectAdapterI. + // + destroy: function() + { + Debug.assert(this._instance !== null); // Must not be called after destruction. + var logger = this._instance.initializationData().logger; + this._servantMapMap.clear(); + + var locatorMap = this._locatorMap.clone(); + this._locatorMap.clear(); + this._instance = null; + + for(var e = locatorMap.entries; e !== null; e = e.next) + { + var locator = e.value; + try + { + locator.deactivate(e.key); + } + catch(ex) + { + var s = "exception during locator deactivation:\n" + "object adapter: `" + this._adapterName + + "'\n" + "locator category: `" + e.key + "'\n" + ExUtil.toString(ex); + logger.error(s); + } + } + } + }); + + Ice.ServantManager = ServantManager; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/SocketOperation.js b/js/src/Ice/SocketOperation.js new file mode 100644 index 00000000000..6e45a494dcf --- /dev/null +++ b/js/src/Ice/SocketOperation.js @@ -0,0 +1,23 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var SocketOperation = + { + None: 0, + Read: 1, + Write: 2, + Connect: 2 // Same as Write + }; + + Ice.SocketOperation = SocketOperation; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/StreamHelpers.js b/js/src/Ice/StreamHelpers.js new file mode 100644 index 00000000000..6fcce91a4d0 --- /dev/null +++ b/js/src/Ice/StreamHelpers.js @@ -0,0 +1,327 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/HashMap"); + require("Ice/OptionalFormat"); + + var Ice = global.Ice || {}; + + var Class = Ice.Class; + var defineProperty = Object.defineProperty; + + var HashMap = Ice.HashMap; + var OptionalFormat = Ice.OptionalFormat; + + var StreamHelpers = {}; + + StreamHelpers.FSizeOptHelper = function() + { + this.writeOpt = function(os, tag, v) + { + if(v !== undefined && os.writeOpt(tag, OptionalFormat.FSize)) + { + var pos = os.startSize(); + this.write(os, v); + os.endSize(pos); + } + }; + + this.readOpt = function(is, tag) + { + var v; + if(is.readOpt(tag, OptionalFormat.FSize)) + { + is.skip(4); + v = this.read(is); + } + return v; + }; + }; + + StreamHelpers.VSizeOptHelper = function() + { + this.writeOpt = function(os, tag, v) + { + if(v !== undefined && os.writeOpt(tag, OptionalFormat.VSize)) + { + os.writeSize(this.minWireSize); + this.write(os, v); + } + }; + + this.readOpt = function(is, tag) + { + var v; + if(is.readOpt(tag, OptionalFormat.VSize)) + { + is.skipSize(); + v = this.read(is); + } + return v; + }; + }; + + StreamHelpers.VSizeContainerOptHelper = function(elementSize) + { + this.writeOpt = function(os, tag, v) + { + if(v !== undefined && os.writeOpt(tag, OptionalFormat.VSize)) + { + var sz = this.size(v); + os.writeSize(sz > 254 ? sz * elementSize + 5 : sz * elementSize + 1); + this.write(os, v); + } + }; + + this.readOpt = function(is, tag) + { + var v; + if(is.readOpt(tag, OptionalFormat.VSize)) + { + is.skipSize(); + v = this.read(is); + } + return v; + }; + }; + + StreamHelpers.VSizeContainer1OptHelper = function() + { + this.writeOpt = function(os, tag, v) + { + if(v !== undefined && os.writeOpt(tag, OptionalFormat.VSize)) + { + this.write(os, v); + } + }; + + this.readOpt = function(is, tag) + { + var v; + if(is.readOpt(tag, OptionalFormat.VSize)) + { + v = this.read(is); + } + return v; + }; + }; + + // + // Sequence helper to write sequences + // + var SequenceHelper = Class({ + write: function(os, v) + { + if(v === null || v.length === 0) + { + os.writeSize(0); + } + else + { + var helper = this.elementHelper; + os.writeSize(v.length); + for(var i = 0; i < v.length; ++i) + { + helper.write(os, v[i]); + } + } + }, + read: function(is) + { + var helper = this.elementHelper; // Cache the element helper. + var sz = is.readAndCheckSeqSize(helper.minWireSize); + var v = []; + v.length = sz; + for(var i = 0; i < sz; ++i) + { + v[i] = helper.read(is); + } + return v; + }, + size: function(v) + { + return (v === null || v === undefined) ? 0 : v.length; + } + }); + + defineProperty(SequenceHelper.prototype, "minWireSize", { + get: function(){ return 1; } + }); + + // Speacialization optimized for ByteSeq + var byteSeqHelper = new SequenceHelper(); + byteSeqHelper.write = function(os, v) { return os.writeByteSeq(v); }; + byteSeqHelper.read = function(is) { return is.readByteSeq(); }; + defineProperty(byteSeqHelper, "elementHelper", { + get: function(){ return Ice.ByteHelper; } + }); + StreamHelpers.VSizeContainer1OptHelper.call(byteSeqHelper); + + // Read method for object sequences + var objectSequenceHelperRead = function(is) + { + var sz = is.readAndCheckSeqSize(1); + var v = []; + v.length = sz; + var elementType = this.elementType; + var readObjectAtIndex = function(idx) + { + is.readObject(function(obj) { v[idx] = obj; }, elementType); + }; + + for(var i = 0; i < sz; ++i) + { + readObjectAtIndex(i); + } + return v; + }; + + StreamHelpers.generateSeqHelper = function(elementHelper, fixed, elementType) + { + if(elementHelper === Ice.ByteHelper) + { + return byteSeqHelper; + } + + var helper = new SequenceHelper(); + if(fixed) + { + if(elementHelper.minWireSize === 1) + { + StreamHelpers.VSizeContainer1OptHelper.call(helper); + } + else + { + StreamHelpers.VSizeContainerOptHelper.call(helper, elementHelper.minWireSize); + } + } + else + { + StreamHelpers.FSizeOptHelper.call(helper); + } + + defineProperty(helper, "elementHelper", { + get: function(){ return elementHelper; } + }); + + if(elementHelper == Ice.ObjectHelper) + { + defineProperty(helper, "elementType", { + get: function(){ return elementType; } + }); + helper.read = objectSequenceHelperRead; + } + + return helper; + }; + + // + // Dictionary helper to write dictionaries + // + var DictionaryHelper = Class({ + write: function(os, v) + { + if(v === null || v.size === 0) + { + os.writeSize(0); + } + else + { + var keyHelper = this.keyHelper; + var valueHelper = this.valueHelper; + os.writeSize(v.size); + for(var e = v.entries; e !== null; e = e.next) + { + keyHelper.write(os, e.key); + valueHelper.write(os, e.value); + } + } + }, + read: function(is) + { + var mapType = this.mapType; + var v = new mapType(); + var sz = is.readSize(); + var keyHelper = this.keyHelper; + var valueHelper = this.valueHelper; + for(var i = 0; i < sz; ++i) + { + v.set(keyHelper.read(is), valueHelper.read(is)); + } + return v; + }, + size: function(v) + { + return (v === null || v === undefined) ? 0 : v.size; + } + }); + + Object.defineProperty(DictionaryHelper.prototype, "minWireSize", { + get: function(){ return 1; } + }); + + // Read method for dictionaries of objects + var objectDictionaryHelperRead = function(is) + { + var sz = is.readSize(); + var mapType = this.mapType; + var v = new mapType(); + var valueType = this.valueType; + + var readObjectForKey = function(key) + { + is.readObject(function(obj) { v.set(key, obj); }, valueType); + }; + + var keyHelper = this.keyHelper; + for(var i = 0; i < sz; ++i) + { + readObjectForKey(keyHelper.read(is)); + } + return v; + }; + + StreamHelpers.generateDictHelper = function(keyHelper, valueHelper, fixed, valueType, mapType) + { + var helper = new DictionaryHelper(); + if(fixed) + { + StreamHelpers.VSizeContainerOptHelper.call(helper, keyHelper.minWireSize + valueHelper.minWireSize); + } + else + { + StreamHelpers.FSizeOptHelper.call(helper); + } + defineProperty(helper, "mapType", { + get: function(){ return mapType; } + }); + defineProperty(helper, "keyHelper", { + get: function(){ return keyHelper; } + }); + defineProperty(helper, "valueHelper", { + get: function(){ return valueHelper; } + }); + + if(valueHelper == Ice.ObjectHelper) + { + defineProperty(helper, "valueType", { + get: function(){ return valueType; } + }); + helper.read = objectDictionaryHelperRead; + } + + return helper; + }; + + Ice.StreamHelpers = StreamHelpers; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/StringUtil.js b/js/src/Ice/StringUtil.js new file mode 100644 index 00000000000..f7866d3d92a --- /dev/null +++ b/js/src/Ice/StringUtil.js @@ -0,0 +1,490 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Debug"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + + var StringUtil = {}; + + // + // Return the index of the first character in str to + // appear in match, starting from start. Returns -1 if none is + // found. + // + StringUtil.findFirstOf = function(str, match, start) + { + start = start === undefined ? 0 : start; + + var len = str.length; + for(var i = start; i < len; i++) + { + var ch = str.charAt(i); + if(match.indexOf(ch) != -1) + { + return i; + } + } + + return -1; + }; + + // + // Return the index of the first character in str which does + // not appear in match, starting from start. Returns -1 if none is + // found. + // + StringUtil.findFirstNotOf = function(str, match, start) + { + start = start === undefined ? 0 : start; + + var len = str.length; + for(var i = start; i < len; i++) + { + var ch = str.charAt(i); + if(match.indexOf(ch) == -1) + { + return i; + } + } + + return -1; + }; + + // + // Write the byte b as an escape sequence if it isn't a printable ASCII + // character and append the escape sequence to sb. Additional characters + // that should be escaped can be passed in special. If b is any of these + // characters, b is preceded by a backslash in sb. + // + function encodeChar(b, sb, special) + { + switch(b) + { + case 92: // '\\' + { + sb.push("\\\\"); + break; + } + case 39: // '\'' + { + sb.push("\\'"); + break; + } + case 34: // '"' + { + sb.push("\\\""); + break; + } + case 8: // '\b' + { + sb.push("\\b"); + break; + } + case 12: // '\f' + { + sb.push("\\f"); + break; + } + case 10: // '\n' + { + sb.push("\\n"); + break; + } + case 13: // '\r' + { + sb.push("\\r"); + break; + } + case 9: // '\t' + { + sb.push("\\t"); + break; + } + default: + { + if(!(b >= 32 && b <= 126)) + { + sb.push('\\'); + var octal = b.toString(8); + // + // Add leading zeroes so that we avoid problems during + // decoding. For example, consider the encoded string + // \0013 (i.e., a character with value 1 followed by + // the character '3'). If the leading zeroes were omitted, + // the result would be incorrectly interpreted by the + // decoder as a single character with value 11. + // + for(var j = octal.length; j < 3; j++) + { + sb.push('0'); + } + sb.push(octal); + } + else + { + var c = String.fromCharCode(b); + if(special !== null && special.indexOf(c) !== -1) + { + sb.push('\\'); + sb.push(c); + } + else + { + sb.push(c); + } + } + } + } + } + + // + // Add escape sequences (such as "\n", or "\007") to make a string + // readable in ASCII. Any characters that appear in special are + // prefixed with a backlash in the returned string. + // + StringUtil.escapeString = function(s, special) + { + special = special === undefined ? null : special; + + var i, length; + if(special !== null) + { + for(i = 0, length = special.length; i < length; ++i) + { + if(special.charCodeAt(i) < 32 || special.charCodeAt(i) > 126) + { + throw new Error("special characters must be in ASCII range 32-126"); + } + } + } + + var result = [], c; + for(i = 0, length = s.length; i < length; ++i) + { + c = s.charCodeAt(i); + if(c < 128) + { + encodeChar(c, result, special); + } + else if(c > 127 && c < 2048) + { + encodeChar((c >> 6) | 192, result, special); + encodeChar((c & 63) | 128, result, special); + } + else + { + encodeChar((c >> 12) | 224, result, special); + encodeChar(((c >> 6) & 63) | 128, result, special); + encodeChar((c & 63) | 128, result, special); + } + } + + return result.join(""); + }; + + function checkChar(s, pos) + { + var n = s.charCodeAt(pos); + if(!(n >= 32 && n <= 126)) + { + var msg; + if(pos > 0) + { + msg = "character after `" + s.substring(0, pos) + "'"; + } + else + { + msg = "first character"; + } + msg += " is not a printable ASCII character (ordinal " + n + ")"; + throw new Error(msg); + } + return n; + } + + // + // Decode the character or escape sequence starting at start and return it. + // nextStart is set to the index of the first character following the decoded + // character or escape sequence. + // + function decodeChar(s, start, end, nextStart) + { + Debug.assert(start >= 0); + Debug.assert(end <= s.length); + + if(start >= end) + { + throw new Error("EOF while decoding string"); + } + + var c; + + if(s.charAt(start) != '\\') + { + c = checkChar(s, start++); + } + else + { + if(start + 1 == end) + { + throw new Error("trailing backslash"); + } + switch(s.charAt(++start)) + { + case '\\': + case '\'': + case '"': + { + c = s.charCodeAt(start++); + break; + } + case 'b': + { + ++start; + c = "\b".charCodeAt(0); + break; + } + case 'f': + { + ++start; + c = "\f".charCodeAt(0); + break; + } + case 'n': + { + ++start; + c = "\n".charCodeAt(0); + break; + } + case 'r': + { + ++start; + c = "\r".charCodeAt(0); + break; + } + case 't': + { + ++start; + c = "\t".charCodeAt(0); + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + var octalChars = "01234567"; + var val = 0; + for(var j = 0; j < 3 && start < end; ++j) + { + var ch = s.charAt(start++); + if(octalChars.indexOf(ch) == -1) + { + --start; + break; + } + val = val * 8 + parseInt(ch); + } + if(val > 255) + { + var msg = "octal value \\" + val.toString(8) + " (" + val + ") is out of range"; + throw new Error(msg); + } + c = val; + break; + } + default: + { + c = checkChar(s, start++); + break; + } + } + } + nextStart.value = start; + return c; + } + + // + // Remove escape sequences from s and append the result to sb. + // Return true if successful, false otherwise. + // + function decodeString(s, start, end, arr) + { + var nextStart = { 'value': 0 }, c, c2, c3; + while(start < end) + { + c = decodeChar(s, start, end, nextStart); + start = nextStart.value; + + if(c < 128) + { + arr.push(String.fromCharCode(c)); + } + else if(c > 191 && c < 224) + { + c2 = decodeChar(s, start, end, nextStart); + start = nextStart.value; + arr.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63))); + } + else + { + c2 = decodeChar(s, start, end, nextStart); + start = nextStart.value; + c3 = decodeChar(s, start, end, nextStart); + start = nextStart.value; + arr.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63))); + } + } + } + + // + // Remove escape sequences added by escapeString. Throws Error + // for an invalid input string. + // + StringUtil.unescapeString = function(s, start, end) + { + start = start === undefined ? 0 : start; + end = end === undefined ? s.length : end; + + Debug.assert(start >= 0 && start <= end && end <= s.length); + + var arr = []; + decodeString(s, start, end, arr); + + return arr.join(""); + }; + + // + // Split string helper; returns null for unmatched quotes + // + StringUtil.splitString = function(str, delim) + { + var v = []; + var s = ""; + var pos = 0; + + var quoteChar = null; + while(pos < str.length) + { + if(quoteChar === null && (str.charAt(pos) === '"' || str.charAt(pos) === '\'')) + { + quoteChar = str.charAt(pos++); + continue; // Skip the quote. + } + else if(quoteChar === null && str.charAt(pos) === '\\' && pos + 1 < str.length && + (str.charAt(pos + 1) === '"' || str.charAt(pos + 1) === '\'')) + { + ++pos; // Skip the backslash + } + else if(quoteChar !== null && str.charAt(pos) === '\\' && pos + 1 < str.length && + str.charAt(pos + 1) === quoteChar) + { + ++pos; // Skip the backslash + } + else if(quoteChar !== null && str.charAt(pos) === quoteChar) + { + ++pos; + quoteChar = null; + continue; // Skip the quote. + } + else if(delim.indexOf(str.charAt(pos)) !== -1) + { + if(quoteChar === null) + { + ++pos; + if(s.length > 0) + { + v.push(s); + s = ""; + } + continue; + } + } + + if(pos < str.length) + { + s += str.charAt(pos++); + } + } + + if(s.length > 0) + { + v.push(s); + } + if(quoteChar !== null) + { + return null; // Unmatched quote. + } + + return v; + }; + + // + // If a single or double quotation mark is found at the start position, + // then the position of the matching closing quote is returned. If no + // quotation mark is found at the start position, then 0 is returned. + // If no matching closing quote is found, then -1 is returned. + // + StringUtil.checkQuote = function(s, start) + { + start = start === undefined ? 0 : start; + + var quoteChar = s.charAt(start); + if(quoteChar == '"' || quoteChar == '\'') + { + start++; + var len = s.length; + var pos; + while(start < len && (pos = s.indexOf(quoteChar, start)) != -1) + { + if(s.charAt(pos - 1) != '\\') + { + return pos; + } + start = pos + 1; + } + return -1; // Unmatched quote + } + return 0; // Not quoted + }; + + StringUtil.hashCode = function(s) + { + var hash = 0; + var n = s.length; + + for(var i = 0; i < n; i++) + { + hash = 31 * hash + s.charCodeAt(i); + } + + return hash; + }; + + StringUtil.toInt = function(s) + { + var n = parseInt(s, 10); + if(isNaN(n)) + { + throw new Error("conversion of `" + s + "' to int failed"); + } + return n; + }; + + Ice.StringUtil = StringUtil; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Struct.js b/js/src/Ice/Struct.js new file mode 100644 index 00000000000..99a882089c5 --- /dev/null +++ b/js/src/Ice/Struct.js @@ -0,0 +1,207 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/HashUtil"); + require("Ice/ArrayUtil"); + require("Ice/HashMap"); + require("Ice/StreamHelpers"); + + var Slice = global.Slice || {}; + var Ice = global.Ice || {}; + + var ArrayUtil = Ice.ArrayUtil; + + var eq = function(e1, e2) + { + if(e1 === e2) + { + return true; // If identity compare equals members are equal. + } + else if(e1 === null || e1 === undefined || e2 === null || e2 === undefined) + { + return false; + } + else if(e1.prototype !== e2.prototype) + { + return false; + } + else if(e1 instanceof Ice.HashMap) + { + return e1.equals(e2); + } + else if(typeof e1.equals == "function") + { + return e1.equals(e2); + } + else if(e1 instanceof Array) + { + return ArrayUtil.equals(e1, e2, eq); + } + return false; + }; + + var equals = function(other) + { + if(this === other) + { + return true; + } + + if(other === null || other === undefined) + { + return false; + } + + if(this.prototype !== other.prototype) + { + return false; + } + + var e1, e2; + for(var key in this) + { + + e1 = this[key]; + e2 = other[key]; + if(typeof e1 == "function") + { + continue; // Don't need to compare functions + } + else if(!eq(e1, e2)) + { + return false; + } + } + return true; + }; + + var clone = function() + { + var other = new this.constructor(); + var e; + for(var key in this) + { + e = this[key]; + if(e === undefined || e === null) + { + other[key] = e; + } + else if(typeof e == "function") + { + continue; + } + else if(typeof e.clone == "function") + { + other[key] = e.clone(); + } + else if(e instanceof Array) + { + other[key] = ArrayUtil.clone(e); + } + else + { + other[key] = e; + } + } + return other; + }; + + var memberHashCode = function(h, e) + { + if(typeof e.hashCode == "function") + { + return Ice.HashUtil.addHashable(h, e); + } + else if(e instanceof Array) + { + return Ice.HashUtil.addArray(h, e, memberHashCode); + } + else + { + var t = typeof(e); + if(e instanceof String || t == "string") + { + return Ice.HashUtil.addString(h, e); + } + else if(e instanceof Number || t == "number") + { + return Ice.HashUtil.addNumber(h, e); + } + else if(e instanceof Boolean || t == "boolean") + { + return Ice.HashUtil.addBoolean(h, e); + } + } + }; + + var hashCode = function() + { + var __h = 5381; + var e; + for(var key in this) + { + e = this[key]; + if(e === undefined || e === null || typeof e == "function") + { + continue; + } + __h = memberHashCode(__h, e); + } + return __h; + }; + + Slice.defineStruct = function(constructor, legalKeyType, writeImpl, readImpl, minWireSize, fixed) + { + var obj = constructor; + + obj.prototype.clone = clone; + + obj.prototype.equals = equals; + + // + // Only generate hashCode if this structure type is a legal dictionary key type. + // + if(legalKeyType) + { + obj.prototype.hashCode = hashCode; + } + + if(readImpl && writeImpl) + { + obj.prototype.__write = writeImpl; + obj.prototype.__read = readImpl; + obj.write = function(os, v) + { + v.__write(os); + }; + obj.read = function(is) + { + var v = new this(); + v.__read(is); + return v; + }; + Object.defineProperty(obj, "minWireSize", { + get: function() { return minWireSize; } + }); + if(fixed) + { + Ice.StreamHelpers.VSizeOptHelper.call(obj); + } + else + { + Ice.StreamHelpers.FSizeOptHelper.call(obj); + } + } + return obj; + }; + + global.Slice = Slice; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TcpEndpointFactory.js b/js/src/Ice/TcpEndpointFactory.js new file mode 100644 index 00000000000..d459685efb5 --- /dev/null +++ b/js/src/Ice/TcpEndpointFactory.js @@ -0,0 +1,49 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/TcpEndpointI"); + require("Ice/Endpoint"); + + var Ice = global.Ice || {}; + + var TcpEndpointI = Ice.TcpEndpointI; + var TCPEndpointType = Ice.TCPEndpointType; + + var TcpEndpointFactory = Ice.Class({ + __init__: function(instance) + { + this._instance = instance; + }, + type: function() + { + return TCPEndpointType; + }, + protocol: function() + { + return "tcp"; + }, + create: function(str, oaEndpoint) + { + return TcpEndpointI.fromString(this._instance, str, oaEndpoint); + }, + read: function(s) + { + return TcpEndpointI.fromStream(s); + }, + destroy: function() + { + this._instance = null; + } + }); + + Ice.TcpEndpointFactory = TcpEndpointFactory; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TcpEndpointI.js b/js/src/Ice/TcpEndpointI.js new file mode 100644 index 00000000000..97851850877 --- /dev/null +++ b/js/src/Ice/TcpEndpointI.js @@ -0,0 +1,496 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Address"); + require("Ice/HashUtil"); + require("Ice/StringUtil"); + require("Ice/TcpTransceiver"); + require("Ice/Endpoint"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var Address = Ice.Address; + var HashUtil = Ice.HashUtil; + var StringUtil = Ice.StringUtil; + var TcpTransceiver = Ice.TcpTransceiver; + + var Class = Ice.Class; + var TcpEndpointI = Class(Ice.Endpoint, { + __init__: function(instance, ho, po, ti, conId, co) + { + this._instance = instance; + this._host = ho; + this._port = po; + this._timeout = ti; + this._connectionId = conId; + this._compress = co; + this.calcHashValue(); + }, + // + // Convert the endpoint to its string form + // + toString: function() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + var s = "tcp"; + + if(this._host !== null && this._host.length > 0) + { + s += " -h "; + var addQuote = this._host.indexOf(':') != -1; + if(addQuote) + { + s += "\""; + } + s += this._host; + if(addQuote) + { + s += "\""; + } + } + + s += " -p " + this._port; + + if(this._timeout != -1) + { + s += " -t " + this._timeout; + } + if(this._compress) + { + s += " -z"; + } + return s; + }, + // + // Return the endpoint information. + // + getInfo: function() + { + return new TCPEndpointInfoI(this._timeout, this._compress, this._host, this._port); + }, + // + // Marshal the endpoint + // + streamWrite: function(s) + { + s.writeShort(Ice.TCPEndpointType); + s.startWriteEncaps(); + s.writeString(this._host); + s.writeInt(this._port); + s.writeInt(this._timeout); + s.writeBool(this._compress); + s.endWriteEncaps(); + }, + // + // Return the endpoint type + // + type: function() + { + return Ice.TCPEndpointType; + }, + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + timeout: function() + { + return this._timeout; + }, + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + changeTimeout: function(timeout) + { + if(timeout === this._timeout) + { + return this; + } + else + { + return new TcpEndpointI(this._instance, this._host, this._port, timeout, this._connectionId, + this._compress); + } + }, + // + // Return a new endpoint with a different connection id. + // + changeConnectionId: function(connectionId) + { + if(connectionId === this._connectionId) + { + return this; + } + else + { + return new TcpEndpointI(this._instance, this._host, this._port, this._timeout, connectionId, + this._compress); + } + }, + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + compress: function() + { + return this._compress; + }, + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + changeCompress: function(compress) + { + if(compress === this._compress) + { + return this; + } + else + { + return new TcpEndpointI(this._instance, this._host, this._port, this._timeout, this._connectionId, + compress); + } + }, + // + // Return true if the endpoint is datagram-based. + // + datagram: function() + { + return false; + }, + // + // Return true if the endpoint is secure. + // + secure: function() + { + return false; + }, + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. In case a + // transceiver is created, this operation also returns a new + // "effective" endpoint, which might differ from this endpoint, + // for example, if a dynamic port number is assigned. + // + transceiver: function(endpoint) + { + endpoint.value = this; + return null; + }, + connect: function() + { + if(this._instance.traceLevels().network >= 2) + { + var msg = "trying to establish tcp connection to " + this._host + ":" + this._port; + this._instance.initializationData().logger.trace(this._instance.traceLevels().networkCat, msg); + } + + return TcpTransceiver.createOutgoing(this._instance, new Address(this._host, this._port)); + }, + hashCode: function() + { + return this._hashCode; + }, + // + // Compare endpoints for sorting purposes + // + equals: function(p) + { + if(!(p instanceof TcpEndpointI)) + { + return false; + } + + if(this === p) + { + return true; + } + + if(this._host !== p._host) + { + return false; + } + + if(this._port !== p._port) + { + return false; + } + + if(this._timeout !== p._timeout) + { + return false; + } + + if(this._connectionId !== p._connectionId) + { + return false; + } + + if(this._compress !== p._compress) + { + return false; + } + + return true; + }, + compareTo: function(p) + { + if(this === p) + { + return 0; + } + + if(p === null) + { + return 1; + } + + if(!(p instanceof TcpEndpointI)) + { + return this.type() < p.type() ? -1 : 1; + } + + if(this._port < p._port) + { + return -1; + } + else if(p._port < this._port) + { + return 1; + } + + if(this._timeout < p._timeout) + { + return -1; + } + else if(p._timeout < this._timeout) + { + return 1; + } + + if(this._connectionId != p._connectionId) + { + return this._connectionId < p._connectionId ? -1 : 1; + } + + if(!this._compress && p._compress) + { + return -1; + } + else if(!p._compress && this._compress) + { + return 1; + } + + if(this._host == p._host) + { + return 0; + } + else + { + return this._host < p._host ? -1 : 1; + } + }, + calcHashValue: function() + { + var h = 5381; + h = HashUtil.addNumber(h, Ice.TCPEndpointType); + h = HashUtil.addString(h, this._host); + h = HashUtil.addNumber(h, this._port); + h = HashUtil.addNumber(h, this._timeout); + h = HashUtil.addString(h, this._connectionId); + h = HashUtil.addBoolean(h, this._compress); + this._hashCode = h; + } + }); + + TcpEndpointI.fromString = function(instance, str, oaEndpoint) + { + var host = null; + var port = 0; + var timeout = -1; + var compress = false; + + var arr = str.split(/[ \t\n\r]+/); + + var i = 0; + while(i < arr.length) + { + if(arr[i].length === 0) + { + i++; + continue; + } + + var option = arr[i++]; + if(option.length != 2 && option.charAt(0) != '-') + { + throw new Ice.EndpointParseException("expected an endpoint option but found `" + option + + "' in endpoint `tcp " + str + "'"); + } + + var argument = null; + if(i < arr.length && arr[i].charAt(0) != '-') + { + argument = arr[i++]; + if(argument.charAt(0) == '\"' && argument.charAt(argument.length - 1) == '\"') + { + argument = argument.substring(1, argument.length - 1); + } + } + + switch(option.charAt(1)) + { + case 'h': + { + if(argument === null) + { + throw new Ice.EndpointParseException( + "no argument provided for -h option in endpoint `tcp " + str + "'"); + } + + host = argument; + break; + } + + case 'p': + { + if(argument === null) + { + throw new Ice.EndpointParseException( + "no argument provided for -p option in endpoint `tcp " + str + "'"); + } + + try + { + port = StringUtil.toInt(argument); + } + catch(ex) + { + throw new Ice.EndpointParseException("invalid port value `" + argument + + "' in endpoint `tcp " + str + "'"); + } + + if(port < 0 || port > 65535) + { + throw new Ice.EndpointParseException("port value `" + argument + + "' out of range in endpoint `tcp " + str + "'"); + } + + break; + } + + case 't': + { + if(argument === null) + { + throw new Ice.EndpointParseException( + "no argument provided for -t option in endpoint `tcp " + str + "'"); + } + + try + { + timeout = StringUtil.toInt(argument); + } + catch(ex) + { + throw new Ice.EndpointParseException( + "invalid timeout value `" + argument + "' in endpoint `tcp " + str + "'"); + } + + break; + } + + case 'z': + { + if(argument !== null) + { + throw new Ice.EndpointParseException("unexpected argument `" + argument + + "' provided for -z option in `tcp " + str + "'"); + } + + compress = true; + break; + } + + default: + { + throw new Ice.EndpointParseException("unknown option `" + option + "' in `tcp " + str + "'"); + } + } + } + + if(host === null) + { + host = instance.defaultsAndOverrides().defaultHost; + } + else if(host == "*") + { + if(oaEndpoint) + { + host = null; + } + else + { + throw new Ice.EndpointParseException("`-h *' not valid for proxy endpoint `tcp " + str + "'"); + } + } + + if(host === null) + { + host = ""; + } + + return new TcpEndpointI(instance, host, port, timeout, "", compress); + }; + + TcpEndpointI.fromStream = function(s) + { + s.startReadEncaps(); + var host = s.readString(); + var port = s.readInt(); + var timeout = s.readInt(); + var compress = s.readBool(); + s.endReadEncaps(); + return new TcpEndpointI(s.instance, host, port, timeout, "", compress); + }; + + Ice.TcpEndpointI = TcpEndpointI; + global.Ice = Ice; + + var TCPEndpointInfoI = Class(Ice.TCPEndpointInfo, { + __init__: function(timeout, compress, host, port) + { + Ice.TCPEndpointInfo.call(this, timeout, compress, host, port); + }, + type: function() + { + return Ice.TCPEndpointType; + }, + datagram: function() + { + return false; + }, + secure: function() + { + return false; + } + }); +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TcpTransceiver.js b/js/src/Ice/TcpTransceiver.js new file mode 100644 index 00000000000..a6a4615297c --- /dev/null +++ b/js/src/Ice/TcpTransceiver.js @@ -0,0 +1,458 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var net = require("net"); + + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/SocketOperation"); + require("Ice/Connection"); + require("Ice/Exception"); + require("Ice/LocalException"); + + var Ice = global.Ice || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var Network = Ice.Network; + var SocketOperation = Ice.SocketOperation; + var LocalException = Ice.LocalException; + var SocketException = Ice.SocketException; + + var StateNeedConnect = 0; + var StateConnectPending = 1; + var StateProxyConnectRequest = 2; + var StateProxyConnectRequestPending = 3; + var StateConnected = 4; + var StateClosed = 5; + + var TcpTransceiver = Ice.Class({ + __init__: function(instance) + { + this._traceLevels = instance.traceLevels(); + this._logger = instance.initializationData().logger; + this._readBuffers = []; + this._readPosition = 0; + }, + setCallbacks: function(connectedCallback, bytesAvailableCallback, bytesWrittenCallback) + { + this._connectedCallback = connectedCallback; + this._bytesAvailableCallback = bytesAvailableCallback; + this._bytesWrittenCallback = bytesWrittenCallback; + + var self = this; + this._fd.on("connect", function() { self.socketConnected(); }); + this._fd.on("close", function(err) { self.socketClosed(err); }); + this._fd.on("error", function(err) { self.socketError(err); }); + this._fd.on("data", function(buf) { self.socketBytesAvailable(buf); }); + }, + // + // Returns SocketOperation.None when initialization is complete. + // + initialize: function(readBuffer, writeBuffer) + { + try + { + if(this._exception) + { + throw this._exception; + } + + if(this._state === StateNeedConnect) + { + this._state = StateConnectPending; + this._fd.connect(this._addr.port, this._addr.host); + return SocketOperation.Connect; // Waiting for connect to complete. + } + else if(this._state === StateConnectPending) + { + // + // Socket is connected. + // + this._desc = fdToString(this._fd, this._proxy, this._addr); + this._state = StateConnected; + } + else if(this._state === StateProxyConnectRequest) + { + // + // Write completed. + // + this._proxy.endWriteConnectRequest(writeBuffer); + this._state = StateProxyConnectRequestPending; // Wait for proxy response + return SocketOperation.Read; + } + else if(this._state === StateProxyConnectRequestPending) + { + // + // Read completed. + // + this._proxy.endReadConnectRequestResponse(readBuffer); + this._state = StateConnected; + } + } + catch(err) + { + if(!this._exception) + { + this._exception = translateError(this._state, err); + } + + if(this._traceLevels.network >= 2) + { + var s = []; + s.push("failed to establish tcp connection\n"); + s.push(fdToString(this._fd, this._proxy, this._addr.host, this._addr.port)); + this._logger.trace(this._traceLevels.networkCat, s.join("")); + } + + throw this._exception; + } + + Debug.assert(this._state === StateConnected); + if(this._traceLevels.network >= 1) + { + var s = "tcp connection established\n" + this._desc; + this._logger.trace(this._traceLevels.networkCat, s); + } + + return SocketOperation.None; + }, + register: function() + { + this._registered = true; + this._fd.resume(); + if(this._exception) + { + this._bytesAvailableCallback(); + } + }, + unregister: function() + { + this._registered = false; + this._fd.pause(); + }, + close: function() + { + if(this._state > StateConnectPending && this._traceLevels.network >= 1) + { + this._logger.trace(this._traceLevels.networkCat, "closing " + this.type() + " connection\n" + + this._desc); + } + + Debug.assert(this._fd !== null); + try + { + this._fd.destroy(); + } + catch(ex) + { + throw translateError(this._state, ex); + } + finally + { + this._fd = null; + } + }, + // + // Returns true if all of the data was flushed to the kernel buffer. + // + write: function(byteBuffer) + { + if(this._exception) + { + throw this._exception; + } + + var remaining = byteBuffer.remaining; + Debug.assert(remaining > 0); + + // + // Create a slice of the source buffer representing the remaining data to be written. + // + var slice = byteBuffer.b.slice(byteBuffer.position, byteBuffer.position + remaining); + + // + // The socket will accept all of the data. + // + byteBuffer.position = byteBuffer.position + remaining; + + var self = this; + + var sync = true; + sync = this._fd.write(slice, null, function() { + if(self._traceLevels.network >= 3) + { + var msg = "sent " + remaining + " bytes via " + self.type() + "\n" + self._desc; + self._logger.trace(self._traceLevels.networkCat, msg); + } + if(!sync) + { + self._bytesWrittenCallback(); + } + }); + return sync; + }, + read: function(byteBuffer, moreData) + { + if(this._exception) + { + throw this._exception; + } + + moreData.value = false; + + if(this._readBuffers.length === 0) + { + return false; // No data available. + } + + var avail = this._readBuffers[0].length - this._readPosition; + Debug.assert(avail > 0); + var remaining = byteBuffer.remaining; + + while(byteBuffer.remaining > 0) + { + if(avail > byteBuffer.remaining) + { + avail = byteBuffer.remaining; + } + + this._readBuffers[0].copy(byteBuffer.b, byteBuffer.position, this._readPosition, + this._readPosition + avail); + + byteBuffer.position += avail; + this._readPosition += avail; + if(this._readPosition === this._readBuffers[0].length) + { + // + // We've exhausted the current read buffer. + // + this._readPosition = 0; + this._readBuffers.shift(); + if(this._readBuffers.length === 0) + { + break; // No more data - we're done. + } + else + { + avail = this._readBuffers[0].length; + } + } + } + + var n = remaining - byteBuffer.remaining; + if(n > 0 && this._traceLevels.network >= 3) + { + var msg = "received " + n + " of " + remaining + " bytes via " + this.type() + "\n" + this._desc; + this._logger.trace(this._traceLevels.networkCat, msg); + } + + moreData.value = this._readBuffers.length > 0; + + return byteBuffer.remaining === 0; + }, + type: function() + { + return "tcp"; + }, + getInfo: function() + { + Debug.assert(this._fd !== null); + var info = this.createInfo(); + info.localAddress = this._fd.localAddress; + info.localPort = this._fd.localPort; + info.remoteAddress = this._fd.remoteAddress; + info.remotePort = this._fd.remotePort; + return info; + }, + createInfo: function() + { + return new Ice.TCPConnectionInfo(); + }, + checkSendSize: function(stream, messageSizeMax) + { + if(stream.size > messageSizeMax) + { + ExUtil.throwMemoryLimitException(stream.size, messageSizeMax); + } + }, + toString: function() + { + return this._desc; + }, + socketConnected: function() + { + Debug.assert(this._connectedCallback !== null); + this._connectedCallback(); + }, + socketBytesAvailable: function(buf) + { + Debug.assert(this._bytesAvailableCallback !== null); + + // + // TODO: Should we set a limit on how much data we can read? + // We can call _fd.pause() to temporarily stop reading. + // + if(buf.length > 0) + { + this._readBuffers.push(buf); + this._bytesAvailableCallback(); + } + }, + socketClosed: function(err) + { + // + // Don't call the closed callback if an error occurred; the error callback + // will be called. + // + if(!err) + { + this.socketError(null); + } + }, + socketError: function(err) + { + this._exception = translateError(this._state, err) + if(this._state < StateConnected) + { + this._connectedCallback(); + } + else if(this._registered) + { + this._bytesAvailableCallback(); + } + } + }); + + function fdToString(fd, targetAddr) + { + if(fd === null) + { + return "<closed>"; + } + + return addressesToString(fd.localAddress, fd.localPort, fd.remoteAddress, fd.remotePort, targetAddr); + } + + function translateError(state, err) + { + if(!err) + { + return new Ice.ConnectionLostException(); + } + else if(state < StateConnected) + { + if(connectionRefused(err.code)) + { + return new Ice.ConnectionRefusedException(err.code, err); + } + else if(connectionFailed(err.code)) + { + return new Ice.ConnectFailedException(err.code, err); + } + } + else if(connectionLost(err.code)) + { + return new Ice.ConnectionLostException(err.code, err); + } + return new Ice.SocketException(err.code, err); + } + + function addressesToString(localHost, localPort, remoteHost, remotePort, targetAddr) + { + remoteHost = remoteHost === undefined ? null : remoteHost; + targetAddr = targetAddr === undefined ? null : targetAddr; + + var s = []; + s.push("local address = "); + s.push(localHost + ":" + localPort); + + if(remoteHost === null && targetAddr !== null) + { + remoteHost = targetAddr.host; + remotePort = targetAddr.port; + } + + if(remoteHost === null) + { + s.push("\nremote address = <not connected>"); + } + else + { + s.push("\nremote address = "); + s.push(remoteHost + ":" + remotePort); + } + + return s.join(""); + }; + + TcpTransceiver.createOutgoing = function(instance, addr) + { + var transceiver = new TcpTransceiver(instance); + + transceiver._fd = new net.Socket(); + transceiver._addr = addr; + transceiver._desc = "remote address: " + addr.host + ":" + addr.port + " <not connected>"; + transceiver._state = StateNeedConnect; + transceiver._registered = false; + transceiver._exception = null; + + return transceiver; + }; + + TcpTransceiver.createIncoming = function(instance, fd) + { + var transceiver = new TcpTransceiver(instance); + + transceiver._fd = fd; + transceiver._addr = null; + transceiver._desc = fdToString(fd); + transceiver._state = StateConnected; + transceiver._registered = false; + transceiver._exception = null; + + return transceiver; + }; + + + var ECONNABORTED = "ECONNABORTED"; + var ECONNREFUSED = "ECONNREFUSED"; + var ECONNRESET = "ECONNRESET" + var EHOSTUNREACH = "EHOSTUNREACH"; + var ENETUNREACH = "ENETUNREACH"; + var ENOTCONN = "ENOTCONN"; + var EPIPE = "EPIPE"; + var ESHUTDOWN = "ESHUTDOWN" + var ETIMEDOUT = "ETIMEDOUT"; + + function connectionRefused(err) + { + return err == ECONNREFUSED; + } + + function connectionFailed(err) + { + return err == ECONNREFUSED || err == ETIMEDOUT || + err == ENETUNREACH || err == EHOSTUNREACH || + err == ECONNRESET || err == ESHUTDOWN || + err == ECONNABORTED; + } + + function connectionLost(err) + { + return err == ECONNRESET || err == ENOTCONN || + err == ESHUTDOWN || err == ECONNABORTED || + err == EPIPE; + } + + Ice.TcpTransceiver = TcpTransceiver; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TimeUtil.js b/js/src/Ice/TimeUtil.js new file mode 100644 index 00000000000..ad152cd7f09 --- /dev/null +++ b/js/src/Ice/TimeUtil.js @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + var TimeUtil = {}; + TimeUtil.now = function() + { + return new Date().getTime(); + }; + Ice.TimeUtil = TimeUtil; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/Timer.js b/js/src/Ice/Timer.js new file mode 100644 index 00000000000..0c489726d98 --- /dev/null +++ b/js/src/Ice/Timer.js @@ -0,0 +1,123 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/HashMap"); + require("Ice/LocalException"); + require("Ice/Class"); + + var Ice = global.Ice || {}; + + var HashMap = Ice.HashMap; + var CommunicatorDestroyedException = Ice.CommunicatorDestroyedException; + + var Timer = Ice.Class({ + __init__: function(instance) + { + this._instance = instance; + this._destroyed = false; + this._tokenId = 0; + this._tokens = new HashMap(); + }, + destroy: function() + { + var self = this; + this._tokens.forEach(function(key, value){ + self.cancel(key); + }); + this._destroyed = true; + this._tokens.clear(); + }, + schedule: function(callback, delay) + { + if(this._destroyed) + { + throw new CommunicatorDestroyedException(); + } + + var token = this._tokenId++; + var self = this; + + var id = setTimeout(function() { self.handleTimeout(token); }, delay); + this._tokens.set(token, { callback: callback, id: id, isInterval: false }); + + return token; + }, + scheduleRepeated: function(callback, period) + { + if(this._destroyed) + { + throw new CommunicatorDestroyedException(); + } + + var token = this._tokenId++; + var self = this; + + var id = setInterval(function() { self.handleInterval(token); }, period); + this._tokens.set(token, { callback: callback, id: id, isInterval: true }); + + return token; + }, + cancel: function(id) + { + if(this._destroyed) + { + return false; + } + + var token = this._tokens.get(id); + if(token === undefined) + { + return false; + } + + this._tokens.delete(id); + if(token.isInterval) + { + clearInterval(token.id); + } + else + { + clearTimeout(token.id); + } + + return true; + }, + handleTimeout: function(id) + { + if(this._destroyed) + { + return; + } + + var token = this._tokens.get(id); + if(token !== undefined) + { + this._tokens.delete(id); + token.callback(); + } + }, + handleInterval: function(id) + { + if(this._destroyed) + { + return; + } + + var token = this._tokens.get(id); + if(token !== undefined) + { + token.callback(); + } + } + }); + + Ice.Timer = Timer; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TraceLevels.js b/js/src/Ice/TraceLevels.js new file mode 100644 index 00000000000..512813d8b18 --- /dev/null +++ b/js/src/Ice/TraceLevels.js @@ -0,0 +1,66 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var TraceLevels = function(properties) + { + var networkCat = "Network"; + var protocolCat = "Protocol"; + var retryCat = "Retry"; + var locationCat = "Locator"; + var slicingCat = "Slicing"; + + var keyBase = "Ice.Trace."; + + var network = properties.getPropertyAsInt(keyBase + networkCat); + var protocol = properties.getPropertyAsInt(keyBase + protocolCat); + var retry = properties.getPropertyAsInt(keyBase + retryCat); + var location = properties.getPropertyAsInt(keyBase + locationCat); + var slicing = properties.getPropertyAsInt(keyBase + slicingCat); + properties.getPropertyAsInt(keyBase + "ThreadPool"); // Avoid an "unused property" warning. + + return Object.create(null, { + 'network': { + get: function() { return network; } + }, + 'networkCat': { + get: function() { return networkCat; } + }, + 'protocol': { + get: function() { return protocol; } + }, + 'protocolCat': { + get: function() { return protocolCat; } + }, + 'retry': { + get: function() { return retry; } + }, + 'retryCat': { + get: function() { return retryCat; } + }, + 'location': { + get: function() { return location; } + }, + 'locationCat': { + get: function() { return locationCat; } + }, + 'slicing': { + get: function() { return slicing; } + }, + 'slicingCat': { + get: function() { return slicingCat; } + } + }); + }; + + Ice.TraceLevels = TraceLevels; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/TraceUtil.js b/js/src/Ice/TraceUtil.js new file mode 100644 index 00000000000..2e119ca6dcf --- /dev/null +++ b/js/src/Ice/TraceUtil.js @@ -0,0 +1,474 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Debug"); + require("Ice/HashMap"); + require("Ice/Protocol"); + require("Ice/StringUtil"); + require("Ice/Current"); + require("Ice/Identity"); + + var Ice = global.Ice || {}; + + // + // Local aliases. + // + var Debug = Ice.Debug; + var HashMap = Ice.HashMap; + var Protocol = Ice.Protocol; + var StringUtil = Ice.StringUtil; + var OperationMode = Ice.OperationMode; + var Identity = Ice.Identity; + + var TraceUtil = {}; + + TraceUtil.traceSend = function(stream, logger, traceLevels) + { + if(traceLevels.protocol >= 1) + { + var p = stream.pos; + stream.pos = 0; + + var s = []; + var type = printMessage(s, stream); + + logger.trace(traceLevels.protocolCat, "sending " + getMessageTypeAsString(type) + " " + s.join("")); + + stream.pos = p; + } + }; + + TraceUtil.traceRecv = function(stream, logger, traceLevels) + { + if(traceLevels.protocol >= 1) + { + var p = stream.pos; + stream.pos = 0; + + var s = []; + var type = printMessage(s, stream); + + logger.trace(traceLevels.protocolCat, "received " + getMessageTypeAsString(type) + " " + s.join("")); + + stream.pos = p; + } + }; + + TraceUtil.trace = function(heading, stream, logger, traceLevels) + { + if(traceLevels.protocol >= 1) + { + var p = stream.pos; + stream.pos = 0; + + var s = []; + s.push(heading); + printMessage(s, stream); + + logger.trace(traceLevels.protocolCat, s.join("")); + stream.pos = p; + } + }; + + var slicingIds = new HashMap(); + + function traceSlicing(kind, typeId, slicingCat, logger) + { + if(!slicingIds.has(typeId)) + { + var s = "unknown " + kind + " type `" + typeId + "'"; + logger.trace(slicingCat, s); + slicingIds.set(typeId, 1); + } + } + + TraceUtil.dumpStream = function(stream) + { + var pos = stream.pos; + stream.pos = 0; + + var data = stream.readBlob(stream.size()); + TraceUtil.dumpOctets(data); + + stream.pos = pos; + }; + + TraceUtil.dumpOctets = function(data) + { + var inc = 8; + var buf = []; + + for(var i = 0; i < data.length; i += inc) + { + var j; + for(j = i; j - i < inc; j++) + { + if(j < data.length) + { + var n = data[j]; + if(n < 0) + { + n += 256; + } + var s; + if(n < 10) + { + s = " " + n; + } + else if(n < 100) + { + s = " " + n; + } + else + { + s = "" + n; + } + buf.push(s + " "); + } + else + { + buf.push(" "); + } + } + + buf.push('"'); + + for(j = i; j < data.length && j - i < inc; j++) + { + if(data[j] >= 32 && data[j] < 127) + { + buf.push(String.fromCharCode(data[j])); + } + else + { + buf.push('.'); + } + } + + buf.push("\"\n"); + } + + console.log(buf.join("")); + }; + + Ice.TraceUtil = TraceUtil; + global.Ice = Ice; + + function printIdentityFacetOperation(s, stream) + { + var identity = new Identity(); + identity.__read(stream); + s.push("\nidentity = " + stream.instance.identityToString(identity)); + + var facet = Ice.StringSeqHelper.read(stream); + s.push("\nfacet = "); + if(facet.length > 0) + { + s.push(StringUtil.escapeString(facet[0], "")); + } + + var operation = stream.readString(); + s.push("\noperation = " + operation); + } + + function printRequest(s, stream) + { + var requestId = stream.readInt(); + s.push("\nrequest id = " + requestId); + if(requestId === 0) + { + s.push(" (oneway)"); + } + + printRequestHeader(s, stream); + } + + function printBatchRequest(s, stream) + { + var batchRequestNum = stream.readInt(); + s.push("\nnumber of requests = " + batchRequestNum); + + for(var i = 0; i < batchRequestNum; ++i) + { + s.push("\nrequest #" + i + ':'); + printRequestHeader(s, stream); + } + } + + function printReply(s, stream) + { + var requestId = stream.readInt(); + s.push("\nrequest id = " + requestId); + + var replyStatus = stream.readByte(); + s.push("\nreply status = " + replyStatus + ' '); + + switch(replyStatus) + { + case Protocol.replyOK: + { + s.push("(ok)"); + break; + } + + case Protocol.replyUserException: + { + s.push("(user exception)"); + break; + } + + case Protocol.replyObjectNotExist: + case Protocol.replyFacetNotExist: + case Protocol.replyOperationNotExist: + { + switch(replyStatus) + { + case Protocol.replyObjectNotExist: + { + s.push("(object not exist)"); + break; + } + + case Protocol.replyFacetNotExist: + { + s.push("(facet not exist)"); + break; + } + + case Protocol.replyOperationNotExist: + { + s.push("(operation not exist)"); + break; + } + + default: + { + Debug.assert(false); + break; + } + } + + printIdentityFacetOperation(s, stream); + break; + } + + case Protocol.replyUnknownException: + case Protocol.replyUnknownLocalException: + case Protocol.replyUnknownUserException: + { + switch(replyStatus) + { + case Protocol.replyUnknownException: + { + s.push("(unknown exception)"); + break; + } + + case Protocol.replyUnknownLocalException: + { + s.push("(unknown local exception)"); + break; + } + + case Protocol.replyUnknownUserException: + { + s.push("(unknown user exception)"); + break; + } + + default: + { + Debug.assert(false); + break; + } + } + + var unknown = stream.readString(); + s.push("\nunknown = " + unknown); + break; + } + + default: + { + s.push("(unknown)"); + break; + } + } + } + + function printRequestHeader(s, stream) + { + printIdentityFacetOperation(s, stream); + + var mode = stream.readByte(); + s.push("\nmode = " + mode + ' '); + switch(OperationMode.valueOf(mode)) + { + case OperationMode.Normal: + { + s.push("(normal)"); + break; + } + + case OperationMode.Nonmutating: + { + s.push("(nonmutating)"); + break; + } + + case OperationMode.Idempotent: + { + s.push("(idempotent)"); + break; + } + + default: + { + s.push("(unknown)"); + break; + } + } + + var sz = stream.readSize(); + s.push("\ncontext = "); + while(sz-- > 0) + { + var key = stream.readString(); + var value = stream.readString(); + s.push(key + '/'+ value); + if(sz > 0) + { + s.push(", "); + } + } + + var ver = stream.skipEncaps(); + if(!ver.equals(Protocol.Encoding_1_0)) + { + s.push("\nencoding = "); + s.push(Ice.encodingVersionToString(ver)); + } + } + + function printHeader(s, stream) + { + stream.readByte(); // Don't bother printing the magic number + stream.readByte(); + stream.readByte(); + stream.readByte(); + + // var pMajor = stream.readByte(); + // var pMinor = stream.readByte(); + // s.push("\nprotocol version = " + pMajor + "." + pMinor); + stream.readByte(); // major + stream.readByte(); // minor + + // var eMajor = stream.readByte(); + // var eMinor = stream.readByte(); + // s.push("\nencoding version = " + eMajor + "." + eMinor); + stream.readByte(); // major + stream.readByte(); // minor + + var type = stream.readByte(); + + s.push("\nmessage type = " + type + " (" + getMessageTypeAsString(type) + ')'); + var compress = stream.readByte(); + s.push("\ncompression status = " + compress + ' '); + switch(compress) + { + case 0: + { + s.push("(not compressed; do not compress response, if any)"); + break; + } + + case 1: + { + s.push("(not compressed; compress response, if any)"); + break; + } + + case 2: + { + s.push("(compressed; compress response, if any)"); + break; + } + + default: + { + s.push("(unknown)"); + break; + } + } + + var size = stream.readInt(); + s.push("\nmessage size = " + size); + return type; + } + + function printMessage(s, stream) + { + var type = printHeader(s, stream); + + switch(type) + { + case Protocol.closeConnectionMsg: + case Protocol.validateConnectionMsg: + { + // We're done. + break; + } + + case Protocol.requestMsg: + { + printRequest(s, stream); + break; + } + + case Protocol.requestBatchMsg: + { + printBatchRequest(s, stream); + break; + } + + case Protocol.replyMsg: + { + printReply(s, stream); + break; + } + + default: + { + break; + } + } + + return type; + } + + function getMessageTypeAsString(type) + { + switch(type) + { + case Protocol.requestMsg: + return "request"; + case Protocol.requestBatchMsg: + return "batch request"; + case Protocol.replyMsg: + return "reply"; + case Protocol.closeConnectionMsg: + return "close connection"; + case Protocol.validateConnectionMsg: + return "validate connection"; + default: + return "unknown"; + } + } +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/UUID.js b/js/src/Ice/UUID.js new file mode 100644 index 00000000000..11928fcb1da --- /dev/null +++ b/js/src/Ice/UUID.js @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + var Ice = global.Ice || {}; + + var UUID = {}; + + UUID.generateUUID = function() + { + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16); + }); + return uuid; + }; + + Ice.UUID = UUID; + Ice.generateUUID = UUID.generateUUID; + + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/UnknownSlicedObject.js b/js/src/Ice/UnknownSlicedObject.js new file mode 100644 index 00000000000..70ef7146f53 --- /dev/null +++ b/js/src/Ice/UnknownSlicedObject.js @@ -0,0 +1,80 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Object"); + require("Ice/Class"); + + var Ice = global.Ice || {}; + + var SliceInfo = function() + { + /** + * The Slice type ID for this slice. + **/ + this.typeId = ""; + + /** + * The Slice compact type ID for this slice. + **/ + this.compactId = -1; + + /** + * The encoded bytes for this slice, including the leading size integer. + **/ + this.bytes = []; + + /** + * The Ice objects referenced by this slice. + **/ + this.objects = []; + + /** + * Whether or not the slice contains optional members. + **/ + this.hasOptionalMembers = false; + + /** + * Whether or not this is the last slice. + **/ + this.isLastSlice = false; + }; + + var SlicedData = function(slices) + { + this.slices = slices; + }; + + var UnknownSlicedObject = Ice.Class(Ice.Object, + { + __init__: function(unknownTypeId) + { + this._unknownTypeId = unknownTypeId; + }, + getUnknownTypeId: function() + { + return this._unknownTypeId; + }, + __write: function(os) + { + os.startWriteObject(this._slicedData); + os.endWriteObject(); + }, + __read: function(is) + { + is.startReadObject(); + this._slicedData = is.endReadObject(true); + } + }); + + Ice.SliceInfo = SliceInfo; + Ice.SlicedData = SlicedData; + Ice.UnknownSlicedObject = UnknownSlicedObject; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/browser/.gitignore b/js/src/Ice/browser/.gitignore new file mode 100644 index 00000000000..9c5c0a3f6d2 --- /dev/null +++ b/js/src/Ice/browser/.gitignore @@ -0,0 +1,2 @@ +ConnectionInfo.js +EndpointInfo.js diff --git a/js/src/Ice/browser/Buffer.js b/js/src/Ice/browser/Buffer.js new file mode 100644 index 00000000000..5d6c33393fe --- /dev/null +++ b/js/src/Ice/browser/Buffer.js @@ -0,0 +1,419 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + // + // IE 10 doesn't implement ArrayBuffer.slice + // + + if(!ArrayBuffer.prototype.slice) + { + ArrayBuffer.prototype.slice = function (start, end) + { + var b = new Uint8Array(this); + end = end === undefined ? b.length : end; + var result = new Uint8Array(new ArrayBuffer(end - start)); + for(var i = 0, length = result.length; i < length; i++) + { + result[i] = b[i + start]; + } + return result.buffer; + }; + } + + var __BufferOverflowException__ = "BufferOverflowException"; + var __BufferUnderflowException__ = "BufferUnderflowException"; + var __IndexOutOfBoundsException__ = "IndexOutOfBoundsException"; + + // + // Buffer implementation to be used by web browsers, it uses ArrayBuffer as + // the store. + // + + require("Ice/Class"); + require("Ice/Long"); + + var Ice = global.Ice || Ice; + var Long = Ice.Long; + + var Buffer = Ice.Class({ + __init__: function(buffer) + { + if(buffer !== undefined) + { + this.b = buffer; + this.v = new DataView(this.b); + } + else + { + this.b = null; // ArrayBuffer + this.v = null; // DataView + } + this._position = 0; + this._limit = 0; + this._shrinkCounter = 0; + }, + empty: function() + { + return this._limit === 0; + }, + resize: function(n) + { + if(n === 0) + { + this.clear(); + } + else if(n > this.capacity) + { + this.reserve(n); + } + this._limit = n; + }, + clear: function() + { + this.b = null; + this.v = null; + this._position = 0; + this._limit = 0; + }, + // + // Call expand(n) to add room for n additional bytes. Note that expand() + // examines the current position of the buffer first; we don't want to + // expand the buffer if the caller is writing to a location that is + // already in the buffer. + // + expand: function(n) + { + var sz = this.capacity === 0 ? n : this._position + n; + if(sz > this._limit) + { + this.resize(sz); + } + }, + reset: function() + { + if(this._limit > 0 && this._limit * 2 < this.capacity) + { + // + // If the current buffer size is smaller than the + // buffer capacity, we shrink the buffer memory to the + // current size. This is to avoid holding on to too much + // memory if it's not needed anymore. + // + if(++this._shrinkCounter > 2) + { + this.reserve(this._limit); + this._shrinkCounter = 0; + } + } + else + { + this._shrinkCounter = 0; + } + this._limit = 0; + this._position = 0; + }, + reserve: function(n) + { + if(n > this.capacity) + { + var capacity = Math.max(n, 2 * this.capacity); + capacity = Math.max(1024, capacity); + if(!this.b) + { + this.b = new ArrayBuffer(capacity); + } + else + { + var b = new Uint8Array(capacity); + b.set(new Uint8Array(this.b)); + this.b = b.buffer; + } + this.v = new DataView(this.b); + } + else if(n < this.capacity) + { + this.b = this.b.slice(0, this.capacity); + this.v = new DataView(this.b); + } + else + { + return; + } + }, + put: function(v) + { + if(this._position === this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setUint8(this._position, v); + this._position++; + }, + putAt: function(i, v) + { + if(i >= this._limit) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.v.setUint8(i, v); + }, + putArray: function(v) + { + if(v.byteLength > 0) + { + //Expects an Uint8Array + if(this._position + v.length > this._limit) + { + throw new Error(__BufferOverflowException__); + } + new Uint8Array(this.b, 0, this.b.byteLength).set(v, this._position); + this._position += v.byteLength; + } + }, + putShort: function(v) + { + if(this._position + 2 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setInt16(this._position, v, true); + this._position += 2; + }, + putInt: function(v) + { + if(this._position + 4 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setInt32(this._position, v, true); + this._position += 4; + }, + putIntAt: function(i, v) + { + if(i + 4 > this._limit || i < 0) + { + throw new Error(__IndexOutOfBoundsException__); + } + this.v.setInt32(i, v, true); + }, + putFloat: function(v) + { + if(this._position + 4 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setFloat32(this._position, v, true); + this._position += 4; + }, + putDouble: function(v) + { + if(this._position + 8 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setFloat64(this._position, v, true); + this._position += 8; + }, + putLong: function(v) + { + if(this._position + 8 > this._limit) + { + throw new Error(__BufferOverflowException__); + } + this.v.setInt32(this._position, v.low, true); + this._position += 4; + this.v.setInt32(this._position, v.high, true); + this._position += 4; + }, + writeString: function(stream, v) + { + // + // Encode the string as utf8 + // + var encoded = unescape(encodeURIComponent(v)); + + stream.writeSize(encoded.length); + stream.expand(encoded.length); + this.putString(encoded, encoded.length); + }, + putString: function(v, sz) + { + if(this._position + sz > this._limit) + { + throw new Error(__BufferOverflowException__); + } + for(var i = 0; i < sz; ++i) + { + this.v.setUint8(this._position, v.charCodeAt(i)); + this._position++; + } + }, + get: function() + { + if(this._position >= this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.v.getUint8(this._position); + this._position++; + return v; + }, + getAt: function(i) + { + if(i < 0 || i >= this._limit) + { + throw new Error(__IndexOutOfBoundsException__); + } + return this.v.getUint8(i); + }, + getArray: function(length) + { + if(this._position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + var buffer = this.b.slice(this._position, this._position + length); + this._position += length; + return new Uint8Array(buffer); + }, + getArrayAt: function(position, length) + { + if(position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + length = length === undefined ? (this.b.byteLength - position) : length; + return new Uint8Array(this.b.slice(position, position + length)); + }, + getShort: function() + { + if(this._limit - this._position < 2) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.v.getInt16(this._position, true); + this._position += 2; + return v; + }, + getInt: function() + { + if(this._limit - this._position < 4) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.v.getInt32(this._position, true); + this._position += 4; + return v; + }, + getFloat: function() + { + if(this._limit - this._position < 4) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.v.getFloat32(this._position, true); + this._position += 4; + return v; + }, + getDouble: function() + { + if(this._limit - this._position < 8) + { + throw new Error(__BufferUnderflowException__); + } + var v = this.v.getFloat64(this._position, true); + this._position += 8; + return v; + }, + getLong: function() + { + if(this._limit - this._position < 8) + { + throw new Error(__BufferUnderflowException__); + } + var v = new Long(); + v.low = this.v.getInt32(this._position, true); + this._position += 4; + v.high = this.v.getInt32(this._position, true); + this._position += 4; + return v; + }, + getString: function(length) + { + if(this._position + length > this._limit) + { + throw new Error(__BufferUnderflowException__); + } + + var data = new DataView(this.b, this._position, length); + var s = ""; + + for(var i = 0; i < length; ++i) + { + s += String.fromCharCode(data.getUint8(i)); + } + this._position += length; + s = decodeURIComponent(escape(s)); + return s; + } + }); + + var prototype = Buffer.prototype; + + Object.defineProperty(prototype, "position", { + get: function() { return this._position; }, + set: function(position){ + if(position >= 0 && position <= this._limit) + { + this._position = position; + } + } + }); + + Object.defineProperty(prototype, "limit", { + get: function() { return this._limit; }, + set: function(limit){ + if(limit <= this.capacity) + { + this._limit = limit; + if(this._position > limit) + { + this._position = limit; + } + } + } + }); + + Object.defineProperty(prototype, "capacity", { + get: function() { return this.b === null ? 0 : this.b.byteLength; } + }); + + Object.defineProperty(prototype, "remaining", { + get: function() { return this._limit - this._position; } + }); + + // + // Create a native buffer from an array of bytes. + // + Buffer.createNative = function(data) + { + if(data === undefined) + { + return new Uint8Array(0); + } + else + { + return new Uint8Array(data); + } + }; + + Ice.Buffer = Buffer; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/browser/Debug.js b/js/src/Ice/browser/Debug.js new file mode 100644 index 00000000000..c6739e40ebd --- /dev/null +++ b/js/src/Ice/browser/Debug.js @@ -0,0 +1,41 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Exception"); + + var Ice = global.Ice || {}; + + var Exception = Ice.Exception; + + var AssertionFailedException = Ice.Class(Error, { + __init__: function(message) + { + Error.call(this); + Exception.captureStackTrace(this); + this.message = message; + } + }); + var Debug = {}; + + Debug.AssertionFailedException = AssertionFailedException; + + Debug.assert = function(b, msg) + { + if(!b) + { + console.log(msg === undefined ? "assertion failed" : msg); + console.log(Error().stack); + throw new AssertionFailedException(msg === undefined ? "assertion failed" : msg); + } + }; + Ice.Debug = Debug; + global.Ice = Ice; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/browser/EndpointFactory.js b/js/src/Ice/browser/EndpointFactory.js new file mode 100644 index 00000000000..c8481c654b5 --- /dev/null +++ b/js/src/Ice/browser/EndpointFactory.js @@ -0,0 +1,51 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Endpoint"); + + require("Ice/browser/EndpointInfo"); + require("Ice/browser/EndpointI"); + + var Ice = global.Ice || {}; + var IceWS = global.IceWS || {}; + + var EndpointI = IceWS.EndpointI; + + var EndpointFactory = Ice.Class({ + __init__:function(instance, secure) + { + this._instance = instance; + this._secure = secure; + }, + type: function() + { + return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType; + }, + protocol: function() + { + return this._secure ? "wss" : "ws"; + }, + create: function(str, oaEndpoint) + { + return EndpointI.fromString(this._instance, this._secure, str, oaEndpoint); + }, + read: function(s) + { + return EndpointI.fromStream(s, this._secure); + }, + destroy: function() + { + this._instance = null; + } + }); + IceWS.EndpointFactory = EndpointFactory; + global.IceWS = IceWS; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/browser/EndpointI.js b/js/src/Ice/browser/EndpointI.js new file mode 100644 index 00000000000..f364a087922 --- /dev/null +++ b/js/src/Ice/browser/EndpointI.js @@ -0,0 +1,543 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Address"); + require("Ice/HashUtil"); + require("Ice/StringUtil"); + require("Ice/Endpoint"); + require("Ice/LocalException"); + + require("Ice/browser/Transceiver"); + require("Ice/browser/EndpointInfo"); + + var Ice = global.Ice || {}; + var IceWS = global.IceWS || {}; + + var Address = Ice.Address; + var HashUtil = Ice.HashUtil; + var StringUtil = Ice.StringUtil; + var Transceiver = IceWS.Transceiver; + + var Class = Ice.Class; + + var EndpointI = Class(Ice.Endpoint, { + __init__: function(instance, secure, ho, po, ti, conId, co, re) + { + this._instance = instance; + this._secure = secure; + this._host = ho; + this._port = po; + this._timeout = ti; + this._connectionId = conId; + this._compress = co; + this._resource = re; + this.calcHashValue(); + }, + // + // Convert the endpoint to its string form + // + toString: function() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + var s = (this._secure ? "wss" : "ws"); + + if(this._host !== null && this._host.length > 0) + { + s += " -h "; + s += (this._host.indexOf(':') !== -1) ? ("\"" + this._host + "\"") : this._host; + } + + s += " -p " + this._port; + + if(this._timeout != -1) + { + s += " -t " + this._timeout; + } + if(this._compress) + { + s += " -z"; + } + if(this._resource !== null && this._resource.length > 0) + { + s += " -r "; + s += (this._resource.indexOf(':') !== -1) ? ("\"" + this._resource + "\"") : this._resource; + } + return s; + }, + // + // Return the endpoint information. + // + getInfo: function() + { + return new EndpointInfoI(this._secure, this._timeout, this._compress, this._host, this._port, this._resource); + }, + // + // Marshal the endpoint + // + streamWrite: function(s) + { + s.writeShort(this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType); + s.startWriteEncaps(); + s.writeString(this._host); + s.writeInt(this._port); + s.writeInt(this._timeout); + s.writeBool(this._compress); + s.writeString(this._resource); + s.endWriteEncaps(); + }, + // + // Return the endpoint type + // + type: function() + { + return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType; + }, + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + timeout: function() + { + return this._timeout; + }, + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + changeTimeout: function(timeout) + { + if(timeout === this._timeout) + { + return this; + } + else + { + return new EndpointI(this._instance, this._secure, this._host, this._port, timeout, this._connectionId, this._compress, this._resource); + } + }, + // + // Return a new endpoint with a different connection id. + // + changeConnectionId: function(connectionId) + { + if(connectionId === this._connectionId) + { + return this; + } + else + { + return new EndpointI(this._instance, this._secure, this._host, this._port, this._timeout, connectionId, this._compress, this._resource); + } + }, + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + compress: function() + { + return this._compress; + }, + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + changeCompress: function(compress) + { + if(compress === this._compress) + { + return this; + } + else + { + return new EndpointI(this._instance, this._secure, this._host, this._port, this._timeout, this._connectionId, compress, this._resource); + } + }, + // + // Return true if the endpoint is datagram-based. + // + datagram: function() + { + return false; + }, + // + // Return true if the endpoint is secure. + // + secure: function() + { + return this._secure; + }, + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. In case a + // transceiver is created, this operation also returns a new + // "effective" endpoint, which might differ from this endpoint, + // for example, if a dynamic port number is assigned. + // + transceiver: function(endpoint) + { + return null; + }, + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. In case an acceptor is created, this operation + // also returns a new "effective" endpoint, which might differ + // from this endpoint, for example, if a dynamic port number is + // assigned. + // + acceptor: function(endpoint, adapterName) + { + return null; + }, + connect: function() + { + if(this._instance.traceLevels().network >= 2) + { + this._instance.initializationData().logger.trace(this._instance.traceLevels().networkCat, + "trying to establish " + (this._secure ? "wss" : "ws") + " connection to " + this._host + ":" + + this._port); + } + + return Transceiver.createOutgoing(this._instance, this._secure, new Address(this._host, this._port), + this._resource); + }, + hashCode: function() + { + return this._hashCode; + }, + // + // Compare endpoints for sorting purposes + // + equals: function(p) + { + if(!(p instanceof EndpointI)) + { + return false; + } + + if(this === p) + { + return true; + } + + if(this._host !== p._host) + { + return false; + } + + if(this._port !== p._port) + { + return false; + } + + if(this._timeout !== p._timeout) + { + return false; + } + + if(this._connectionId !== p._connectionId) + { + return false; + } + + if(this._compress !== p._compress) + { + return false; + } + + if(this._resource !== p._resource) + { + return false; + } + + return true; + }, + compareTo: function(p) + { + if(this === p) + { + return 0; + } + + if(p === null) + { + return 1; + } + + if(!(p instanceof EndpointI)) + { + return this.type() < p.type() ? -1 : 1; + } + + if(this._port < p._port) + { + return -1; + } + else if(p._port < this._port) + { + return 1; + } + + if(this._timeout < p._timeout) + { + return -1; + } + else if(p._timeout < this._timeout) + { + return 1; + } + + if(this._connectionId != p._connectionId) + { + return this._connectionId < p._connectionId ? -1 : 1; + } + + if(!this._compress && p._compress) + { + return -1; + } + else if(!p._compress && this._compress) + { + return 1; + } + + if(this._host < p._host) + { + return -1; + } + else if(p._host < this._host) + { + return 1; + } + + if(this._resource == p._resource) + { + return 0; + } + else + { + return this._resource < p._resource ? -1 : 1; + } + }, + calcHashValue: function() + { + var h = 5381; + h = HashUtil.addNumber(h, this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType); + h = HashUtil.addString(h, this._host); + h = HashUtil.addNumber(h, this._port); + h = HashUtil.addNumber(h, this._timeout); + h = HashUtil.addString(h, this._connectionId); + h = HashUtil.addBoolean(h, this._compress); + h = HashUtil.addString(h, this._resource); + this._hashCode = h; + } + }); + + EndpointI.fromString = function(instance, secure, str, oaEndpoint) + { + var host = null; + var port = 0; + var timeout = -1; + var compress = false; + var resource = ""; + + var protocol = secure ? "wss" : "ws"; + + var arr = str.split(/[ \t\n\r]+/); + + var i = 0; + while(i < arr.length) + { + if(arr[i].length === 0) + { + i++; + continue; + } + + var option = arr[i++]; + if(option.length != 2 && option.charAt(0) != '-') + { + throw new Ice.EndpointParseException("expected an endpoint option but found `" + option + + "' in endpoint `" + protocol + " " + str + "'"); + } + + var argument = null; + if(i < arr.length && arr[i].charAt(0) != '-') + { + argument = arr[i++]; + if(argument.charAt(0) == '\"' && argument.charAt(argument.length - 1) == '\"') + { + argument = argument.substring(1, argument.length - 1); + } + } + + switch(option.charAt(1)) + { + case 'h': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -h option in endpoint `" + + protocol + " " + str + "'"); + } + + host = argument; + break; + } + + case 'p': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -p option in endpoint `" + + protocol + " " + str + "'"); + } + + try + { + port = StringUtil.toInt(argument); + } + catch(ex) + { + throw new Ice.EndpointParseException("invalid port value `" + argument + + "' in endpoint `" + protocol + " " + str + "'"); + } + + if(port < 0 || port > 65535) + { + throw new Ice.EndpointParseException("port value `" + argument + + "' out of range in endpoint `" + protocol + " " + str + + "'"); + } + + break; + } + + case 'r': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -r option in endpoint `" + + protocol + " " + str + "'"); + } + + resource = argument; + break; + } + + case 't': + { + if(argument === null) + { + throw new Ice.EndpointParseException("no argument provided for -t option in endpoint `" + + protocol + " " + str + "'"); + } + + try + { + timeout = StringUtil.toInt(argument); + } + catch(ex) + { + throw new Ice.EndpointParseException("invalid timeout value `" + argument + + "' in endpoint `" + protocol + " " + str + "'"); + } + + break; + } + + case 'z': + { + if(argument !== null) + { + throw new Ice.EndpointParseException("unexpected argument `" + argument + + "' provided for -z option in `" + protocol + " " + str + + "'"); + } + + compress = true; + break; + } + + default: + { + throw new Ice.EndpointParseException("unknown option `" + option + "' in `" + protocol + " " + + str + "'"); + } + } + } + + if(host === null) + { + host = instance.defaultsAndOverrides().defaultHost; + } + else if(host == "*") + { + if(oaEndpoint) + { + host = null; + } + else + { + throw new Ice.EndpointParseException("`-h *' not valid for proxy endpoint `" + protocol + " " + str + + "'"); + } + } + + if(host === null) + { + host = ""; + } + return new EndpointI(instance, secure, host, port, timeout, "", compress, resource); + }; + + EndpointI.fromStream = function(s, secure) + { + s.startReadEncaps(); + var host = s.readString(); + var port = s.readInt(); + var timeout = s.readInt(); + var compress = s.readBool(); + var resource = s.readString(); + s.endReadEncaps(); + return new EndpointI(s.instance, secure, host, port, timeout, "", compress, resource); + }; + + IceWS.EndpointI = EndpointI; + global.IceWS = IceWS; + + var EndpointInfoI = Class(IceWS.EndpointInfo, { + __init__: function(secure, timeout, compress, host, port, resource) + { + IceWS.EndpointInfo.call(this, timeout, compress, host, port, resource); + this.secure = secure; + }, + type: function() + { + return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType; + }, + datagram: function() + { + return false; + }, + secure: function() + { + return this._secure; + } + }); +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/browser/Transceiver.js b/js/src/Ice/browser/Transceiver.js new file mode 100644 index 00000000000..f700dfbfc55 --- /dev/null +++ b/js/src/Ice/browser/Transceiver.js @@ -0,0 +1,418 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(global){ + require("Ice/Class"); + require("Ice/Debug"); + require("Ice/ExUtil"); + require("Ice/Network"); + require("Ice/SocketOperation"); + require("Ice/Connection"); + require("Ice/Exception"); + require("Ice/LocalException"); + + require("Ice/browser/ConnectionInfo"); + + var Ice = global.Ice || {}; + var IceWS = global.IceWS || {}; + + var Debug = Ice.Debug; + var ExUtil = Ice.ExUtil; + var Network = Ice.Network; + var SocketOperation = Ice.SocketOperation; + var Conn = Ice.Connection; + var LocalException = Ice.LocalException; + var SocketException = Ice.SocketException; + + var StateNeedConnect = 0; + var StateConnectPending = 1; + var StateConnected = 2; + var StateClosePending = 3; + var StateClosed = 4; + + var IsFirefox = navigator.userAgent.indexOf("Firefox") !== -1; + + var Transceiver = Ice.Class({ + __init__: function(instance) + { + this._traceLevels = instance.traceLevels(); + this._logger = instance.initializationData().logger; + this._readBuffers = []; + this._readPosition = 0; + }, + setCallbacks: function(connectedCallback, bytesAvailableCallback, bytesWrittenCallback) + { + this._connectedCallback = connectedCallback; + this._bytesAvailableCallback = bytesAvailableCallback; + this._bytesWrittenCallback = bytesWrittenCallback; + }, + // + // Returns SocketOperation.None when initialization is complete. + // + initialize: function(readBuffer, writeBuffer) + { + try + { + if(this._exception) + { + throw this._exception; + } + + if(this._state === StateNeedConnect) + { + this._state = StateConnectPending; + this._fd = new WebSocket(this._url, "ice.zeroc.com"); + this._fd.binaryType = "arraybuffer"; + var self = this; + this._fd.onopen = function(e) { self.socketConnected(e); }; + this._fd.onmessage = function(e) { self.socketBytesAvailable(e.data); }; + this._fd.onclose = function(e) { self.socketClosed(e); }; + return SocketOperation.Connect; // Waiting for connect to complete. + } + else if(this._state === StateConnectPending) + { + // + // Socket is connected. + // + this._desc = fdToString(this._addr); + this._state = StateConnected; + } + } + catch(err) + { + if(!this._exception) + { + this._exception = translateError(this._state, err); + } + + if(this._traceLevels.network >= 2) + { + var s = []; + s.push("failed to establish " + this.type() + " connection\n"); + s.push(fdToString(this._addr)); + this._logger.trace(this._traceLevels.networkCat, s.join("")); + } + throw this._exception; + } + + Debug.assert(this._state === StateConnected); + if(this._traceLevels.network >= 1) + { + this._logger.trace(this._traceLevels.networkCat, this.type() + + " connection established\n" + this._desc); + } + return SocketOperation.None; + }, + register: function() + { + // + // Register the socket data listener. + // + this._registered = true; + if(this._hasBytesAvailable || this._exception) + { + this._bytesAvailableCallback(); + this._hasBytesAvailable = false; + } + }, + unregister: function() + { + // + // Unregister the socket data listener. + // + this._registered = false; + }, + close: function() + { + if(this._fd === null) + { + Debug.assert(this._exception); // Websocket creation failed. + return; + } + + // + // WORKAROUND: With Firefox, calling close() if the websocket isn't connected + // yet doesn't close the connection. The server doesn't receive any close frame + // and the underlying socket isn't closed causing the server to hang on closing + // the connection until the browser exits. + // + // To workaround this problem, we always wait for the socket to be connected + // or closed before closing the socket. + // + if(this._fd.readyState === WebSocket.CONNECTING && IsFirefox) + { + this._state = StateClosePending; + return; + } + + if(this._state == StateConnected && this._traceLevels.network >= 1) + { + this._logger.trace(this._traceLevels.networkCat, "closing " + this.type() + " connection\n" + + this._desc); + } + + Debug.assert(this._fd !== null); + try + { + this._state = StateClosed; + this._fd.close(); + } + catch(ex) + { + throw translateError(this._state, ex); + } + finally + { + this._fd = null; + } + }, + // + // Returns true if all of the data was flushed to the kernel buffer. + // + write: function(byteBuffer) + { + if(this._exception) + { + throw this._exception; + } + + var remaining = byteBuffer.remaining; + Debug.assert(remaining > 0); + Debug.assert(this._fd); + + // + // Delay the send if more than 1KB is already queued for + // sending on the socket. If less, we consider that it's + // fine to push more data on the socket. + // + if(this._fd.bufferedAmount < 1024) + { + // + // Create a slice of the source buffer representing the remaining data to be written. + // + var slice = byteBuffer.b.slice(byteBuffer.position, byteBuffer.position + remaining); + + // + // The socket will accept all of the data. + // + byteBuffer.position = byteBuffer.position + remaining; + this._fd.send(slice); + if(remaining > 0 && this._traceLevels.network >= 3) + { + var msg = "sent " + remaining + " of " + remaining + " bytes via " + this.type() + "\n" +this._desc; + this._logger.trace(this._traceLevels.networkCat, msg); + } + return true; + } + else + { + var transceiver = this; + var writtenCB = function() + { + if(transceiver._fd) + { + if(transceiver._fd.bufferedAmount === 0 || this._exception) + { + transceiver._bytesWrittenCallback(); + } + else + { + setTimeout(writtenCB, 50); + } + } + }; + setTimeout(writtenCB, 50); + return false; + } + }, + read: function(byteBuffer, moreData) + { + if(this._exception) + { + throw this._exception; + } + + moreData.value = false; + + if(this._readBuffers.length === 0) + { + return false; // No data available. + } + + var avail = this._readBuffers[0].byteLength - this._readPosition; + Debug.assert(avail > 0); + var remaining = byteBuffer.remaining; + + while(byteBuffer.remaining > 0) + { + if(avail > byteBuffer.remaining) + { + avail = byteBuffer.remaining; + } + + new Uint8Array(byteBuffer.b).set(new Uint8Array(this._readBuffers[0], this._readPosition, avail), + byteBuffer.position); + + byteBuffer.position += avail; + this._readPosition += avail; + if(this._readPosition === this._readBuffers[0].byteLength) + { + // + // We've exhausted the current read buffer. + // + this._readPosition = 0; + this._readBuffers.shift(); + if(this._readBuffers.length === 0) + { + break; // No more data - we're done. + } + else + { + avail = this._readBuffers[0].byteLength; + } + } + } + + var n = remaining - byteBuffer.remaining; + if(n > 0 && this._traceLevels.network >= 3) + { + var msg = "received " + n + " of " + remaining + " bytes via " + this.type() + "\n" + this._desc; + this._logger.trace(this._traceLevels.networkCat, msg); + } + + moreData.value = this._readBuffers.byteLength > 0; + + return byteBuffer.remaining === 0; + }, + type: function() + { + return this._secure ? "wss" : "ws"; + }, + getInfo: function() + { + Debug.assert(this._fd !== null); + var info = this.createInfo(); + + // + // The WebSocket API doens't provide this info + // + info.localAddress = ""; + info.localPort = -1; + info.remoteAddress = this._addr.host; + info.remotePort = this._addr.port; + return info; + }, + createInfo: function() + { + return new IceWS.ConnectionInfo(); + }, + checkSendSize: function(stream, messageSizeMax) + { + if(stream.size > messageSizeMax) + { + ExUtil.throwMemoryLimitException(stream.size, messageSizeMax); + } + }, + toString: function() + { + return this._desc; + }, + socketConnected: function(e) + { + if(this._state == StateClosePending) + { + this.close(); + return; + } + + Debug.assert(this._connectedCallback !== null); + this._connectedCallback(); + }, + socketBytesAvailable: function(buf) + { + Debug.assert(this._bytesAvailableCallback !== null); + if(buf.byteLength > 0) + { + this._readBuffers.push(buf); + if(this._registered) + { + this._bytesAvailableCallback(); + } + else if(!this._hasBytesAvailable) + { + this._hasBytesAvailable = true; + } + } + }, + socketClosed: function(err) + { + if(this._state == StateClosePending) + { + this.close(); + return; + } + + this._exception = translateError(this._state, err); + if(this._state < StateConnected) + { + this._connectedCallback(); + } + else if(this._registered) + { + this._bytesAvailableCallback(); + } + }, + }); + + function fdToString(address) + { + return "local address = <not available>\nremote address = " + address.host + ":" + address.port; + } + + function translateError(state, err) + { + if(state < StateConnected) + { + return new Ice.ConnectFailedException(err.code, err); + } + else + { + if(err === 1000) // CLOSE_NORMAL + { + throw new Ice.ConnectionLostException(); + } + return new Ice.SocketException(err.code, err); + } + } + + Transceiver.createOutgoing = function(instance, secure, addr, resource) + { + var transceiver = new Transceiver(instance); + + var url = secure ? "wss" : "ws"; + url += "://" + addr.host; + if(addr.port !== 80) + { + url += ":" + addr.port; + } + url += resource ? resource : "/"; + transceiver._url = url; + transceiver._fd = null; + transceiver._addr = addr; + transceiver._desc = "remote address: " + addr.host + ":" + addr.port + " <not connected>"; + transceiver._state = StateNeedConnect; + transceiver._secure = secure; + transceiver._exception = null; + + return transceiver; + }; + + IceWS.Transceiver = Transceiver; + global.IceWS = IceWS; +}(typeof (global) === "undefined" ? window : global)); diff --git a/js/src/Ice/package.json b/js/src/Ice/package.json new file mode 100644 index 00000000000..c5e32a6ea18 --- /dev/null +++ b/js/src/Ice/package.json @@ -0,0 +1,5 @@ +{ + "name": "Ice", + "version": "0.1.0", + "main": "Ice.js" +} diff --git a/js/src/IceGrid/.gitignore b/js/src/IceGrid/.gitignore new file mode 100644 index 00000000000..2cd3235f4ad --- /dev/null +++ b/js/src/IceGrid/.gitignore @@ -0,0 +1,10 @@ +Admin.js +Descriptor.js +Exception.js +FileParser.js +Locator.js +Observer.js +Query.js +Registry.js +Session.js +UserAccountMapper.js diff --git a/js/src/IceGrid/IceGrid.js b/js/src/IceGrid/IceGrid.js new file mode 100644 index 00000000000..413b0fd0aa9 --- /dev/null +++ b/js/src/IceGrid/IceGrid.js @@ -0,0 +1,21 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +(function(){ + require("IceGrid/Admin"); + require("IceGrid/Descriptor"); + require("IceGrid/Exception"); + require("IceGrid/FileParser"); + require("IceGrid/Locator"); + require("IceGrid/Observer"); + require("IceGrid/Query"); + require("IceGrid/Registry"); + require("IceGrid/Session"); + require("IceGrid/UserAccountMapper"); +}()); diff --git a/js/src/IceGrid/Makefile b/js/src/IceGrid/Makefile new file mode 100644 index 00000000000..0d952c92ced --- /dev/null +++ b/js/src/IceGrid/Makefile @@ -0,0 +1,43 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +LIBNAME = IceGrid + +TARGETS = $(call mklibtargets,$(LIBNAME)) + +SLICES = $(SDIR)/Admin.ice \ + $(SDIR)/Descriptor.ice \ + $(SDIR)/Exception.ice \ + $(SDIR)/FileParser.ice \ + $(SDIR)/Locator.ice \ + $(SDIR)/Observer.ice \ + $(SDIR)/Query.ice \ + $(SDIR)/Registry.ice \ + $(SDIR)/Session.ice \ + $(SDIR)/UserAccountMapper.ice + +SDIR = $(slicedir)/IceGrid + +GEN_SRCS = $(patsubst $(SDIR)/%.ice, %.js, $(SLICES)) + +SRCS := $(GEN_SRCS) +INSTALL_SRCS := IceGrid.js $(GEN_SRCS) + +include $(top_srcdir)/config/Make.rules.js + +SLICE2JSFLAGS := $(SLICE2JSFLAGS) --ice -I$(slicedir) + +lint:: $(INSTALL_SRCS) + jshint $(LINTFLAGS) $(INSTALL_SRCS) + +install:: all + $(call installlib,$(DESTDIR)$(install_libdir),$(libdir),$(LIBNAME)) + $(call installmodule,$(DESTDIR)$(install_moduledir),$(INSTALL_SRCS),$(LIBNAME)) diff --git a/js/src/IceGrid/Makefile.mak b/js/src/IceGrid/Makefile.mak new file mode 100644 index 00000000000..f64be2a73c6 --- /dev/null +++ b/js/src/IceGrid/Makefile.mak @@ -0,0 +1,42 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +LIBNAME = IceGrid + +GEN_SRCS = Admin.js \ + Descriptor.js \ + Exception.js \ + FileParser.js \ + Locator.js \ + Observer.js \ + Query.js \ + Registry.js \ + Session.js \ + UserAccountMapper.js + +SDIR = $(slicedir)\IceGrid + +SRCS = $(GEN_SRCS) +INSTALL_SRCS = IceGrid.js $(GEN_SRCS) + +!include $(top_srcdir)\config\Make.rules.mak.js + +SLICE2JSFLAGS = $(SLICE2JSFLAGS) --ice -I"$(slicedir)" + +MODULEDIR = $(install_moduledir)\$(LIBNAME) + +install:: all + @if not exist $(MODULEDIR) \ + @echo "Creating $(MODULEDIR)" && \ + mkdir "$(MODULEDIR)" + @for %i in ( $(INSTALL_SRCS) ) do \ + copy %i "$(MODULEDIR)" + copy package.json "$(MODULEDIR)" diff --git a/js/src/IceGrid/package.json b/js/src/IceGrid/package.json new file mode 100644 index 00000000000..d928926a5e1 --- /dev/null +++ b/js/src/IceGrid/package.json @@ -0,0 +1,5 @@ +{ + "name": "IceGrid", + "version": "0.1.0", + "main": "IceGrid.js" +} diff --git a/js/src/IceStorm/.gitignore b/js/src/IceStorm/.gitignore new file mode 100644 index 00000000000..1e3e811ef95 --- /dev/null +++ b/js/src/IceStorm/.gitignore @@ -0,0 +1,2 @@ +IceStorm.js +Metrics.js diff --git a/js/src/IceStorm/Makefile b/js/src/IceStorm/Makefile new file mode 100644 index 00000000000..876c8e05439 --- /dev/null +++ b/js/src/IceStorm/Makefile @@ -0,0 +1,35 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +LIBNAME = IceStorm + +TARGETS = $(call mklibtargets,$(LIBNAME)) + +SLICES = $(SDIR)/IceStorm.ice \ + $(SDIR)/Metrics.ice + +SDIR = $(slicedir)/IceStorm + +GEN_SRCS = $(patsubst $(SDIR)/%.ice, %.js, $(SLICES)) + +SRCS := $(GEN_SRCS) +INSTALL_SRCS := IceStorm.js $(GEN_SRCS) + +include $(top_srcdir)/config/Make.rules.js + +SLICE2JSFLAGS := $(SLICE2JSFLAGS) --ice -I$(slicedir) + +lint:: $(INSTALL_SRCS) + jshint $(LINTFLAGS) $(INSTALL_SRCS) + +install:: all + $(call installlib,$(DESTDIR)$(install_libdir),$(libdir),$(LIBNAME)) + $(call installmodule,$(DESTDIR)$(install_moduledir),$(INSTALL_SRCS),$(LIBNAME)) diff --git a/js/src/IceStorm/Makefile.mak b/js/src/IceStorm/Makefile.mak new file mode 100644 index 00000000000..073ab55fca9 --- /dev/null +++ b/js/src/IceStorm/Makefile.mak @@ -0,0 +1,34 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +LIBNAME = IceStorm + +GEN_SRCS = IceStorm.js \ + Metrics.js + +SDIR = $(slicedir)\IceStorm + +SRCS = $(GEN_SRCS) +INSTALL_SRCS = IceStorm.js $(GEN_SRCS) + +!include $(top_srcdir)\config\Make.rules.mak.js + +SLICE2JSFLAGS = $(SLICE2JSFLAGS) --ice -I"$(slicedir)" + +MODULEDIR = $(install_moduledir)\$(LIBNAME) + +install:: all + @if not exist $(MODULEDIR) \ + @echo "Creating $(MODULEDIR)" && \ + mkdir "$(MODULEDIR)" + @for %i in ( $(INSTALL_SRCS) ) do \ + copy %i "$(MODULEDIR)" + copy package.json "$(MODULEDIR)" diff --git a/js/src/IceStorm/package.json b/js/src/IceStorm/package.json new file mode 100644 index 00000000000..c41a8446174 --- /dev/null +++ b/js/src/IceStorm/package.json @@ -0,0 +1,5 @@ +{ + "name": "IceStorm", + "version": "0.1.0", + "main": "IceStorm.js" +} diff --git a/js/src/Makefile b/js/src/Makefile new file mode 100644 index 00000000000..8144440381c --- /dev/null +++ b/js/src/Makefile @@ -0,0 +1,33 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = .. + +include $(top_srcdir)/config/Make.rules.js + +SUBDIRS = Ice Glacier2 IceStorm IceGrid + +.PHONY: $(EVERYTHING) $(SUBDIRS) + + +all:: $(SUBDIRS) + +$(SUBDIRS): + @echo "making all in $@" + @$(MAKE) all --directory=$@ + +$(EVERYTHING_EXCEPT_ALL):: + @for subdir in $(SUBDIRS); \ + do \ + if test -d $$subdir ; \ + then \ + echo "making $@ in $$subdir"; \ + ( cd $$subdir && $(MAKE) $@ ) || exit 1; \ + fi; \ + done diff --git a/js/src/Makefile.mak b/js/src/Makefile.mak new file mode 100644 index 00000000000..d87264e34a1 --- /dev/null +++ b/js/src/Makefile.mak @@ -0,0 +1,19 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = .. + +!include $(top_srcdir)\config\Make.rules.mak.js + +SUBDIRS = Ice Glacier2 IceStorm IceGrid + +$(EVERYTHING):: + @for %i in ( $(SUBDIRS) ) do \ + @echo "making $@ in %i" && \ + cmd /c "cd %i && $(MAKE) -nologo -f Makefile.mak $@" || exit 1 |