You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2009/02/09 23:51:44 UTC

svn commit: r742776 - in /myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util: ./ _xhr/

Author: werpu
Date: Mon Feb  9 22:51:43 2009
New Revision: 742776

URL: http://svn.apache.org/viewvc?rev=742776&view=rev
Log:
https://issues.apache.org/jira/browse/MYFACES-2148

alternative transport implementation
smaller and down to the core of the spec without any overhead!

Added:
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_ListenerQueue.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRFrameworkAdapter.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRTransport.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_XHRConst.js
Modified:
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_JSF2Utils.js
    myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_TrinidadFrameworkAdapter.js

Modified: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_JSF2Utils.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_JSF2Utils.js?rev=742776&r1=742775&r2=742776&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_JSF2Utils.js (original)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_JSF2Utils.js Mon Feb  9 22:51:43 2009
@@ -292,18 +292,18 @@
 
 
     myfaces._JSF2Utils.arrayToString = function(/*String or array*/ arr, /*string*/ delimiter) {
-      if( myfaces._JSF2Utils.isString(arr) ) {
-        return arr;
-      }
-      var resultArr = [];
-      for(var cnt = 0; cnt < arr.length; cnt ++) {
-          if(myfaces._JSF2Utils.isString(arr[cnt])) {
-              resultArr.push(arr[cnt]);
-          } else {
-              resultArr.push(arr[cnt].toString());
-          }
-      }
-      return resultArr.join(delimiter);
+        if( myfaces._JSF2Utils.isString(arr) ) {
+            return arr;
+        }
+        var resultArr = [];
+        for(var cnt = 0; cnt < arr.length; cnt ++) {
+            if(myfaces._JSF2Utils.isString(arr[cnt])) {
+                resultArr.push(arr[cnt]);
+            } else {
+                resultArr.push(arr[cnt].toString());
+            }
+        }
+        return resultArr.join(delimiter);
     };
 
     /**
@@ -459,4 +459,34 @@
         .replace(/\"/g, '%22')
         .replace(/\'/g, '%27');
     }
+
+    /**
+     * fetches the XHR object in a browser neutral way
+     * so that it can be processed further on
+     */
+    myfaces._JSF2Utils.getXHR = function() {
+        /*if('undefined' != typeof(myfaces._JSF2Utils._xhr)) {
+            myfaces._JSF2Utils._xhr.abort();
+            return myfaces._JSF2Utils._xhr;
+        }*/
+        //all newer browsers!
+        if ('undefined' != typeof XMLHttpRequest  ) {
+            myfaces._JSF2Utils._xhr = new XMLHttpRequest();
+        }
+        if (!myfaces._JSF2Utils._xhr) {
+            // Interne
+            // t Explorer 6 or older
+            try {
+                myfaces._JSF2Utils._xhr = new ActiveXObject("Msxml2.XMLHTTP");
+            } catch(e) {
+                try {
+                    myfaces._JSF2Utils._xhr = new ActiveXObject("Microsoft.XMLHTTP");
+                } catch(e) {
+                    myfaces._JSF2Utils._xhr = null;
+                }
+            }
+        }
+        return myfaces._JSF2Utils._xhr;
+    }
+
 }

