1 /* Licensed to the Apache Software Foundation (ASF) under one or more
  2  * contributor license agreements.  See the NOTICE file distributed with
  3  * this work for additional information regarding copyright ownership.
  4  * The ASF licenses this file to you under the Apache License, Version 2.0
  5  * (the "License"); you may not use this file except in compliance with
  6  * the License.  You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 /**
 18  * @class
 19  * @name Impl
 20  * @memberOf myfaces._impl.core
 21  * @description Implementation singleton which implements all interface method
 22  * defined by our jsf.js API
 23  * */
 24 myfaces._impl.core._Runtime.singletonExtendClass("myfaces._impl.core.Impl", Object,
 25 /**
 26  * @lends myfaces._impl.core.Impl.prototype
 27  */
 28 {
 29 
 30     //third option myfaces._impl.xhrCoreAjax which will be the new core impl for now
 31     _transport      : new (myfaces._impl.core._Runtime.getGlobalConfig("transport", myfaces._impl.xhrCore._Transports))(),
 32 
 33     /**
 34      * external event listener queue!
 35      */
 36     _evtListeners   : new (myfaces._impl.core._Runtime.getGlobalConfig("eventListenerQueue", myfaces._impl._util._ListenerQueue))(),
 37 
 38     /**
 39      * external error listener queue!
 40      */
 41     _errListeners   : new (myfaces._impl.core._Runtime.getGlobalConfig("errorListenerQueue", myfaces._impl._util._ListenerQueue))(),
 42 
 43     /*CONSTANTS*/
 44 
 45     /*internal identifiers for options*/
 46     IDENT_ALL:  "@all",
 47     IDENT_NONE: "@none",
 48     IDENT_THIS: "@this",
 49     IDENT_FORM: "@form",
 50 
 51     /*
 52      * [STATIC] constants
 53      */
 54 
 55     P_PARTIAL_SOURCE:   "javax.faces.source",
 56     P_VIEWSTATE:        "javax.faces.ViewState",
 57     P_AJAX:             "javax.faces.partial.ajax",
 58     P_EXECUTE:          "javax.faces.partial.execute",
 59     P_RENDER:           "javax.faces.partial.render",
 60     P_EVT:              "javax.faces.partial.event",
 61 
 62     /* message types */
 63     ERROR: "error",
 64     EVENT: "event",
 65 
 66     /* event emitting stages */
 67     BEGIN:      "begin",
 68     COMPLETE:   "complete",
 69     SUCCESS:    "success",
 70 
 71     /*ajax errors spec 14.4.2*/
 72     HTTPERROR:      "httpError",
 73     EMPTY_RESPONSE: "emptyResponse",
 74     MALFORMEDXML:   "malformedXML",
 75     SERVER_ERROR:   "serverError",
 76     CLIENT_ERROR:   "clientError",
 77     TIMEOUT_EVENT:  "timeout",
 78 
 79     _Lang:  myfaces._impl._util._Lang,
 80     _Dom:   myfaces._impl._util._Dom,
 81 
 82     /*error reporting threshold*/
 83     _threshold: "ERROR",
 84 
 85     /*blockfilter for the passthrough filtering, the attributes given here
 86      * will not be transmitted from the options into the passthrough*/
 87     _BLOCKFILTER: {onerror: true, onevent: true, render: true, execute: true, myfaces: true},
 88 
 89     /**
 90      * collect and encode data for a given form element (must be of type form)
 91      * find the javax.faces.ViewState element and encode its value as well!
 92      * return a concatenated string of the encoded values!
 93      *
 94      * @throws error in case of the given element not being of type form!
 95      * https://issues.apache.org/jira/browse/MYFACES-2110
 96      */
 97     getViewState : function(form) {
 98         /**
 99          *  typecheck assert!, we opt for strong typing here
100          *  because it makes it easier to detect bugs
101          */
102         if (form) {
103             form = this._Lang.byId(form);
104         }
105 
106         if (!form
107                 || !form.nodeName
108                 || form.nodeName.toLowerCase() != "form") {
109             throw new Error(this._Lang.getMessage("ERR_VIEWSTATE"));
110         }
111 
112         var ajaxUtils = new myfaces._impl.xhrCore._AjaxUtils(0);
113 
114         var ret = this._Lang.createFormDataDecorator([]);
115         ajaxUtils.encodeSubmittableFields(ret, null, null, null, form, null);
116         return ret.makeFinal();
117     },
118 
119     /**
120      * this function has to send the ajax requests
121      *
122      * following request conditions must be met:
123      * <ul>
124      *  <li> the request must be sent asynchronously! </li>
125      *  <li> the request must be a POST!!! request </li>
126      *  <li> the request url must be the form action attribute </li>
127      *  <li> all requests must be queued with a client side request queue to ensure the request ordering!</li>
128      * </ul>
129      *
130      * @param {String|Node} elem any dom element no matter being it html or jsf, from which the event is emitted
131      * @param {|Event|} event any javascript event supported by that object
132      * @param {|Object|} options  map of options being pushed into the ajax cycle
133      *
134      *
135      * TODO refactoring, the passthrgh handling is only for dragging in request parameters
136      * we should rewrite this part
137      *
138      *
139      * a) transformArguments out of the function
140      * b) passThrough handling with a map copy with a filter map block map
141      */
142     request : function(elem, event, options) {
143      
144         /*namespace remap for our local function context we mix the entire function namespace into
145          *a local function variable so that we do not have to write the entire namespace
146          *all the time
147          **/
148         var _Lang = this._Lang;
149         var _Dom =  this._Dom;
150         var getConfig = myfaces._impl.core._Runtime.getLocalOrGlobalConfig;
151 
152         /*assert if the onerror is set and once if it is set it must be of type function*/
153         _Lang.assertType(options.onerror, "function");
154         /*assert if the onevent is set and once if it is set it must be of type function*/
155         _Lang.assertType(options.onevent, "function");
156 
157         //options not set we define a default one with nothing
158         options = options || {};
159 
160 		/*preparations for jsf 2.2 windowid handling*/
161         //pass the window id into the options if not set already
162         if(!options.windowId) {
163             var windowId = _Dom.getWindowId();
164             (windowId) ? options["javax.faces.windowId"] = windowId: null;
165         } else {
166             options["javax.faces.windowId"] = options.windowId;
167             delete options.windowId;
168         }
169 
170         /**
171          * we cross reference statically hence the mapping here
172          * the entire mapping between the functions is stateless
173          */
174         //null definitely means no event passed down so we skip the ie specific checks
175         if ('undefined' == typeof event) {
176             event = window.event || null;
177         }
178 
179         elem = _Dom.byIdOrName(elem);
180         var elementId = _Dom.nodeIdOrName(elem);
181 
182         /*
183          * We make a copy of our options because
184          * we should not touch the incoming params!
185          */
186         var passThrgh = _Lang.mixMaps({}, options, true, this._BLOCKFILTER);
187 
188         if (event) {
189             passThrgh[this.P_EVT] = event.type;
190         }
191 
192         /**
193          * ajax pass through context with the source
194          * onevent and onerror
195          */
196         var context = {
197             source: elem,
198             onevent: options.onevent,
199             onerror: options.onerror,
200 
201             //TODO move the myfaces part into the _mfInternal part
202             myfaces: options.myfaces
203         };
204 
205         /**
206          * fetch the parent form
207          *
208          * note we also add an override possibility here
209          * so that people can use dummy forms and work
210          * with detached objects
211          */
212         var form = (options.myfaces && options.myfaces.form)?
213                 _Lang.byId(options.myfaces.form):
214                 this._getForm(elem, event);
215 
216         /**
217          * binding contract the javax.faces.source must be set
218          */
219         passThrgh[this.P_PARTIAL_SOURCE] = elementId;
220 
221         /**
222          * javax.faces.partial.ajax must be set to true
223          */
224         passThrgh[this.P_AJAX] = true;
225 
226         if (options.execute) {
227             /*the options must be a blank delimited list of strings*/
228             /*compliance with Mojarra which automatically adds @this to an execute
229              * the spec rev 2.0a however states, if none is issued nothing at all should be sent down
230              */
231             this._transformList(passThrgh, this.P_EXECUTE, options.execute + " @this", form, elementId);
232         } else {
233             passThrgh[this.P_EXECUTE] = elementId;
234         }
235 
236         if (options.render) {
237             this._transformList(passThrgh, this.P_RENDER, options.render, form, elementId);
238         }
239 
240         /**
241          * multiple transports upcoming jsf 2.1 feature currently allowed
242          * default (no value) xhrQueuedPost
243          *
244          * xhrQueuedPost
245          * xhrPost
246          * xhrGet
247          * xhrQueuedGet
248          * iframePost
249          * iframeQueuedPost
250          *
251          */
252         var transportType = this._getTransportType(context, passThrgh, form);
253 
254         //additional meta information to speed things up, note internal non jsf
255         //pass through options are stored under _mfInternal in the context
256         context._mfInternal = {};
257         var mfInternal = context._mfInternal;
258 
259         mfInternal["_mfSourceFormId"] =     form.id;
260         mfInternal["_mfSourceControlId"] =  elementId;
261         mfInternal["_mfTransportType"] =    transportType;
262 
263         //mojarra compatibility, mojarra is sending the form id as well
264         //this is not documented behavior but can be determined by running
265         //mojarra under blackbox conditions
266         //i assume it does the same as our formId_submit=1 so leaving it out
267         //wont hurt but for the sake of compatibility we are going to add it
268         passThrgh[form.id] = form.id;
269 
270         this._transport[transportType](elem, form, context, passThrgh);
271 
272     },
273 
274     /**
275      * fetches the form in an unprecise manner depending
276      * on an element or event target
277      *
278      * @param elem
279      * @param event
280      */
281     _getForm: function(elem, event) {
282         var _Dom =  this._Dom;
283         var _Lang = this._Lang;
284         var form =  _Dom.fuzzyFormDetection(elem);
285 
286         if (!form && event) {
287             //in case of no form is given we retry over the issuing event
288             form = _Dom.fuzzyFormDetection(_Lang.getEventTarget(event));
289             if (!form) {
290                 throw Error(_Lang.getMessage("ERR_FORM"));
291             }
292         } else if (!form) {
293             throw Error(_Lang.getMessage("ERR_FORM"));
294         }
295         return form;
296     },
297 
298     /**
299      * determines the transport type to be called
300      * for the ajax call
301      *
302      * @param context the context
303      * @param passThrgh  pass through values
304      * @param form the form which issues the request
305      */
306     _getTransportType: function(context, passThrgh, form) {
307         /**
308          * if execute or render exist
309          * we have to pass them down as a blank delimited string representation
310          * of an array of ids!
311          */
312         //for now we turn off the transport auto selection, to enable 2.0 backwards compatibility
313         //on protocol level, the file upload only can be turned on if the auto selection is set to true
314         var getConfig = myfaces._impl.core._Runtime.getLocalOrGlobalConfig;
315         var _Lang     = this._Lang;
316         var _Dom      = this._Dom;
317 
318         var transportAutoSelection = getConfig(context, "transportAutoSelection", false);
319         var isMultipart = (transportAutoSelection && _Dom.getAttribute(form, "enctype") == "multipart/form-data") ?
320                 _Dom.isMultipartCandidate(passThrgh[this.P_EXECUTE]) :
321                 false;
322 
323         /**
324          * multiple transports upcoming jsf 2.1 feature currently allowed
325          * default (no value) xhrQueuedPost
326          *
327          * xhrQueuedPost
328          * xhrPost
329          * xhrGet
330          * xhrQueuedGet
331          * iframePost
332          * iframeQueuedPost
333          *
334          */
335         var transportType = (!isMultipart) ?
336                 getConfig(context, "transportType", "xhrQueuedPost") :
337                 getConfig(context, "transportType", "multipartQueuedPost");
338         if (!this._transport[transportType]) {
339             //throw new Error("Transport type " + transportType + " does not exist");
340             throw new Error(_Lang.getMessage("ERR_TRANSPORT",null, transportType));
341         }
342         return transportType;
343 
344     },
345 
346     /**
347      * transforms the list to the expected one
348      * with the proper none all form and this handling
349      * (note we also could use a simple string replace but then
350      * we would have had double entries under some circumstances)
351      *
352      * @param passThrgh
353      * @param target
354      * @param srcStr
355      * @param form
356      * @param elementId
357      */
358     _transformList: function(passThrgh, target, srcStr, form, elementId) {
359         var _Lang = this._Lang;
360         //this is probably the fastest transformation method
361         //it uses an array and an index to position all elements correctly
362         //the offset variable is there to prevent 0 which results in a javascript
363         //false
364         var offset = 1,
365                 vals  = (srcStr) ? srcStr.split(/\s+/) : [],
366                 idIdx = (vals.length) ? _Lang.arrToMap(vals, offset) : {},
367 
368                 //helpers to improve speed and compression
369                 none    = idIdx[this.IDENT_NONE],
370                 all     = idIdx[this.IDENT_ALL],
371                 theThis = idIdx[this.IDENT_THIS],
372                 theForm = idIdx[this.IDENT_FORM];
373 
374         if (none) {
375             //in case of none only one value is returned
376             passThrgh[target] = this.IDENT_NONE;
377             return passThrgh;
378         }
379         if (all) {
380             //in case of all only one value is returned
381             passThrgh[target] = this.IDENT_ALL;
382             return passThrgh;
383         }
384 
385         if (theForm) {
386             //the form is replaced with the proper id but the other
387             //values are not touched
388             vals[theForm - offset] = form.id;
389         }
390         if (theThis && !idIdx[elementId]) {
391             //in case of this, the element id is set
392             vals[theThis - offset] = elementId;
393         }
394 
395         //the final list must be blank separated
396         passThrgh[target] = vals.join(" ");
397         return passThrgh;
398     },
399 
400     addOnError : function(/*function*/errorListener) {
401         /*error handling already done in the assert of the queue*/
402         this._errListeners.enqueue(errorListener);
403     },
404 
405     addOnEvent : function(/*function*/eventListener) {
406         /*error handling already done in the assert of the queue*/
407         this._evtListeners.enqueue(eventListener);
408     },
409 
410 
411 
412     /**
413      * implementation triggering the error chain
414      *
415      * @param {Object} request the request object which comes from the xhr cycle
416      * @param {Object} context (Map) the context object being pushed over the xhr cycle keeping additional metadata
417      * @param {String} name the error name
418      * @param {String} serverErrorName the server error name in case of a server error
419      * @param {String} serverErrorMessage the server error message in case of a server error
420      *
421      *  handles the errors, in case of an onError exists within the context the onError is called as local error handler
422      *  the registered error handlers in the queue receiv an error message to be dealt with
423      *  and if the projectStage is at development an alert box is displayed
424      *
425      *  note: we have additional functionality here, via the global config myfaces.config.defaultErrorOutput a function can be provided
426      *  which changes the default output behavior from alert to something else
427      *
428      *
429      */
430     sendError : function sendError(/*Object*/request, /*Object*/ context, /*String*/ name, /*String*/ serverErrorName, /*String*/ serverErrorMessage) {
431         var _Lang = myfaces._impl._util._Lang;
432         var UNKNOWN = _Lang.getMessage("UNKNOWN");
433 
434         var eventData = {};
435         //we keep this in a closure because we might reuse it for our serverErrorMessage
436         var malFormedMessage = function() {
437             return (name && name === myfaces._impl.core.Impl.MALFORMEDXML) ? _Lang.getMessage("ERR_MALFORMEDXML") : "";
438         };
439 
440 
441 
442         //by setting unknown values to unknown we can handle cases
443         //better where a simulated context is pushed into the system
444         eventData.type = this.ERROR;
445 
446         eventData.status            = name || UNKNOWN;
447         eventData.serverErrorName   = serverErrorName || UNKNOWN;
448         eventData.serverErrorMessage =  serverErrorMessage || UNKNOWN;
449 
450         try {
451             eventData.source        = context.source || UNKNOWN;
452             eventData.responseCode  = request.status || UNKNOWN;
453             eventData.responseText  = request.responseText  || UNKNOWN;
454             eventData.responseXML   = request.responseXML || UNKNOWN;
455         } catch (e) {
456             // silently ignore: user can find out by examining the event data
457         }
458 
459         /**/
460         if (context["onerror"]) {
461             context.onerror(eventData);
462         }
463 
464         /*now we serve the queue as well*/
465         this._errListeners.broadcastEvent(eventData);
466 
467         if (jsf.getProjectStage() === "Development" && this._errListeners.length() == 0) {
468             var defaultErrorOutput = myfaces._impl.core._Runtime.getGlobalConfig("defaultErrorOutput", alert);
469             var finalMessage = [];
470 
471             finalMessage.push((name) ? name : "");
472             finalMessage.push((serverErrorName) ? serverErrorName : "");
473             finalMessage.push((serverErrorMessage) ? serverErrorMessage : "");
474             finalMessage.push(malFormedMessage());
475             finalMessage.push("\n\n");
476             finalMessage.push( _Lang.getMessage("MSG_DEV_MODE"));
477             defaultErrorOutput(finalMessage.join(""));
478         }
479     },
480 
481     /**
482      * sends an event
483      */
484     sendEvent : function sendEvent(/*Object*/request, /*Object*/ context, /*event name*/ name) {
485         var _Lang = myfaces._impl._util._Lang;
486         var eventData = {};
487         var UNKNOWN = _Lang.getMessage("UNKNOWN");
488 
489         eventData.type = this.EVENT;
490 
491         eventData.status = name;
492         eventData.source = context.source;
493 
494 
495         if (name !== this.BEGIN) {
496 
497             try {
498                 //we bypass a problem with ie here, ie throws an exception if no status is given on the xhr object instead of just passing a value
499                 var getValue = function(value, key) {
500                     try {
501                         return value[key]
502                     } catch (e) {
503                         return UNKNOWN;
504                     }
505                 };
506 
507                 eventData.responseCode = getValue(request, "status");
508                 eventData.responseText = getValue(request, "responseText");
509                 eventData.responseXML  = getValue(request, "responseXML");
510 
511             } catch (e) {
512                 var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl);
513                 impl.sendError(request, context, this.CLIENT_ERROR, "ErrorRetrievingResponse",
514                         _Lang.getMessage("ERR_CONSTRUCT", e.toString()));
515                      
516                 //client errors are not swallowed
517                 throw e;
518             }
519 
520         }
521 
522         /**/
523         if (context.onevent) {
524             /*calling null to preserve the original scope*/
525             context.onevent.call(null, eventData);
526         }
527 
528         /*now we serve the queue as well*/
529         this._evtListeners.broadcastEvent(eventData);
530     },
531 
532     /**
533      * processes the ajax response if the ajax request completes successfully
534      * this is the case for non queued outside calls which are triggered by calling response
535      * themselves and hence the case according to the spec
536      *
537      * @param {Object} request (xhrRequest) the ajax request!
538      * @param {Object} context (Map) context map keeping context data not being passed down over
539      * the request boundary but kept on the client
540      */
541     response : function(request, context) {
542         this._transport.response(request, context);
543     },
544 
545     /**
546      * @return the project stage also emitted by the server:
547      * it cannot be cached and must be delivered over the server
548      * The value for it comes from the request parameter of the jsf.js script called "stage".
549      */
550     getProjectStage : function() {
551         /* run through all script tags and try to find the one that includes jsf.js */
552         var scriptTags = document.getElementsByTagName("script");
553         var getConfig  = myfaces._impl.core._Runtime.getGlobalConfig;
554         
555         for (var i = 0; i < scriptTags.length; i++) {
556             if (scriptTags[i].src.search(/\/javax\.faces\.resource\/jsf\.js.*ln=javax\.faces/) != -1) {
557                 var result = scriptTags[i].src.match(/stage=([^&;]*)/);
558                 if (result) {
559                     // we found stage=XXX
560                     // return only valid values of ProjectStage
561                     if (   result[1] == "Production"
562                         || result[1] == "Development"
563                         || result[1] == "SystemTest"
564                         || result[1] == "UnitTest") {
565                         return result[1];
566                     }
567                 }
568                 else {
569                     //we found the script, but there was no stage parameter -- Production
570                     //(we also add an override here for testing purposes, the default, however is Production)
571                     return getConfig("projectStage", "Production");
572                     //return "Production";
573                 }
574             }
575         }
576         /* we could not find anything valid --> return the default value */
577         return getConfig("projectStage", "Production");
578     },
579 
580     /**
581      * implementation of the external chain function
582      * moved into the impl
583      *
584      *  @param {Object} source the source which also becomes
585      * the scope for the calling function (unspecified side behavior)
586      * the spec states here that the source can be any arbitrary code block.
587      * Which means it either is a javascript function directly passed or a code block
588      * which has to be evaluated separately.
589      *
590      * After revisiting the code additional testing against components showed that
591      * the this parameter is only targeted at the component triggering the eval
592      * (event) if a string code block is passed. This is behavior we have to resemble
593      * in our function here as well, I guess.
594      *
595      * @param {Event} event the event object being passed down into the the chain as event origin
596      *   the spec is contradicting here, it on one hand defines event, and on the other
597      *   it says it is optional, after asking, it meant that event must be passed down
598      *   but can be undefined
599      */
600     chain : function(source, event) {
601         var len   = arguments.length;
602         var _Lang = this._Lang;
603 
604         //the spec is contradicting here, it on one hand defines event, and on the other
605         //it says it is optional, I have cleared this up now
606         //the spec meant the param must be passed down, but can be 'undefined'
607         if (len < 2) {
608             throw new Error(_Lang.getMessage("ERR_EV_OR_UNKNOWN"));
609         } else if (len < 3) {
610             if ('function' == typeof event || this._Lang.isString(event)) {
611 
612                 throw new Error(_Lang.getMessage("ERR_EVT_PASS"));
613             }
614             //nothing to be done here, move along
615             return true;
616         }
617         //now we fetch from what is given from the parameter list
618         //we cannot work with splice here in any performant way so we do it the hard way
619         //arguments only are give if not set to undefined even null values!
620 
621         //assertions source either null or set as dom element:
622 
623         if ('undefined' == typeof source) {
624             throw new Error(_Lang.getMessage("ERR_SOURCE_DEF_NULL"));
625             //allowed chain datatypes
626         } else if ('function' == typeof source) {
627             throw new Error(_Lang.getMessage("ERR_SOURCE_FUNC"));
628         }
629         if (this._Lang.isString(source)) {
630             throw new Error(_Lang.getMessage("ERR_SOURCE_NOSTR"));
631         }
632 
633         //assertion if event is a function or a string we already are in our function elements
634         //since event either is undefined, null or a valid event object
635 
636         if ('function' == typeof event || this._Lang.isString(event)) {
637             throw new Error(_Lang.getMessage("ERR_EV_OR_UNKNOWN"));
638         }
639 
640         for (var cnt = 2; cnt < len; cnt++) {
641             //we do not change the scope of the incoming functions
642             //but we reuse the argument array capabilities of apply
643             var ret;
644 
645             if ('function' == typeof arguments[cnt]) {
646                 ret = arguments[cnt].call(source, event);
647             } else {
648                 //either a function or a string can be passed in case of a string we have to wrap it into another function
649                 ret = new Function("event", arguments[cnt]).call(source, event);
650             }
651             //now if one function returns false in between we stop the execution of the cycle
652             //here, note we do a strong comparison here to avoid constructs like 'false' or null triggering
653             if (ret === false /*undefined check implicitly done here by using a strong compare*/) {
654                 return false;
655             }
656         }
657         return true;
658 
659     },
660 
661     /**
662      * error handler behavior called internally
663      * and only into the impl it takes care of the
664      * internal message transformation to a myfaces internal error
665      * and then uses the standard send error mechanisms
666      * also a double error logging prevention is done as well
667      *
668      * @param request the request currently being processed
669      * @param context the context affected by this error
670      * @param sourceClass the sourceclass throwing the error
671      * @param func the function throwing the error
672      * @param exception the exception being thrown
673      */
674      stdErrorHandler: function(request, context, sourceClass, func, exception) {
675 
676         var _Lang = myfaces._impl._util._Lang;
677         var exProcessed = _Lang.isExceptionProcessed(exception);
678         try {
679             //newer browsers do not allow to hold additional values on native objects like exceptions
680             //we hence capsule it into the request, which is gced automatically
681             //on ie as well, since the stdErrorHandler usually is called between requests
682             //this is a valid approach
683 
684             if (this._threshold == "ERROR" && !exProcessed) {
685                 this.sendError(request, context, this.CLIENT_ERROR, exception.name,
686                         "MyFaces ERROR:" + this._Lang.createErrorMsg(sourceClass, func, exception));
687             }
688         } finally {
689 
690             //we forward the exception, just in case so that the client
691             //will receive it in any way
692             try {
693                 if (!exProcessed) {
694                     _Lang.setExceptionProcessed(exception);
695                 }
696             } catch(e) {
697 
698             }
699             throw exception;
700         }
701     }
702 });    
703