summaryrefslogtreecommitdiff
path: root/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js
diff options
context:
space:
mode:
authorDerrell Lipman <derrell@samba.org>2007-02-06 03:23:52 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:44:34 -0500
commitef256c958066c19bb10cbe9745bdf96b6514762d (patch)
treedbe379cb3021c5af2d1b8f74528e4fff968fc712 /webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js
parenta8d0f5df6266aeacdb20326094c0401618ab62e8 (diff)
downloadsamba-ef256c958066c19bb10cbe9745bdf96b6514762d.tar.gz
samba-ef256c958066c19bb10cbe9745bdf96b6514762d.tar.bz2
samba-ef256c958066c19bb10cbe9745bdf96b6514762d.zip
r21167: - Upgrade to latest released qooxdoo. This is step 1 of the upgrade process,
which involves adding the latest SDK. This new version has not 2, but 4 icon themes. In order to quiet Andrew B. who thinks that 9000+ files in a gui api is too many (come on, disk space is free these days :-), I have removed 3 of the 4 icon themes, leaving only the default, Nuvola. That lowers the file count by 1/3. I'm sure Andrew still isn't happy, but I hope he's a bit happier... Step 2 will make him happier yet. (This used to be commit d161c1382c36238105b85d0499d5a1011f580f52)
Diffstat (limited to 'webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js')
-rw-r--r--webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js581
1 files changed, 581 insertions, 0 deletions
diff --git a/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js
new file mode 100644
index 0000000000..88fe2f46a6
--- /dev/null
+++ b/webapps/qooxdoo-0.6.5-sdk/frontend/framework/source/class/qx/io/remote/Rpc.js
@@ -0,0 +1,581 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 STZ-IDA, Germany, http://www.stz-ida.de
+ 2006 Derrell Lipman
+
+ License:
+ LGPL: http://www.gnu.org/licenses/lgpl.html
+ EPL: http://www.eclipse.org/org/documents/epl-v10.php
+ See the LICENSE file in the project's top-level directory for details.
+
+ Authors:
+ * Andreas Junghans (lucidcake)
+ * Derrell Lipman (derrell)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(io_remote)
+
+************************************************************************ */
+
+
+/**
+ * Provides a Remote Procedure Call (RPC) implementation.
+ *
+ * Each instance of this class represents a "Service". These services can
+ * correspond to various concepts on the server side (depending on the
+ * programming language/environment being used), but usually, a service means
+ * a class on the server.
+ *
+ * In case multiple instances of the same service are needed, they can be
+ * distinguished by ids. If such an id is specified, the server routes all
+ * calls to a service that have the same id to the same server-side instance.
+ *
+ * When calling a server-side method, the parameters and return values are
+ * converted automatically. Supported types are int (and Integer), double
+ * (and Double), String, Date, Map, and JavaBeans. Beans must habe a default
+ * constructor on the server side and are represented by simple JavaScript
+ * objects on the client side (used as associative arrays with keys matching
+ * the server-side properties). Beans can also be nested, but be careful to not
+ * create circular references! There are no checks to detect these (which would
+ * be expensive), so you as the user are responsible for avoiding them.
+ *
+ * @param url {String} identifies the url where the service
+ * is found. Note that if the url is to
+ * a domain (server) other than where the
+ * qooxdoo script came from, i.e. it is
+ * cross-domain, then you must also call
+ * the setCrossDomain(true) method to
+ * enable the ScriptTransport instead of
+ * the XmlHttpTransport, since the latter
+ * can not handle cross-domain requests.
+ *
+ * @param serviceName {String} identifies the service. For the Java
+ * implementation, this is the fully
+ * qualified name of the class that offers
+ * the service methods
+ * (e.g. "my.pkg.MyService").
+ *
+ * @event completed (qx.event.type.DataEvent)
+ * @event failed (qx.event.type.DataEvent)
+ * @event timeout (qx.event.type.DataEvent)
+ * @event aborted (qx.event.type.DataEvent)
+ */
+
+qx.OO.defineClass("qx.io.remote.Rpc", qx.core.Target,
+function(url, serviceName)
+{
+ qx.core.Target.call(this);
+
+ this.setUrl(url);
+ if (serviceName != null) {
+ this.setServiceName(serviceName);
+ }
+ this._previousServerSuffix = null;
+ this._currentServerSuffix = null;
+ if (qx.core.ServerSettings) {
+ this._currentServerSuffix = qx.core.ServerSettings.serverPathSuffix;
+ }
+});
+
+
+
+
+
+
+/*
+---------------------------------------------------------------------------
+ PROPERTIES
+---------------------------------------------------------------------------
+*/
+
+/**
+ The timeout for asynchronous calls in milliseconds.
+ */
+qx.OO.addProperty({ name : "timeout", type : "number" });
+
+/**
+ Indicate that the request is cross domain.
+
+ A request is cross domain if the request's URL points to a host other
+ than the local host. This switches the concrete implementation that
+ is used for sending the request from qx.io.remote.XmlHttpTransport to
+ qx.io.remote.ScriptTransport because only the latter can handle cross domain
+ requests.
+*/
+qx.OO.addProperty({ name : "crossDomain", type : "boolean", defaultValue : false });
+
+/**
+ The URL at which the service is located.
+*/
+qx.OO.addProperty({ name : "url", type : "string", defaultValue : null });
+
+/**
+ The service name.
+*/
+qx.OO.addProperty({ name : "serviceName", type : "string", defaultValue : null });
+
+/**
+ Data sent as "out of band" data in the request to the server. The format of
+ the data is opaque to RPC and may be recognized only by particular servers
+ It is up to the server to decide what to do with it: whether to ignore it,
+ handle it locally before calling the specified method, or pass it on to the
+ method. This server data is not sent to the server if it has been set to
+ 'undefined'.
+*/
+qx.OO.addProperty({ name : "serverData", type : "object", defaultValue : undefined });
+
+/**
+ Username to use for HTTP authentication. Null if HTTP authentication
+ is not used.
+*/
+qx.OO.addProperty({ name : "username", type : "string" });
+
+/**
+ Password to use for HTTP authentication. Null if HTTP authentication
+ is not used.
+*/
+qx.OO.addProperty({ name : "password", type : "string" });
+
+/**
+ Use Basic HTTP Authentication
+*/
+qx.OO.addProperty({ name : "useBasicHttpAuth", type : "boolean" });
+
+/**
+ Origins of errors
+*/
+qx.io.remote.Rpc.origin =
+{
+ server : 1,
+ application : 2,
+ transport : 3,
+ local : 4
+}
+
+/**
+ Locally-detected errors
+*/
+qx.io.remote.Rpc.localError =
+{
+ timeout : 1,
+ abort : 2
+}
+
+
+/*
+---------------------------------------------------------------------------
+ CORE METHODS
+---------------------------------------------------------------------------
+*/
+
+/* callType: 0 = sync, 1 = async with handler, 2 = async event listeners */
+/**
+ * Internal RPC call method
+ *
+ * @param args {Array} array of arguments
+ * @param callType {Integer} 0 = sync, 1 = async with handler, 2 = async event listeners
+ * @param refreshSession {Boolean} whether a new session should be requested
+ */
+qx.Proto._callInternal = function(args, callType, refreshSession) {
+ var self = this;
+ var offset = (callType == 0 ? 0 : 1)
+ var whichMethod = (refreshSession ? "refreshSession" : args[offset]);
+ var handler = args[0];
+ var argsArray = [];
+ var eventTarget = this;
+
+ for (var i = offset + 1; i < args.length; ++i) {
+ argsArray.push(args[i]);
+ }
+ var req = new qx.io.remote.Request(this.getUrl(),
+ qx.net.Http.METHOD_POST,
+ qx.util.Mime.JSON);
+ var requestObject = {
+ "service": (refreshSession ? null : this.getServiceName()),
+ "method": whichMethod,
+ "id": req.getSequenceNumber(),
+ "params": argsArray
+ // additional field 'server_data' optionally included, below
+ }
+
+ // See if there's any out-of-band data to be sent to the server
+ var serverData = this.getServerData();
+ if (serverData !== undefined) {
+ // There is. Send it.
+ requestObject.server_data = serverData;
+ }
+
+ req.setCrossDomain(this.getCrossDomain());
+
+ if (this.getUsername()) {
+ req.setUseBasicHttpAuth(this.getUseBasicHttpAuth());
+ req.setUsername(this.getUsername());
+ req.setPassword(this.getPassword());
+ }
+
+ req.setTimeout(this.getTimeout());
+ var ex = null;
+ var id = null;
+ var result = null;
+
+ var handleRequestFinished = function(eventType, eventTarget) {
+ switch(callType)
+ {
+ case 0: // sync
+ break;
+
+ case 1: // async with handler function
+ handler(result, ex, id);
+ break;
+
+ case 2: // async with event listeners
+ // Dispatch the event to our listeners.
+ if (! ex) {
+ eventTarget.createDispatchDataEvent(eventType, result);
+ } else {
+ // Add the id to the exception
+ ex.id = id;
+
+ if (args[0]) { // coalesce
+ // They requested that we coalesce all failure types to "failed"
+ eventTarget.createDispatchDataEvent("failed", ex);
+ } else {
+ // No coalese so use original event type
+ eventTarget.createDispatchDataEvent(eventType, ex);
+ }
+ }
+ }
+ }
+
+ var addToStringToObject = function(obj) {
+ obj.toString = function() {
+ switch(obj.origin)
+ {
+ case qx.io.remote.Rpc.origin.server:
+ return "Server error " + obj.code + ": " + obj.message;
+ case qx.io.remote.Rpc.origin.application:
+ return "Application error " + obj.code + ": " + obj.message;
+ case qx.io.remote.Rpc.origin.transport:
+ return "Transport error " + obj.code + ": " + obj.message;
+ case qx.io.remote.Rpc.origin.local:
+ return "Local error " + obj.code + ": " + obj.message;
+ default:
+ return "UNEXPECTED origin " + obj.origin + " error " + obj.code + ": " + obj.message;
+ }
+ }
+ }
+
+ var makeException = function(origin, code, message) {
+ var ex = new Object();
+
+ ex.origin = origin;
+ ex.code = code;
+ ex.message = message;
+ addToStringToObject(ex);
+
+ return ex;
+ }
+
+ req.addEventListener("failed", function(evt) {
+ var code = evt.getData().getStatusCode();
+ ex = makeException(qx.io.remote.Rpc.origin.transport,
+ code,
+ qx.io.remote.Exchange.statusCodeToString(code));
+ id = this.getSequenceNumber();
+ handleRequestFinished("failed", eventTarget);
+ });
+ req.addEventListener("timeout", function(evt) {
+ ex = makeException(qx.io.remote.Rpc.origin.local,
+ qx.io.remote.Rpc.localError.timeout,
+ "Local time-out expired");
+ id = this.getSequenceNumber();
+ handleRequestFinished("timeout", eventTarget);
+ });
+ req.addEventListener("aborted", function(evt) {
+ ex = makeException(qx.io.remote.Rpc.origin.local,
+ qx.io.remote.Rpc.localError.abort,
+ "Aborted");
+ id = this.getSequenceNumber();
+ handleRequestFinished("aborted", eventTarget);
+ });
+ req.addEventListener("completed", function(evt) {
+ result = evt.getData().getContent();
+ id = result["id"];
+ if (id != this.getSequenceNumber()) {
+ this.warn("Received id (" + id + ") does not match requested id (" + this.getSequenceNumber() + ")!");
+ }
+ var exTest = result["error"];
+ if (exTest != null) {
+ result = null;
+ addToStringToObject(exTest);
+ ex = exTest;
+ } else {
+ result = result["result"];
+ if (refreshSession) {
+ result = eval("(" + result + ")");
+ var newSuffix = qx.core.ServerSettings.serverPathSuffix;
+ if (self._currentServerSuffix != newSuffix) {
+ self._previousServerSuffix = self._currentServerSuffix;
+ self._currentServerSuffix = newSuffix;
+ }
+ self.setUrl(self.fixUrl(self.getUrl()));
+ }
+ }
+ handleRequestFinished("completed", eventTarget);
+ });
+ req.setData(qx.io.Json.stringify(requestObject));
+ req.setAsynchronous(callType > 0);
+
+ if (req.getCrossDomain()) {
+ // Our choice here has no effect anyway. This is purely informational.
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ } else {
+ // When not cross-domain, set type to text/json
+ req.setRequestHeader("Content-Type", qx.util.Mime.JSON);
+ }
+
+ req.send();
+
+ if (callType == 0) {
+ if (ex != null) {
+ var error = new Error(ex.toString());
+ error.rpcdetails = ex;
+ throw error;
+ }
+ return result;
+ } else {
+ return req;
+ }
+}
+
+
+/**
+ * Helper method to rewrite a URL with a stale session id (so that it includes
+ * the correct session id afterwards).
+ *
+ * @param url {String} the URL to examine.
+ *
+ * @return {String} the (possibly re-written) URL.
+ */
+
+qx.Proto.fixUrl = function(url) {
+ if (this._previousServerSuffix == null || this._currentServerSuffix == null ||
+ this._previousServerSuffix == "" ||
+ this._previousServerSuffix == this._currentServerSuffix) {
+ return url;
+ }
+ var index = url.indexOf(this._previousServerSuffix);
+ if (index == -1) {
+ return url;
+ }
+ return url.substring(0, index) + this._currentServerSuffix +
+ url.substring(index + this._previousServerSuffix.length);
+};
+
+
+/**
+ * Makes a synchronous server call. The method arguments (if any) follow
+ * after the method name (as normal JavaScript arguments, separated by commas,
+ * not as an array).
+ * <p>
+ * If a problem occurs when making the call, an exception is thrown.
+ * </p>
+ * <p>
+ * WARNING. With some browsers, the synchronous interface
+ * causes the browser to hang while awaiting a response! If the server
+ * decides to pause for a minute or two, your browser may do nothing
+ * (including refreshing following window changes) until the response is
+ * received. Instead, use the asynchronous interface.
+ * </p>
+ * <p>
+ * YOU HAVE BEEN WARNED.
+ * </p>
+ *
+ * @param methodName {String} the name of the method to call.
+ *
+ * @return {var} the result returned by the server.
+ */
+
+qx.Proto.callSync = function(methodName) {
+ return this._callInternal(arguments, 0);
+}
+
+
+/**
+ * Makes an asynchronous server call. The method arguments (if any) follow
+ * after the method name (as normal JavaScript arguments, separated by commas,
+ * not as an array).
+ * <p>
+ * When an answer from the server arrives, the <code>handler</code> function
+ * is called with the result of the call as the first, an exception as the
+ * second parameter, and the id (aka sequence number) of the invoking request
+ * as the third parameter. If the call was successful, the second parameter is
+ * <code>null</code>. If there was a problem, the second parameter contains an
+ * exception, and the first one is <code>null</code>.
+ * </p>
+ * <p>
+ * The return value of this method is a call reference that you can store if
+ * you want to abort the request later on. This value should be treated as
+ * opaque and can change completely in the future! The only thing you can rely
+ * on is that the <code>abort</code> method will accept this reference and
+ * that you can retrieve the sequence number of the request by invoking the
+ * getSequenceNumber() method (see below).
+ * </p>
+ * <p>
+ * If a specific method is being called, asynchronously, a number of times in
+ * succession, the getSequenceNumber() method may be used to disambiguate
+ * which request a response corresponds to. The sequence number value is a
+ * value which increments with each request.)
+ * </p>
+ *
+ * @param handler {Function} the callback function.
+ *
+ * @param methodName {String} the name of the method to call.
+ *
+ * @return {var} the method call reference.
+ */
+
+qx.Proto.callAsync = function(handler, methodName) {
+ return this._callInternal(arguments, 1);
+}
+
+
+/**
+ * Makes an asynchronous server call and dispatch an event upon completion or
+ * failure. The method arguments (if any) follow after the method name (as
+ * normal JavaScript arguments, separated by commas, not as an array).
+ * <p>
+ * When an answer from the server arrives (or fails to arrive on time), if an
+ * exception occurred, a "failed", "timeout" or "aborted" event, as
+ * appropriate, is dispatched to any waiting event listeners. If no exception
+ * occurred, a "completed" event is dispatched.
+ * </p>
+ * <p>
+ * When a "failed", "timeout" or "aborted" event is dispatched, the event data
+ * contains an object with the properties 'origin', 'code', 'message' and
+ * 'id'. The object has a toString() function which may be called to convert
+ * the exception to a string.
+ * </p>
+ * <p>
+ * When a "completed" event is dispatched, the event data contains the
+ * JSON-RPC result.
+ * </p>
+ * <p>
+ * The return value of this method is a call reference that you can store if
+ * you want to abort the request later on. This value should be treated as
+ * opaque and can change completely in the future! The only thing you can rely
+ * on is that the <code>abort</code> method will accept this reference and
+ * that you can retrieve the sequence number of the request by invoking the
+ * getSequenceNumber() method (see below).
+ * </p>
+ * <p>
+ * If a specific method is being called, asynchronously, a number of times in
+ * succession, the getSequenceNumber() method may be used to disambiguate
+ * which request a response corresponds to. The sequence number value is a
+ * value which increments with each request.)
+ * </p>
+ *
+ * @param coalesce {Boolean} coalesce all failure types ("failed",
+ * "timeout", and "aborted") to "failed".
+ * This is reasonable in many cases, as
+ * the provided exception contains adequate
+ * disambiguating information.
+ *
+ * @param methodName {String} the name of the method to call.
+ *
+ * @return {var} the method call reference.
+ */
+
+qx.Proto.callAsyncListeners = function(coalesce, methodName) {
+ return this._callInternal(arguments, 2);
+}
+
+
+/**
+ * Refreshes a server session by retrieving the session id again from the
+ * server.
+ * <p>
+ * The specified handler function is called when the refresh is complete. The
+ * first parameter can be <code>true</code> (indicating that a refresh either
+ * wasn't necessary at this time or it was successful) or <code>false</code>
+ * (indicating that a refresh would have been necessary but can't be performed
+ * because the server backend doesn't support it). If there is a non-null
+ * second parameter, it's an exception indicating that there was an error when
+ * refreshing the session.
+ * </p>
+ *
+ * @param handler {Function} a callback function that is called when the
+ * refresh is complete (or failed).
+ */
+
+qx.Proto.refreshSession = function(handler) {
+ if (this.getCrossDomain()) {
+ if (qx.core.ServerSettings && qx.core.ServerSettings.serverPathSuffix) {
+ var timeDiff = (new Date()).getTime() - qx.core.ServerSettings.lastSessionRefresh;
+ if (timeDiff/1000 > (qx.core.ServerSettings.sessionTimeoutInSeconds - 30)) {
+ //this.info("refreshing session");
+ this._callInternal([handler], 1, true);
+ } else {
+ handler(true); // session refresh was OK (in this case: not needed)
+ }
+ } else {
+ handler(false); // no refresh possible, but would be necessary
+ }
+ } else {
+ handler(true); // session refresh was OK (in this case: not needed)
+ }
+}
+
+
+/**
+ * Aborts an asynchronous server call. Consequently, the callback function
+ * provided to <code>callAsync</code> or <code>callAsyncListeners</code> will
+ * be called with an exception.
+ *
+ * @param opaqueCallRef {var} the call reference as returned by
+ * <code>callAsync</code> or
+ * <code>callAsyncListeners</code>
+ */
+
+qx.Proto.abort = function(opaqueCallRef) {
+ opaqueCallRef.abort();
+}
+
+
+/**
+ * Creates an URL for talking to a local service. A local service is one that
+ * lives in the same application as the page calling the service. For backends
+ * that don't support this auto-generation, this method returns null.
+ *
+ * @param instanceId {String ? null} an optional identifier for the
+ * server side instance that should be
+ * used. All calls to the same service
+ * with the same instance id are
+ * routed to the same object instance
+ * on the server. The instance id can
+ * also be used to provide additional
+ * data for the service instantiation
+ * on the server.
+ *
+ * @return {String} the url.
+ */
+
+qx.Class.makeServerURL = function(instanceId) {
+ var retVal = null;
+ if (qx.core.ServerSettings) {
+ retVal = qx.core.ServerSettings.serverPathPrefix + "/.qxrpc" +
+ qx.core.ServerSettings.serverPathSuffix;
+ if (instanceId != null) {
+ retVal += "?instanceId=" + instanceId;
+ }
+ }
+ return retVal;
+}