Added: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_ListenerQueue.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_ListenerQueue.js?rev=742776&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_ListenerQueue.js (added)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_ListenerQueue.js Mon Feb  9 22:51:43 2009
@@ -0,0 +1,82 @@
+/*
+ * a classical listener queue pattern
+ */
+
+
+_reserveMyfaces();
+
+/**
+ * Simple listener queue with closures which shall be
+ * called
+ *
+ * idea:
+ * var queue = new myfaces._ListenerQueue();
+ */
+if(!myfaces._JSF2Utils.exists(myfaces, "_ListenerQueue")) {
+    myfaces._ListenerQueue = function() {
+        this._queue = [];
+
+    }
+
+    myfaces._ListenerQueue.prototype._assertListener = function(/*function*/listener) {
+        if("function" != typeof (listener)) {
+            throw new Exception("Error: myfaces._ListenerQueue." + arguments.caller.toString() + "Parameter must be of type function");
+        }
+    }
+
+    myfaces._ListenerQueue.prototype.add = function(/*function*/listener) {
+        this._assertListener( listener);
+        this._queue.push(listener);
+    }
+
+    myfaces._ListenerQueue.prototype.remove = function(/*function*/listener) {
+        this._assertListener( listener);
+        /*find element in queue*/
+        var cnt = 0;
+        while(cnt < this._queue.length && this._queue[cnt] != listener) {
+            cnt += 1;
+        }
+        /*found*/
+        if(cnt < this._queue.length) {
+            this._queue[cnt] = null;
+            /*we remove the element now as fast as possible*/
+            this._queue.splice(cnt, 1);
+        }
+
+    }
+   /**
+     * generic broadcast with a number of arguments being passed down
+     * @param scope the execution scope for the event callback
+     * @param argument,...*  the arguments list which has to be passed
+     *                  down the queue function
+     */
+    myfaces._ListenerQueue.prototype.broadcastScopedEvent = function(scope, /*any*/argument) {
+        for(var cnt = 0; cnt < this._queue.length; cnt ++) {
+            /**
+             * we call the original method under its original scope
+             * use hitch to keep the original scope in place
+             * because there is no way that I can keep it from here
+             **/
+            var varArgs = [];
+            for(var argsCnt = 1; argsCnt < arguments.length; argsCnt++) {
+                varArgs.push(arguments[argsCnt]);
+            }
+            this._queue[cnt].apply(scope, varArgs);
+        }
+    }
+
+    /**
+     * generic broadcast with a number of arguments being passed down
+     */
+    myfaces._ListenerQueue.prototype.broadcastEvent = function(/*any*/argument) {
+        for(var cnt = 0; cnt < this._queue.length; cnt ++) {
+            /**
+             * we call the original method under its original scope
+             * use hitch to keep the original scope in place
+             * because there is no way that I can keep it from here
+             **/
+ 
+            this._queue[cnt].apply(null, arguments);
+        }
+    }
+};
\ No newline at end of file

Modified: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_TrinidadFrameworkAdapter.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_TrinidadFrameworkAdapter.js?rev=742776&r1=742775&r2=742776&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_TrinidadFrameworkAdapter.js (original)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_TrinidadFrameworkAdapter.js Mon Feb  9 22:51:43 2009
@@ -47,6 +47,7 @@
                 break;;
             case myfaces._TrXMLRequestEvent.STATUS_SEND_AFTER:
                 /*still waiting, we can add listeners later if it is allowed*/
+
                 break;
             case myfaces._TrXMLRequestEvent.STATUS_COMPLETE:
                 /**

Added: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRFrameworkAdapter.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRFrameworkAdapter.js?rev=742776&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRFrameworkAdapter.js (added)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRFrameworkAdapter.js Mon Feb  9 22:51:43 2009
@@ -0,0 +1,100 @@
+/*
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+
+
+_reserveMyfaces();
+
+/**
+ * Adapter functionc call for the SimpleXHR Transport to be
+ * compatible to jsf2!
+ * For now no direct connection should exist between jsf2 and
+ * the transports so that they stay interchangeable!
+ *
+ * Generally a factory pattern should be supported in the long
+ * run to be able to switch adapters on the fly, probably an iframe
+ * transport has to be added as well.
+ *
+ * But to keep the code lean it should be noted
+ * that no transport code should be mixed with the other!
+ */
+if (!myfaces._JSF2Utils.exists(myfaces, "_SimpleXHRFrameworkAdapter")) {
+    myfaces._SimpleXHRFrameworkAdapter = function() {
+        this._delegate = new myfaces._SimpleXHRTransport();
+        /*we fixate the scope of the event callback to the this object trinidad does a remapping 
+         *to the scope object we currently do it here later*/
+        this._delegate.addEventListener(this._eventCallback);
+    };
+
+    /**
+     * unmapping via the event callback
+     * which then triggers into
+     * the jsf event and error subsystem
+     */
+    myfaces._SimpleXHRFrameworkAdapter.prototype._eventCallback = function(/**/event) {
+        var xhrConst = myfaces._XHRConst;
+        var xhrContext = event.context;
+        var request = event.transport;
+
+        var complete = false;
+        /*here we have to do the event mapping back into the ri events*/
+
+        //TODO check whether the scope changes on the sendEvent so that we have to bind it to our context!
+        switch(request.readyState) {
+            //TODO add mapping code here
+            case xhrConst.READY_STATE_OPENED:
+                jsf.ajax.sendEvent(null, xhrContext, jsf.ajax._AJAX_STAGE_BEGIN)
+                break;
+            case xhrConst.READY_STATE_DONE:
+                /**
+                  *here we can do our needed callbacks so
+                  *that the specification is satisfied
+                  **/
+                complete = true;
+                var responseStatusCode = event.status;
+
+                if(200 <= responseStatusCode && 300 > responseStatusCode ) {
+                    jsf.ajax.sendEvent(request, xhrContext, jsf.ajax._AJAX_STAGE_COMPLETE);
+
+                    //TODO do the dom manipulation callback here
+                    jsf.ajax.response(request, xhrContext);
+                } else {
+                    jsf.ajax.sendEvent(request, xhrContext, jsf.ajax._AJAX_STAGE_COMPLETE);
+                    jsf.ajax.sendError(request, xhrContext, jsf.ajax._AJAX_STAGE_HTTPERROR);
+                }
+                break;
+            default:
+                break;
+        }
+        return complete;
+    };
+
+    /**
+     * central request callback
+     */
+    myfaces._SimpleXHRFrameworkAdapter.prototype.sendRequest = function(ajaxContext,  action, viewState, passThroughArguments ) {
+        var data = {};
+        data.context = ajaxContext;
+        data.action = action;
+        data.viewstate = viewState;
+        data.passthroughArguments = passThroughArguments;
+        this._delegate.send(data);
+    };
+
+}
+
+
+
+
+

Added: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRTransport.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRTransport.js?rev=742776&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRTransport.js (added)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_SimpleXHRTransport.js Mon Feb  9 22:51:43 2009
@@ -0,0 +1,169 @@
+/*
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+
+
+/**
+ Implementation of the JSF 2.0 transport layer as xml http request
+ as for now only XmlHttpRequest is implemented as Transport,
+ but in the long run we probably also can use iframes
+ and web sockets as transport layers.
+
+ The pluggable transport layer system is similar to what dojo has to offer
+ but is no direct port due to the huge dependency chain dojo introduces in this
+ area, we do not want to have a dojo dependency in our subsystem
+
+ This transport layer allows an ajax command queuing as defined by the jsf 2 spec
+ and also adds more flexibility in the ajax callbacks to ease the
+ adding of trinidad compatible hooks in the implementation details
+
+ The public api utilizes this transport class for calling the entire ajax part!
+ */
+_reserveMyfaces();
+
+
+if (!myfaces._JSF2Utils.exists(myfaces, "_SimpleXHRTransport")) {
+    myfaces._SimpleXHRTransport = function() {
+        /*we need an instance of our utils for the hitch function*/
+        this._utils = myfaces._JSF2Utils;
+        /*const namespace remapping*/
+        this.xhrConst = myfaces._XHRConst;
+
+        /*currently only post is supported*/
+        this._sendMethod = "POST";
+        /*currently only async is supported*/
+        this._async = true;
+        /*calls for now must be uncached*/
+        this._cached = false;
+
+        this._eventListeners = new myfaces._ListenerQueue();
+  
+        /*
+         * caching data queue forw cached
+         * ajax post requests!
+         */
+        this._transportDataQueue = [];
+    };
+
+
+    myfaces._SimpleXHRTransport.prototype.addEventListener = function(/*function*/ eventListener) {
+        this._eventListeners.add(eventListener);
+    };
+    myfaces._SimpleXHRTransport.prototype.removeEventListener = function(/*function*/ eventListener) {
+        this._eventListeners.remove(eventListener);
+    };
+
+    myfaces._SimpleXHRTransport.prototype._handleXHREvent = function() {
+        /*handle the event callbach*/
+       
+        var data = this._transportDataQueue[0];
+        this._eventListeners.broadcastEvent(data);
+  
+        if(data.transport.readyState === this.xhrConst.READY_STATE_DONE) {
+            this._transportDataQueue.shift();
+            this._process(true);
+            /*ie cleanup*/
+            delete data.transport;
+        }
+        
+        return true;
+    };
+
+    /**
+     * central queue processing callback!
+     * @param inProcess marks whether the process is called from the outside
+     * or after terminating an xhr request from the inside
+     * we have to cover this that way because of a callback error in mozilla
+     */
+    myfaces._SimpleXHRTransport.prototype._process = function( /*boolean*/ inProcess) {
+        try {
+            
+            var size = this._transportDataQueue.length;
+            if(size > 1 && !inProcess) return; /*still in queue no send can be issued*/
+            if(size === 0) {
+               
+                return; /*empty queue process has to terminate*/
+            }
+
+
+            /*note this only works this way because javascript multitasks only premptively
+             *if a real multithreading is added please put the outer send into a critical region
+             *to prevent concurrency issues*/
+            var data = this._transportDataQueue[0];
+
+            data.transport = this._utils.getXHR();
+
+            var transport = data.transport;
+            var passThrough = data.passthroughArguments;
+
+            if(!this._utils.isString(passThrough)) {
+                passThrough = this._utils.getPostbackContentFromMap(passThrough);
+            }
+            if(!this._cached) { //we bypass any caching if needed!
+                //set the pragmas here as well
+                data.action = data.action + ((data.action.indexOf('?') == -1) ? "?" :"&" )+ "AjaxRequestUniqueId = "+(new Date().getTime());
+            }
+            /**
+             * We set the on ready state change here
+             */
+            
+            transport.onreadystatechange = this._utils.hitch(this, this._handleXHREvent);
+            transport.open(this._sendMethod, data.action, this._async);
+            if(this._utils.exists(transport, "setRequestHeader")) {
+                transport.setRequestHeader(this.xhrConst.FACES_REQUEST, this.xhrConst.PARTIAL_AJAX);
+                transport.setRequestHeader(this.xhrConst.CONTENT_TYPE, this.xhrConst.XFORM_ENCODED);
+            }
+       
+            //THE RI does a notification here
+            //be we rely on the official W3C codes
+            //which should be sufficient for the callback
+            //on "begin"
+            transport.send(data.viewstate +"&"+ passThrough);
+           
+
+        } catch (e) {
+            //Browser error...
+            /*internal error we log it and then we splice the affected event away*/
+            myfaces._Logger.getInstance().error("Error in  myfaces._SimpleXHRTransport.prototype._process",  e);
+            if(this._transportDataQueue.length > 0) {
+                this._transportDataQueue.shift();
+            }
+        }
+    }
+
+    /**
+     * data = {
+     *      context //jsf2 context
+     *      action  //String
+     *      viewstate //String
+     *      passthroughArguments //Map|String
+     *      //added by this method
+     *      transport //XHRObject
+     *  }
+     * called indirectly from ajaxContext, sourceForm.action, viewState, passThroughArguments
+     **/
+    myfaces._SimpleXHRTransport.prototype.send = function(/*Object*/data) {
+
+        var queueData = {};
+        queueData.context = data.context;
+        queueData.action = data.action;
+        queueData.viewstate = data.viewstate;
+        queueData.passthroughArguments = data.passthroughArguments;
+
+        this._transportDataQueue.push(queueData);
+        /*we initiate a send if none is in progress currently*/
+        
+        this._process(false);
+    };
+}
\ No newline at end of file

Added: myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_XHRConst.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_XHRConst.js?rev=742776&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_XHRConst.js (added)
+++ myfaces/core/branches/2_0_0/api/src/main/javascript/META-INF/resources/javax/faces/_util/_xhr/_XHRConst.js Mon Feb  9 22:51:43 2009
@@ -0,0 +1,39 @@
+/*
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use  myfaces._XHRConst file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+_reserveMyfaces();
+
+/**
+ * Various constants used by the xhr subsystem!
+ */
+if (!myfaces._JSF2Utils.exists(myfaces, "_XHRConst")) {
+    myfaces._XHRConst = {};
+    /*
+     *
+     * ready states according to www.w3.org
+     *
+     */
+
+    myfaces._XHRConst.READY_STATE_UNSENT = 0;
+    myfaces._XHRConst.READY_STATE_OPENED = 1;
+    myfaces._XHRConst.READY_STATE_HEADERS_RECEIVED = 2;
+    myfaces._XHRConst.READY_STATE_LOADING = 3;
+    myfaces._XHRConst.READY_STATE_DONE = 4;
+
+    /*header constants for the ajax request*/
+    myfaces._XHRConst.FACES_REQUEST = 'Faces-Request';
+    myfaces._XHRConst.PARTIAL_AJAX = 'partial/ajax';
+    myfaces._XHRConst.CONTENT_TYPE = "Content-Type";
+    myfaces._XHRConst.XFORM_ENCODED = "application/x-www-form-urlencoded";
+}