Sorcerer's IsleCode QueryParam Scanner / files

     1component {
     2/*
     3	Copyright (c) 2009-2012, Sean Corfield, Ryan Cogswell
     4
     5	Licensed under the Apache License, Version 2.0 (the "License");
     6	you may not use this file except in compliance with the License.
     7	You may obtain a copy of the License at
     8
     9		http://www.apache.org/licenses/LICENSE-2.0
    10
    11	Unless required by applicable law or agreed to in writing, software
    12	distributed under the License is distributed on an "AS IS" BASIS,
    13	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14	See the License for the specific language governing permissions and
    15	limitations under the License.
    16*/
    17
    18	this.name = hash( getBaseTemplatePath() );
    19	if ( len( getContextRoot() ) ) {
    20		variables.cgiScriptName = replace( CGI.SCRIPT_NAME, getContextRoot(), '' );
    21		variables.cgiPathInfo = replace( CGI.PATH_INFO, getContextRoot(), '' );
    22	} else {
    23		variables.cgiScriptName = CGI.SCRIPT_NAME;
    24		variables.cgiPathInfo = CGI.PATH_INFO;
    25	}
    26	request._fw1 = {
    27        cgiScriptName = CGI.SCRIPT_NAME,
    28        cgiRequestMethod = CGI.REQUEST_METHOD,
    29        controllers = [ ],
    30        requestDefaultsInitialized = false,
    31        services = [ ],
    32        trace = [ ]
    33    };
    34	// do not rely on these, they are meant to be true magic...
    35    variables.magicApplicationSubsystem = '][';
    36	variables.magicApplicationController = '[]';
    37	variables.magicApplicationAction = '__';
    38	variables.magicBaseURL = '-[]-';
    39	
    40	public void function abortController() {
    41		request._fw1.abortController = true;
    42        frameworkTrace( 'abortController() called' );
    43		throw( type='FW1.AbortControllerException', message='abortController() called' );
    44	}
    45
    46	public boolean function actionSpecifiesSubsystem( string action ) {
    47
    48		if ( !usingSubsystems() ) {
    49			return false;
    50		}
    51		return listLen( action, variables.framework.subsystemDelimiter ) > 1 ||
    52			right( action, 1 ) == variables.framework.subsystemDelimiter;
    53	}
    54	
    55	public void function addRoute( any routes, string target, any methods = [ ], string statusCode = '' ) {
    56		if ( !isArray( routes ) ) routes = [ routes ];
    57		if ( !isArray( methods ) ) methods = [ methods ];
    58		param name="variables.framework.routes" default="#[ ]#"; 
    59		if ( len( statusCode ) ) target = statusCode & ':' & target;
    60		for ( var route in routes ) {
    61			if ( arrayLen( methods ) ) {
    62				for ( var method in methods ) {
    63					arrayAppend( variables.framework.routes, { '$#method##route#' = target } );
    64				}
    65			} else {
    66				arrayAppend( variables.framework.routes, { '#route#' = target } );
    67			}
    68		}
    69	}
    70	
    71	/*
    72	 *	buildURL() should be used from views to construct urls when using subsystems or
    73	 *	in order to provide a simpler transition to using subsystems in the future
    74	 */
    75	public string function buildURL( string action = '.', string path = variables.magicBaseURL, any queryString = '' ) {
    76		if ( action == '.' ) action = getFullyQualifiedAction();
    77		if ( path == variables.magicBaseURL ) path = getBaseURL();
    78		var omitIndex = false;
    79		if ( path == 'useSubsystemConfig' ) {
    80			var subsystemConfig = getSubsystemConfig( getSubsystem( action ) );
    81			if ( structKeyExists( subsystemConfig, 'baseURL' ) ) {
    82				path = subsystemConfig.baseURL;
    83			} else {
    84				path = getBaseURL();
    85			}
    86		}
    87		if ( path == 'useCgiScriptName' ) {
    88			path = request._fw1.cgiScriptName;
    89			if ( variables.framework.SESOmitIndex ) {
    90				path = getDirectoryFromPath( path );
    91				omitIndex = true;
    92			}
    93		} else if ( path == 'useRequestURI' ) {
    94			path = getPageContext().getRequest().getRequestURI();
    95			if ( variables.framework.SESOmitIndex ) {
    96				path = getDirectoryFromPath( path );
    97				omitIndex = true;
    98			}
    99		}
   100		// if queryString is a struct, massage it into a string
   101		if ( isStruct( queryString ) && structCount( queryString ) ) {
   102			var q = '';
   103			for( var key in queryString ) {
   104				if( isSimpleValue( queryString[key] ) ){
   105					q &= '#urlEncodedFormat( key )#=#urlEncodedFormat( queryString[ key ] )#&';
   106				}
   107			}
   108			queryString = q;
   109		}
   110		else if ( !isSimpleValue( queryString ) ) {
   111			queryString = '';
   112		}
   113		if ( queryString == '' ) {
   114			// extract query string from action section:
   115			var q = find( '?', action );
   116			var a = find( '##', action );
   117			if ( q > 0 ) {
   118                if ( q < len( action ) ) {
   119				    queryString = right( action, len( action ) - q );
   120                }
   121				if ( q == 1 ) {
   122					action = '';
   123				} else {
   124					action = left( action, q - 1 );
   125				}
   126			} else if ( a > 0 ) {
   127				queryString = right( action, len( action ) - a + 1 );
   128				if ( a == 1 ) {
   129					action = '';
   130				} else {
   131					action = left( action, a - 1 );
   132				}
   133			}
   134		}
   135		var cosmeticAction = getFullyQualifiedAction( action );
   136		var isHomeAction = cosmeticAction == getFullyQualifiedAction( variables.framework.home );
   137		var isDefaultItem = getItem( cosmeticAction ) == variables.framework.defaultItem;
   138		
   139		var initialDelim = '?';
   140		var varDelim = '&';
   141		var equalDelim = '=';
   142		var basePath = '';
   143		var extraArgs = '';
   144		var queryPart = '';
   145		var anchor = '';
   146		var ses = false;
   147		if ( find( '?', path ) > 0 ) {
   148			if ( right( path, 1 ) == '?' || right( path, 1 ) == '&' ) {
   149				initialDelim = '';
   150			} else {
   151				initialDelim = '&';
   152			}
   153		} else if ( structKeyExists( request._fw1, 'generateSES' ) && request._fw1.generateSES ) {
   154			if ( omitIndex ) {
   155				initialDelim = '';
   156			} else {
   157				initialDelim = '/';
   158			}
   159			varDelim = '/';
   160			equalDelim = '/';
   161			ses = true;
   162		}
   163		var curDelim = varDelim;
   164		
   165		if ( usingSubsystems() && getSubsystem( cosmeticAction ) == variables.framework.defaultSubsystem ) {
   166			cosmeticAction = getSectionAndItem( cosmeticAction );
   167		}
   168		
   169		if ( len( queryString ) ) {
   170			// extract query part and anchor from query string:
   171			q = find( '?', queryString );
   172			if ( q > 0 ) {
   173				queryPart = right( queryString, len( queryString ) - q );
   174				if ( q > 1 ) {
   175					extraArgs = left( queryString, q - 1 );
   176				}
   177				a = find( '##', queryPart );
   178				if ( a > 0 ) {
   179					anchor = right( queryPart, len( queryPart ) - a );
   180					if ( a == 1 ) {
   181						queryPart = '';
   182					} else {
   183						queryPart = left( queryPart, a - 1 );
   184					}
   185				}
   186			} else {
   187				extraArgs = queryString;
   188				a = find( '##', extraArgs );
   189				if ( a > 0 ) {
   190					anchor = right( extraArgs, len( extraArgs ) - a );
   191					if ( a == 1 ) {
   192						extraArgs = '';
   193					} else {
   194						extraArgs = left( extraArgs, a - 1 );
   195					}
   196				}
   197			}
   198			if ( ses ) {
   199				extraArgs = listChangeDelims( extraArgs, '/', '&=' );
   200			}
   201		}
   202		
   203		if ( ses ) {
   204			if ( isHomeAction && extraArgs == '' ) {
   205				basePath = path;
   206			} else if ( isDefaultItem && extraArgs == '' ) {
   207				basePath = path & initialDelim & listFirst( cosmeticAction, '.' );
   208			} else {
   209				basePath = path & initialDelim & replace( cosmeticAction, '.', '/' );
   210			}
   211		} else {
   212			if ( isHomeAction ) {
   213				basePath = path;
   214				curDelim = '?';
   215			} else if ( isDefaultItem ) {
   216				basePath = path & initialDelim & variables.framework.action & equalDelim & listFirst( cosmeticAction, '.' );
   217			} else {
   218				basePath = path & initialDelim & variables.framework.action & equalDelim & cosmeticAction;
   219			}
   220		}
   221		
   222		if ( extraArgs != '' ) {
   223			basePath = basePath & curDelim & extraArgs;
   224			curDelim = varDelim;
   225		}
   226		if ( queryPart != '' ) {
   227			if ( ses ) {
   228				basePath = basePath & '?' & queryPart;
   229			} else {
   230				basePath = basePath & curDelim & queryPart;
   231			}
   232		}
   233		if ( anchor != '' ) {
   234			basePath = basePath & '##' & anchor;
   235		}
   236		return basePath;
   237	}
   238
   239	/*
   240	 * call this from your Application.cfc methods to queue up additional controller
   241	 * method calls at the start of the request
   242	 */
   243	public void function controller( string action ) {
   244		var subsystem = getSubsystem( action );
   245		var section = getSection( action );
   246		var item = getItem( action );
   247		var tuple = { };
   248
   249		if ( structKeyExists( request._fw1, 'controllerExecutionStarted' ) ) {
   250			raiseException( type='FW1.controllerExecutionStarted', message="Controller '#action#' may not be added at this point.",
   251				detail='The controller execution phase has already started. Controllers may not be added by other controller methods.' );
   252		}
   253
   254		tuple.controller = getController( section = section, subsystem = subsystem );
   255		tuple.key = subsystem & variables.framework.subsystemDelimiter & section;
   256        tuple.subsystem = subsystem;
   257        tuple.section = section;
   258		tuple.item = item;
   259
   260		if ( structKeyExists( tuple, 'controller' ) && isObject( tuple.controller ) && !isNull(tuple.controller)) {
   261            frameworkTrace( 'queuing controller', subsystem, section, item );
   262			arrayAppend( request._fw1.controllers, tuple );
   263		}
   264	}
   265
   266	/*
   267	 * can be overridden to customize how views and layouts are found - can be
   268	 * used to provide skinning / common views / layouts etc
   269	 */
   270	public string function customizeViewOrLayoutPath( struct pathInfo, string type, string fullPath ) {
   271		// fullPath is: '#pathInfo.base##type#s/#pathInfo.path#.cfm'
   272		return fullPath;
   273	}
   274
   275	/*
   276	 * return the action URL variable name - allows applications to build URLs
   277	 */
   278	public string function getAction() {
   279		return variables.framework.action;
   280	}
   281	
   282	/*
   283	 * returns the base URL for redirects and links etc
   284	 * can be overridden if you need to modify this per-request
   285	 */
   286	public string function getBaseURL() {
   287		return variables.framework.baseURL;
   288	}
   289	
   290	/*
   291	 *	returns whatever the framework has been told is a bean factory
   292	 *	this will return a subsystem-specific bean factory if one
   293	 *	exists for the current request's subsystem (or for the specified subsystem
   294	 *	if passed in)
   295	 */
   296	public any function getBeanFactory( string subsystem = '' ) {
   297		if ( len( subsystem ) > 0 ) {
   298			if ( hasSubsystemBeanFactory( subsystem ) ) {
   299				return getSubsystemBeanFactory( subsystem );
   300			}
   301			return getDefaultBeanFactory();
   302		}
   303		if ( !usingSubsystems() ) {
   304			return getDefaultBeanFactory();
   305		}
   306		if ( structKeyExists( request, 'subsystem' ) && len( request.subsystem ) > 0 ) {
   307			return getBeanFactory( request.subsystem );
   308		}
   309		if ( len( variables.framework.defaultSubsystem ) > 0 ) {
   310			return getBeanFactory( variables.framework.defaultSubsystem );
   311		}
   312		return getDefaultBeanFactory();
   313	}
   314	
   315	/*
   316	 * return the framework configuration
   317	 */
   318	public struct function getConfig()
   319	{
   320		// return a copy to make it read only from outside the framework:
   321		return structCopy( framework );
   322	}
   323
   324	/*
   325	 * returns the bean factory set via setBeanFactory
   326	 */
   327	public any function getDefaultBeanFactory() {
   328		return application[ variables.framework.applicationKey ].factory;
   329	}
   330
   331	/*
   332	 * returns the name of the default subsystem
   333	 */
   334	public string function getDefaultSubsystem() {
   335
   336		if ( !usingSubsystems() ) {
   337			return '';
   338		}
   339
   340		if ( structKeyExists( request, 'subsystem' ) ) {
   341			return request.subsystem;
   342		}
   343
   344		if ( variables.framework.defaultSubsystem == '' ) {
   345			raiseException( type='FW1.subsystemNotSpecified', message='No subsystem specified and no default configured.',
   346					detail='When using subsystems, every request should specify a subsystem or variables.framework.defaultSubsystem should be configured.' );
   347		}
   348
   349		return variables.framework.defaultSubsystem;
   350
   351	}
   352	
   353    /*
   354     * override this to provide your environment selector
   355     */
   356    public string function getEnvironment() {
   357        return '';
   358    }
   359	
   360	/*
   361	 * return an action with all applicable parts (subsystem, section, and item) specified
   362	 * using defaults from the configuration or request where appropriate
   363	 */
   364	public string function getFullyQualifiedAction( string action = request.action ) {
   365		if ( usingSubsystems() ) {
   366			return getSubsystem( action ) & variables.framework.subsystemDelimiter & getSectionAndItem( action );
   367		}
   368
   369		return getSectionAndItem( action );
   370	}
   371
   372    /*
   373     * return the local hostname of the server
   374     */
   375    public string function getHostname() {
   376        return createObject( 'java', 'java.net.InetAddress' ).getLocalHost().getHostName();
   377    }
   378	
   379	/*
   380	 * return the item part of the action
   381	 */
   382	public string function getItem( string action = request.action ) {
   383		return listLast( getSectionAndItem( action ), '.' );
   384	}
   385	
   386	
   387	/*
   388	 * return the current route (if any)
   389	 */
   390	public string function getRoute() {
   391		return structKeyExists( request._fw1, 'route' ) ? request._fw1.route : '';
   392	}
   393	
   394	
   395	/*
   396	 * return the configured routes
   397	 */
   398	public array function getRoutes() {
   399		return variables.framework.routes;
   400	}
   401	
   402	/*
   403	 * return the resource route templates
   404	 */
   405	public array function getResourceRouteTemplates() {
   406		return variables.framework.resourceRouteTemplates;
   407	}
   408	
   409	/*
   410	 * return the section part of the action
   411	 */
   412	public string function getSection( string action = request.action ) {
   413		return listFirst( getSectionAndItem( action ), '.' );
   414	}
   415	
   416	
   417	/*
   418	 * return the action without the subsystem
   419	 */
   420	public string function getSectionAndItem( string action = request.action ) {
   421		var sectionAndItem = '';
   422
   423		if ( usingSubsystems() && actionSpecifiesSubsystem( action ) ) {
   424			if ( listLen( action, variables.framework.subsystemDelimiter ) > 1 ) {
   425				sectionAndItem = listLast( action, variables.framework.subsystemDelimiter );
   426			}
   427		} else {
   428			sectionAndItem = action;
   429		}
   430
   431		if ( len( sectionAndItem ) == 0 ) {
   432			sectionAndItem = variables.framework.defaultSection & '.' & variables.framework.defaultItem;
   433		} else if ( listLen( sectionAndItem, '.' ) == 1 ) {
   434			if ( left( sectionAndItem, 1 ) == '.' ) {
   435				if ( structKeyExists( request, 'section' ) ) {
   436					sectionAndItem = request.section & '.' & listLast( sectionAndItem, '.' );
   437				} else {
   438					sectionAndItem = variables.framework.defaultSection & '.' & listLast( sectionAndItem, '.' );
   439				}
   440			} else {
   441				sectionAndItem = listFirst( sectionAndItem, '.' ) & '.' & variables.framework.defaultItem;
   442			}
   443		} else {
   444			sectionAndItem = listFirst( sectionAndItem, '.' ) & '.' & listLast( sectionAndItem, '.' );
   445		}
   446
   447		return sectionAndItem;
   448	}
   449	
   450	
   451	/*
   452	 * return the default service result key
   453	 * override this if you want the default service result to be
   454	 * stored under a different request context key, based on the
   455	 * requested action, e.g., return getSection( action );
   456	 */
   457	public string function getServiceKey( action ) {
   458		return 'data';
   459	}
   460	
   461	/*
   462	 * return the subsystem part of the action
   463	 */
   464	public string function getSubsystem( string action = request.action ) {
   465		if ( actionSpecifiesSubsystem( action ) ) {
   466			return listFirst( action, variables.framework.subsystemDelimiter );
   467		}
   468		return getDefaultSubsystem();
   469	}
   470
   471    /*
   472     * return the base directory for the current request's subsystem
   473     */
   474    public string function getSubsystemBase() {
   475        return request.subsystemBase;
   476    }
   477	
   478	/*
   479	 * return the (optional) configuration for a subsystem
   480	 */
   481	public struct function getSubsystemConfig( string subsystem ) {
   482		if ( structKeyExists( variables.framework.subsystems, subsystem ) ) {
   483			// return a copy to make it read only from outside the framework:
   484			return structCopy( variables.framework.subsystems[ subsystem ] );
   485		}
   486		return { };
   487	}
   488
   489	/*
   490	 * returns the bean factory set via setSubsystemBeanFactory
   491	 * same effect as getBeanFactory when not using subsystems
   492	 */
   493	public any function getSubsystemBeanFactory( string subsystem ) {
   494
   495		setupSubsystemWrapper( subsystem );
   496
   497		return application[ variables.framework.applicationKey ].subsystemFactories[ subsystem ];
   498
   499	}
   500	
   501	/*
   502	 * returns true iff a call to getBeanFactory() will successfully return a bean factory
   503	 * previously set via setBeanFactory or setSubsystemBeanFactory
   504	 */
   505	public boolean function hasBeanFactory() {
   506
   507		if ( hasDefaultBeanFactory() ) {
   508			return true;
   509		}
   510
   511		if ( !usingSubsystems() ) {
   512			return false;
   513		}
   514
   515		if ( structKeyExists( request, 'subsystem' ) ) {
   516			return hasSubsystemBeanFactory(request.subsystem);
   517		}
   518
   519		if ( len(variables.framework.defaultSubsystem) > 0 ) {
   520			return hasSubsystemBeanFactory(variables.framework.defaultSubsystem);
   521		}
   522
   523		return false;
   524
   525	}
   526
   527	/*
   528	 * returns true iff the framework has been told about a bean factory via setBeanFactory
   529	 */
   530	public boolean function hasDefaultBeanFactory() {
   531		return structKeyExists( application[ variables.framework.applicationKey ], 'factory' );
   532	}
   533
   534	/*
   535	 * returns true if a subsystem specific bean factory has been set
   536	 */
   537	public boolean function hasSubsystemBeanFactory( string subsystem ) {
   538
   539		ensureNewFrameworkStructsExist();
   540
   541		return structKeyExists( application[ variables.framework.applicationKey ].subsystemFactories, subsystem );
   542
   543	}
   544
   545	/*
   546	 * layout() may be invoked inside layouts
   547	 * returns the UI generated by the named layout and body
   548	 */
   549	public string function layout( string path, string body ) {
   550		var layoutPath = parseViewOrLayoutPath( path, 'layout' );
   551        frameworkTrace( 'layout( #path# ) called - rendering #layoutPath#' );
   552		return internalLayout( layoutPath, body );
   553	}
   554
   555	/*
   556	 * it is better to set up your application configuration in
   557	 * your setupApplication() method since that is called on a
   558	 * framework reload
   559	 * if you do override onApplicationStart(), you must call
   560	 * super.onApplicationStart() first
   561	 */
   562	public any function onApplicationStart() {
   563		setupFrameworkDefaults();
   564		setupRequestDefaults();
   565		setupApplicationWrapper();
   566	}
   567
   568	/*
   569	 * can be overridden, calling super.onError(exception,event) is optional
   570	 * depending on what error handling behavior you want
   571	 * note: you need to rename / disable onError() on OpenBD since it does
   572	 * not seem to be passed exception or event correctly when something fails
   573	 * in the code...
   574	 */
   575	public void function onError( any exception, string event ) {
   576		try {
   577		    if ( !structKeyExists( variables, 'framework' ) ||
   578                 !structKeyExists( variables.framework, 'version' ) ) {
   579		      // error occurred before framework was initialized
   580		      failure( exception, event, false, true );
   581		      return;
   582		    }
   583		    
   584			// record details of the exception:
   585			if ( structKeyExists( request, 'action' ) ) {
   586				request.failedAction = request.action;
   587			}
   588			request.exception = exception;
   589			request.event = event;
   590			// reset lifecycle flags:
   591            structDelete( request, 'layout' );
   592			structDelete( request._fw1, 'controllerExecutionComplete' );
   593			structDelete( request._fw1, 'controllerExecutionStarted' );
   594			structDelete( request._fw1, 'serviceExecutionComplete' );
   595			structDelete( request._fw1, 'overrideViewAction' );
   596            if ( structKeyExists( request._fw1, 'renderData' ) ) {
   597                // need to reset the content type as well!
   598                try {
   599                    getPageContext().getResponse().setContentType( 'text/html; charset=utf-8' );
   600                } catch ( any e ) {
   601                    // but ignore any exceptions
   602                }
   603                structDelete( request._fw1, 'renderData' );
   604            }
   605			// setup the new controller action, based on the error action:
   606			request._fw1.controllers = [ ];
   607            // reset services for this new action:
   608            request._fw1.services = [ ];
   609			
   610			if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'error' ) ) {
   611				request.action = variables.framework.error;
   612			} else {
   613				// this is an edge case so we don't bother with subsystems etc
   614				// (because if part of the framework defaults are not present,
   615				// we'd have to do a lot of conditional logic here!)
   616				request.action = 'main.error';
   617			}
   618			// ensure request.context is available
   619			if ( !structKeyExists( request, 'context' ) ) {
   620			    request.context = { };
   621			}
   622			if ( !structKeyExists( request, 'base' ) ) {
   623				if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'base' ) ) {
   624					request.base = variables.framework.base;
   625				} else {
   626					request.base = '';
   627				}
   628			}
   629			if ( !structKeyExists( request, 'cfcbase' ) ) {
   630				if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'cfcbase' ) ) {
   631					request.cfcbase = variables.framework.cfcbase;
   632				} else {
   633					request.cfcbase = '';
   634				}
   635			}
   636			frameworkTrace( 'onError( #exception.message#, #event# ) called' );
   637			setupRequestWrapper( false );
   638			onRequest( '' );
   639            frameworkTraceRender();
   640		} catch ( any e ) {
   641			failure( e, 'onError' );
   642			failure( exception, event, true );
   643            frameworkTraceRender();
   644		}
   645
   646	}
   647
   648	/*
   649	 * this can be overridden if you want to change the behavior when
   650	 * FW/1 cannot find a matching view
   651	 */
   652	public string function onMissingView( struct rc ) {
   653		// unable to find a matching view - fail with a nice exception
   654		viewNotFound();
   655		// if we got here, we would return the string to be rendered
   656		// but viewNotFound() throws an exception...
   657        // for example, return view( 'main.missing' );
   658	}
   659
   660	/*
   661	 * This can be overridden if you want to change the behavior when
   662	 * FW/1 encounters an error when trying to populate bean properties
   663	 * using all of the keys in the request context (rather than a
   664	 * specific list of keys).  By default FW/1 silently ignores these errors.
   665	 * Available in the arguments are the bean cfc and the property that was
   666	 * being set when the error occurred as well as the request context structure.
   667	 * You can also reference the cfcatch variable for details about the error.
   668	 */
   669	public void function onPopulateError( any cfc, string property, struct rc ) {
   670	}
   671
   672	/*
   673	 * not intended to be overridden, automatically deleted for CFC requests
   674	 */
   675	public any function onRequest( string targetPath ) {
   676
   677		var out = 0;
   678		var i = 0;
   679		var tuple = 0;
   680		var _data_fw1 = 0;
   681		var once = { };
   682		var n = 0;
   683
   684		request._fw1.controllerExecutionStarted = true;
   685		try {
   686			n = arrayLen( request._fw1.controllers );
   687			for ( i = 1; i <= n; i = i + 1 ) {
   688				tuple = request._fw1.controllers[ i ];
   689				// run before once per controller:
   690				if ( !structKeyExists( once, tuple.key ) ) {
   691					once[ tuple.key ] = i;
   692					doController( tuple, 'before', 'before' );
   693					if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   694				}
   695				doController( tuple, 'start' & tuple.item, 'start' );
   696				if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   697				doController( tuple, tuple.item, 'item' );
   698				if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   699			}
   700			n = arrayLen( request._fw1.services );
   701			for ( i = 1; i <= n; i = i + 1 ) {
   702				tuple = request._fw1.services[ i ];
   703				if ( tuple.key == '' ) {
   704					// throw the result away:
   705					doService( tuple, tuple.item, tuple.args, tuple.enforceExistence );
   706					if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   707				} else {
   708					_data_fw1 = doService( tuple, tuple.item, tuple.args, tuple.enforceExistence );
   709					if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   710					if ( isDefined('_data_fw1') ) {
   711                        frameworkTrace( 'store service result in rc.#tuple.key#', tuple.subsystem, tuple.section, tuple.item );
   712						request.context[ tuple.key ] = _data_fw1;
   713					} else {
   714                        frameworkTrace( 'service returned no result for rc.#tuple.key#', tuple.subsystem, tuple.section, tuple.item );
   715                    }
   716				}
   717			}
   718			request._fw1.serviceExecutionComplete = true;
   719			n = arrayLen( request._fw1.controllers );
   720			for ( i = n; i >= 1; i = i - 1 ) {
   721				tuple = request._fw1.controllers[ i ];
   722				doController( tuple, 'end' & tuple.item, 'end' );
   723				if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   724				if ( once[ tuple.key ] eq i ) {
   725					doController( tuple, 'after', 'after' );
   726					if ( structKeyExists( request._fw1, 'abortController' ) ) abortController();
   727				}
   728			}
   729		} catch ( FW1.AbortControllerException e ) {
   730			request._fw1.serviceExecutionComplete = true;
   731		}
   732		request._fw1.controllerExecutionComplete = true;
   733
   734        if ( structKeyExists( request._fw1, 'renderData' ) ) {
   735            out = renderDataWithContentType();
   736        } else {
   737		    buildViewQueue();
   738            frameworkTrace( 'setupView() called' );
   739		    setupView();
   740		    if ( structKeyExists(request._fw1, 'view') ) {
   741                frameworkTrace( 'rendering #request._fw1.view#' );
   742			    out = internalView( request._fw1.view );
   743		    } else {
   744                frameworkTrace( 'onMissingView() called' );
   745			    out = onMissingView( request.context );
   746		    }
   747            
   748            buildLayoutQueue();
   749		    for ( i = 1; i <= arrayLen(request._fw1.layouts); i = i + 1 ) {
   750			    if ( structKeyExists(request, 'layout') && !request.layout ) {
   751                    frameworkTrace( 'aborting layout rendering' );
   752				    break;
   753			    }
   754                frameworkTrace( 'rendering #request._fw1.layouts[i]#' );
   755			    out = internalLayout( request._fw1.layouts[i], out );
   756		    }
   757        }
   758		writeOutput( out );
   759		setupResponseWrapper();
   760	}
   761
   762    /*
   763     * if you override onRequestEnd(), call super.onRequestEnd() if you
   764     * want tracing functionality to continue working
   765     */
   766    public any function onRequestEnd() {
   767        frameworkTraceRender();
   768    }
   769
   770	/*
   771	 * it is better to set up your request configuration in
   772	 * your setupRequest() method
   773	 * if you do override onRequestStart(), you must call
   774	 * super.onRequestStart() first
   775	 */
   776	public any function onRequestStart( string targetPath ) {
   777		setupFrameworkDefaults();
   778		setupRequestDefaults();
   779
   780		if ( !isFrameworkInitialized() || isFrameworkReloadRequest() ) {
   781			setupApplicationWrapper();
   782		}
   783
   784		restoreFlashContext();
   785		// ensure flash context cannot override request action:
   786		request.context[variables.framework.action] = request.action;
   787
   788		// allow configured extensions and paths to pass through to the requested template.
   789		// NOTE: for unhandledPaths, we make the list into an escaped regular expression so we match on subdirectories.  
   790		// Meaning /myexcludepath will match '/myexcludepath' and all subdirectories  
   791		if ( listFindNoCase( variables.framework.unhandledExtensions, listLast( targetPath, '.' ) ) || 
   792				REFindNoCase( '^(' & variables.framework.unhandledPathRegex & ')', targetPath ) ) {		
   793			structDelete(this, 'onRequest');
   794			structDelete(variables, 'onRequest');
   795			structDelete(this, 'onRequestEnd');
   796			structDelete(variables, 'onRequestEnd');			
   797            if ( !variables.framework.unhandledErrorCaught ) {
   798			    structDelete(this, 'onError');
   799			    structDelete(variables, 'onError');
   800            }
   801		} else {
   802			setupRequestWrapper( true );
   803		}
   804	}
   805
   806	/*
   807	 * it is better to set up your session configuration in
   808	 * your setupSession() method
   809	 * if you do override onSessionStart(), you must call
   810	 * super.onSessionStart() first
   811	 */
   812	public any function onSessionStart() {
   813		setupFrameworkDefaults();
   814		setupRequestDefaults();
   815		setupSessionWrapper();
   816	}
   817	
   818	// populate() may be invoked inside controllers
   819	public any function populate( any cfc, string keys = '', boolean trustKeys = false, boolean trim = false, deep = false ) {
   820		if ( keys == '' ) {
   821			if ( trustKeys ) {
   822				// assume everything in the request context can be set into the CFC
   823				for ( var property in request.context ) {
   824					try {
   825						var args = { };
   826						args[ property ] = request.context[ property ];
   827						if ( trim && isSimpleValue( args[ property ] ) ) args[ property ] = trim( args[ property ] );
   828						// cfc[ 'set'&property ]( argumentCollection = args ); // ugh! no portable script version of this?!?!						
   829						setProperty( cfc, property, args );
   830					} catch ( any e ) {
   831						onPopulateError( cfc, property, request.context );
   832					}
   833				}
   834			} else {
   835				var setters = findImplicitAndExplicitSetters( cfc );
   836				for ( var property in setters ) {
   837					if ( structKeyExists( request.context, property ) ) {
   838						var args = { };
   839						args[ property ] = request.context[ property ];
   840						if ( trim && isSimpleValue( args[ property ] ) ) args[ property ] = trim( args[ property ] );
   841						// cfc[ 'set'&property ]( argumentCollection = args ); // ugh! no portable script version of this?!?!
   842						setProperty( cfc, property, args );
   843					} else if ( deep && structKeyExists( cfc, 'get' & property ) ) {
   844						//look for a context property that starts with the property
   845						for ( var key in request.context ) {
   846							if ( listFindNoCase( key, property, '.') ) {
   847								try {
   848									setProperty( cfc, key, { '#key#' = request.context[ key ] } );
   849								} catch ( any e ) {
   850									onPopulateError( cfc, key, request.context);
   851								}
   852							}
   853						}
   854					}
   855				}
   856			}
   857		} else {
   858			var setters = findImplicitAndExplicitSetters( cfc );
   859			var keyArray = listToArray( keys );
   860			for ( var property in keyArray ) {
   861				var trimProperty = trim( property );
   862				if ( structKeyExists( setters, trimProperty ) || trustKeys ) {
   863					if ( structKeyExists( request.context, trimProperty ) ) {
   864						var args = { };
   865						args[ trimProperty ] = request.context[ trimProperty ];
   866						if ( trim && isSimpleValue( args[ trimProperty ] ) ) args[ trimProperty ] = trim( args[ trimProperty ] );
   867						// cfc[ 'set'&trimproperty ]( argumentCollection = args ); // ugh! no portable script version of this?!?!
   868						setProperty( cfc, trimProperty, args );
   869					}
   870				} else if ( deep ) {
   871					if ( listLen( trimProperty, '.' ) > 1 ) {
   872						var prop = listFirst( trimProperty, '.' );
   873						if ( structKeyExists( cfc, 'get' & prop ) ) {
   874                            setProperty( cfc, trimProperty, { '#trimProperty#' = request.context[ trimProperty ] } );
   875                        }
   876					}
   877				}
   878			}
   879		}
   880		return cfc;
   881	}
   882
   883	private void function setProperty( struct cfc, string property, struct args ) {
   884		if ( listLen( property, '.' ) > 1 ) {
   885			var firstObjName = listFirst( property, '.' );
   886			var newProperty = listRest( property,  '.' );
   887
   888			args[ newProperty ] = args[ property ];
   889			structDelete( args, property );
   890
   891			if ( structKeyExists( cfc , 'get' & firstObjName ) ) {
   892				var obj = getProperty( cfc, firstObjName );
   893				if ( !isNull( obj ) ) setProperty( obj, newProperty, args );
   894			}
   895		} else {
   896			evaluate( 'cfc.set#property#( argumentCollection = args )' );
   897		}
   898	}
   899	
   900	private any function getProperty( struct cfc, string property ) {
   901		if ( structKeyExists( cfc, 'get#property#' ) ) return evaluate( 'cfc.get#property#()' );
   902	}
   903
   904	// call from your controller to redirect to a clean URL based on an action, pushing data to flash scope if necessary:
   905	public void function redirect( string action, string preserve = 'none', string append = 'none', string path = variables.magicBaseURL, string queryString = '', string statusCode = '302' ) {
   906		if ( path == variables.magicBaseURL ) path = getBaseURL();
   907		var preserveKey = '';
   908		if ( preserve != 'none' ) {
   909			preserveKey = saveFlashContext( preserve );
   910		}
   911		var baseQueryString = '';
   912		if ( append != 'none' ) {
   913			if ( append == 'all' ) {
   914				for ( var key in request.context ) {
   915					if ( isSimpleValue( request.context[ key ] ) ) {
   916						baseQueryString = listAppend( baseQueryString, key & '=' & urlEncodedFormat( request.context[ key ] ), '&' );
   917					}
   918				}
   919			} else {
   920				var keys = listToArray( append );
   921				for ( var key in keys ) {
   922					if ( structKeyExists( request.context, key ) && isSimpleValue( request.context[ key ] ) ) {
   923						baseQueryString = listAppend( baseQueryString, key & '=' & urlEncodedFormat( request.context[ key ] ), '&' );
   924					}
   925				}
   926				
   927			}
   928		}
   929		
   930		if ( baseQueryString != '' ) {
   931			if ( queryString != '' ) {
   932				if ( left( queryString, 1 ) == '?' || left( queryString, 1 ) == '##' ) {
   933					baseQueryString = baseQueryString & queryString;
   934				} else {
   935					baseQueryString = baseQueryString & '&' & queryString;
   936				}
   937			}
   938		} else {
   939			baseQueryString = queryString;
   940		}
   941		
   942		var targetURL = buildURL( action, path, baseQueryString );
   943		if ( preserveKey != '' && variables.framework.maxNumContextsPreserved > 1 ) {
   944			if ( find( '?', targetURL ) ) {
   945				preserveKey = '&#variables.framework.preserveKeyURLKey#=#preserveKey#';
   946			} else {
   947				preserveKey = '?#variables.framework.preserveKeyURLKey#=#preserveKey#';
   948			}
   949			if ( find( '##', targetURL ) ) {
   950				targetURL = listFirst( targetURL, '##' ) & preserveKey & '##' & listRest( targetURL, '##' );
   951			} else {
   952				targetURL = targetURL & preserveKey;
   953			}
   954		}
   955		setupResponseWrapper();
   956        if ( variables.framework.trace ) {
   957            frameworkTrace( 'redirecting to #targetURL# (#statusCode#)' );
   958            try {
   959                session._fw1_trace = request._fw1.trace;
   960            } catch ( any _ ) {
   961                // ignore exception if session is not enabled
   962            }
   963        }
   964		location( targetURL, false, statusCode );
   965	}
   966
   967    // call this to render data rather than a view and layouts
   968    public void function renderData( string type, any data ) {
   969        request._fw1.renderData = { type = type, data = data };
   970    }
   971	
   972	// call this from your controller to queue up additional services
   973	public void function service( string action, string key, struct args = { }, boolean enforceExistence = true ) {
   974		var subsystem = getSubsystem( action );
   975		var section = getSection( action );
   976		var item = getItem( action );
   977		var tuple = { };
   978
   979		if ( structKeyExists( request._fw1, 'serviceExecutionComplete' ) ) {
   980			raiseException( type='FW1.serviceExecutionComplete', message="Service '#action#' may not be added at this point.",
   981				detail='The service execution phase is complete. Services may not be added by end*() or after() controller methods.' );
   982		}
   983
   984		tuple.service = getService(section=section, subsystem=subsystem);
   985        tuple.subsystem = subsystem;
   986        tuple.section = section;
   987		tuple.item = item;
   988		tuple.key = key;
   989		tuple.args = args;
   990		tuple.enforceExistence = enforceExistence;
   991
   992		if ( structKeyExists( tuple, 'service' ) && isObject( tuple.service ) ) {
   993            frameworkTrace( 'queuing service', subsystem, section, item );
   994			arrayAppend( request._fw1.services, tuple );
   995		} else if ( enforceExistence ) {
   996			raiseException( type='FW1.serviceCfcNotFound', message="Service '#action#' does not exist.",
   997				detail="To have the execution of this service be conditional based upon its existence, pass in a fourth parameter (or enforceExistence if using named arguments) of 'false'." );
   998		}
   999	}
  1000	/*
  1001	 * call this from your setupApplication() method to tell the framework
  1002	 * about your bean factory - only assumption is that it supports:
  1003	 * - containsBean(name) - returns true if factory contains that named bean, else false
  1004	 * - getBean(name) - returns the named bean
  1005	 */
  1006	public void function setBeanFactory( any beanFactory ) {
  1007
  1008		application[ variables.framework.applicationKey ].factory = beanFactory;
  1009
  1010	}
  1011
  1012	/*
  1013	 * use this to override the default layout
  1014	 */
  1015	public void function setLayout( string action ) {
  1016		request._fw1.overrideLayoutAction = validateAction( action );
  1017	}
  1018	
  1019	/*
  1020	 * call this from your setupSubsystem() method to tell the framework
  1021	 * about your subsystem-specific bean factory - only assumption is that it supports:
  1022	 * - containsBean(name) - returns true if factory contains that named bean, else false
  1023	 * - getBean(name) - returns the named bean
  1024	 */
  1025	public void function setSubsystemBeanFactory( string subsystem, any factory ) {
  1026
  1027		ensureNewFrameworkStructsExist();
  1028		application[ variables.framework.applicationKey ].subsystemFactories[ subsystem ] = factory;
  1029
  1030	}
  1031
  1032	/*
  1033	 * override this to provide application-specific initialization
  1034	 * if you want the framework to use a bean factory and autowire
  1035	 * controllers and services, call setBeanFactory(factory) in your
  1036	 * setupApplication() method
  1037	 * you do not need to call super.setupApplication()
  1038	 */
  1039	public void function setupApplication() { }
  1040
  1041	/*
  1042	 * override this to provide environment-specific initialization
  1043	 * you do not need to call super.setupEnvironment()
  1044	 */
  1045	public void function setupEnvironment( string env ) { }
  1046
  1047	/*
  1048	 * override this to provide request-specific initialization
  1049	 * you do not need to call super.setupRequest()
  1050	 */
  1051	public void function setupRequest() { }
  1052
  1053	/*
  1054	 * override this to provide request-specific finalization
  1055	 * you do not need to call super.setupResponse()
  1056	 */
  1057	public void function setupResponse() { }
  1058
  1059	/*
  1060	 * override this to provide session-specific initialization
  1061	 * you do not need to call super.setupSession()
  1062	 */
  1063	public void function setupSession() { }
  1064
  1065	/*
  1066	 * override this to provide subsystem-specific initialization
  1067	 * if you want the framework to use a bean factory and autowire
  1068	 * controllers and services, call
  1069	 *   setSubsystemBeanFactory( subsystem, factory )
  1070	 * in your setupSubsystem() method
  1071	 * you do not need to call super.setupSubsystem( subsystem )
  1072	 */
  1073	public void function setupSubsystem( string subsystem ) { }
  1074	
  1075	/*
  1076	 * override this to provide pre-rendering logic, e.g., to
  1077	 * populate the request context with globally required data
  1078	 * you do not need to call super.setupView()
  1079	 */
  1080	public void function setupView() { }
  1081	
  1082	/*
  1083	 * use this to override the default view
  1084	 */
  1085	public void function setView( string action ) {
  1086		request._fw1.overrideViewAction = validateAction( action );
  1087	}
  1088
  1089	/*
  1090	 * returns true if the application is configured to use subsystems
  1091	 */
  1092	public boolean function usingSubsystems() {
  1093		return variables.framework.usingSubsystems;
  1094	}
  1095	
  1096	/*
  1097	 * view() may be invoked inside views and layouts
  1098	 * returns the UI generated by the named view
  1099	 */
  1100	public string function view( string path, struct args = { },
  1101                                 any missingView = { } ) {
  1102		var viewPath = parseViewOrLayoutPath( path, 'view' );
  1103        if ( cachedFileExists( viewPath ) ) {
  1104            frameworkTrace( 'view( #path# ) called - rendering #viewPath#' );
  1105		    return internalView( viewPath, args );
  1106        } else if ( isSimpleValue( missingView ) ) {
  1107            return missingView;
  1108        } else {
  1109            frameworkTrace( 'view( #path# ) called - onMissingView() called' );
  1110            return onMissingView( request.context );
  1111        }
  1112	}
  1113	
  1114	// THE FOLLOWING METHODS SHOULD ALL BE CONSIDERED PRIVATE / UNCALLABLE
  1115	
  1116	private void function autowire( any cfc, any beanFactory ) {
  1117		var setters = findImplicitAndExplicitSetters( cfc );
  1118		for ( var property in setters ) {
  1119			if ( beanFactory.containsBean( property ) ) {
  1120				var args = { };
  1121				args[ property ] = beanFactory.getBean( property );
  1122				// cfc['set'&property](argumentCollection = args) does not work on ACF9
  1123				evaluate( 'cfc.set#property#( argumentCollection = args )' );
  1124			}
  1125		}
  1126	}
  1127	
  1128	private void function buildLayoutQueue() {
  1129		var siteWideLayoutBase = request.base & getSubsystemDirPrefix( variables.framework.siteWideLayoutSubsystem );
  1130		var testLayout = 0;
  1131		// default behavior:
  1132		var subsystem = request.subsystem;
  1133		var section = request.section;
  1134		var item = request.item;
  1135		var subsystembase = '';
  1136		
  1137		request._fw1.layouts = [ ];
  1138		
  1139		// has layout been overridden?
  1140		if ( structKeyExists( request._fw1, 'overrideLayoutAction' ) ) {
  1141			subsystem = getSubsystem( request._fw1.overrideLayoutAction );
  1142			section = getSection( request._fw1.overrideLayoutAction );
  1143			item = getItem( request._fw1.overrideLayoutAction );
  1144			structDelete( request._fw1, 'overrideLayoutAction' );
  1145		}
  1146		subsystembase = request.base & getSubsystemDirPrefix( subsystem );
  1147        frameworkTrace( 'building layout queue', subsystem, section, item );
  1148		// look for item-specific layout:
  1149		testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter &
  1150													section & '/' & item, 'layout' );
  1151		if ( cachedFileExists( testLayout ) ) {
  1152            frameworkTrace( 'found item-specific layout #testLayout#', subsystem, section, item );
  1153			arrayAppend( request._fw1.layouts, testLayout );
  1154        }
  1155		// look for section-specific layout:
  1156		testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter &
  1157													section, 'layout' );
  1158		if ( cachedFileExists( testLayout ) ) {
  1159            frameworkTrace( 'found section-specific layout #testLayout#', subsystem, section, item );
  1160			arrayAppend( request._fw1.layouts, testLayout );
  1161		}
  1162		// look for subsystem-specific layout (site-wide layout if not using subsystems):
  1163		if ( request.section != 'default' ) {
  1164			testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter &
  1165														'default', 'layout' );
  1166			if ( cachedFileExists( testLayout ) ) {
  1167                frameworkTrace( 'found default layout #testLayout#', subsystem, section, item );
  1168				arrayAppend( request._fw1.layouts, testLayout );
  1169			}
  1170		}
  1171		// look for site-wide layout (only applicable if using subsystems)
  1172		if ( usingSubsystems() && siteWideLayoutBase != subsystembase ) {
  1173			testLayout = parseViewOrLayoutPath( variables.framework.siteWideLayoutSubsystem & variables.framework.subsystemDelimiter &
  1174														'default', 'layout' );
  1175			if ( cachedFileExists( testLayout ) ) {
  1176                frameworkTrace( 'found #variables.framework.siteWideLayoutSubsystem# layout #testLayout#', subsystem, section, item );
  1177				arrayAppend( request._fw1.layouts, testLayout );
  1178			}
  1179		}
  1180	}
  1181
  1182
  1183	private void function buildViewQueue() {
  1184		// default behavior:
  1185		var subsystem = request.subsystem;
  1186		var section = request.section;
  1187		var item = request.item;
  1188		var subsystembase = '';
  1189		
  1190		// has view been overridden?
  1191		if ( structKeyExists( request._fw1, 'overrideViewAction' ) ) {
  1192			subsystem = getSubsystem( request._fw1.overrideViewAction );
  1193			section = getSection( request._fw1.overrideViewAction );
  1194			item = getItem( request._fw1.overrideViewAction );
  1195			structDelete( request._fw1, 'overrideViewAction' );
  1196		}
  1197		subsystembase = request.base & getSubsystemDirPrefix( subsystem );
  1198        frameworkTrace( 'building view queue', subsystem, section, item );
  1199		// view and layout setup - used to be in setupRequestWrapper():
  1200		request._fw1.view = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter &
  1201													section & '/' & item, 'view' );
  1202		if ( cachedFileExists( request._fw1.view ) ) {
  1203            frameworkTrace( 'found view #request._fw1.view#', subsystem, section, item );
  1204        } else {
  1205            frameworkTrace( 'no such view #request._fw1.view#', subsystem, section, item );
  1206			request.missingView = request._fw1.view;
  1207			// ensures original view not re-invoked for onError() case:
  1208			structDelete( request._fw1, 'view' );
  1209		}
  1210	}
  1211
  1212
  1213	private boolean function cachedFileExists( string filePath ) {
  1214		var cache = application[ variables.framework.applicationKey ].cache;
  1215		if ( !variables.framework.cacheFileExists ) {
  1216			return fileExists( expandPath( filePath) );
  1217		}
  1218		param name="cache.fileExists" default="#{ }#";
  1219		if ( !structKeyExists( cache.fileExists, filePath ) ) {
  1220			cache.fileExists[ filePath ] = fileExists( expandPath( filePath ) );
  1221		}
  1222		return cache.fileExists[ filePath ];
  1223	}
  1224	
  1225	
  1226	private string function cfcFilePath( string dottedPath ) {
  1227		if ( dottedPath == '' ) {
  1228			return '/';
  1229		} else {
  1230			return '/' & replace( dottedPath, '.', '/', 'all' ) & '/';
  1231		}
  1232	}
  1233	
  1234	private void function doController( struct tuple, string method, string lifecycle ) {
  1235        var cfc = tuple.controller;
  1236		if ( structKeyExists( cfc, method ) ) {
  1237			try {
  1238                frameworkTrace( 'calling #lifecycle# controller', tuple.subsystem, tuple.section, method );
  1239				evaluate( 'cfc.#method#( rc = request.context )' );
  1240			} catch ( any e ) {
  1241				setCfcMethodFailureInfo( cfc, method );
  1242				rethrow;
  1243			}
  1244		}
  1245		else if ( structKeyExists( cfc, 'onMissingMethod' ) ) {
  1246			try {
  1247                frameworkTrace( 'calling #lifecycle# controller (via onMissingMethod)', tuple.subsystem, tuple.section, method );
  1248				evaluate( 'cfc.#method#( rc = request.context, method = lifecycle )' );
  1249			} catch ( any e ) {
  1250				setCfcMethodFailureInfo( cfc, method );
  1251				rethrow;
  1252			}
  1253		} else {
  1254            frameworkTrace( 'no #lifecycle# controller to call', tuple.subsystem, tuple.section, method );
  1255        }
  1256	}
  1257	
  1258	private any function doService( struct tuple, string method, struct args, boolean enforceExistence ) {
  1259        var cfc = tuple.service;
  1260		if ( structKeyExists( cfc, method ) || structKeyExists( cfc, 'onMissingMethod' ) ) {
  1261			try {
  1262				structAppend( args, request.context, false );
  1263                frameworkTrace( 'calling service', tuple.subsystem, tuple.section, method );
  1264				var _result_fw1 = evaluate( 'cfc.#method#( argumentCollection = args )' );
  1265				if ( !isNull( _result_fw1 ) ) {
  1266					return _result_fw1;
  1267				}
  1268			} catch ( any e ) {
  1269				setCfcMethodFailureInfo( cfc, method );
  1270				rethrow;
  1271			}
  1272		} else if ( enforceExistence ) {
  1273			raiseException( type='FW1.serviceMethodNotFound', message="Service method '#method#' does not exist in service '#getMetadata( cfc ).fullname#'.",
  1274				detail="To have the execution of this service method be conditional based upon its existence, pass in a third parameter of 'false'." );
  1275		}
  1276	}
  1277	
  1278	private void function dumpException( any exception ) {
  1279		writeDump( var = exception, label = 'Exception' );
  1280	}
  1281	
  1282	private void function ensureNewFrameworkStructsExist() {
  1283
  1284		var framework = application[variables.framework.applicationKey];
  1285
  1286		if ( !structKeyExists(framework, 'subsystemFactories') ) {
  1287			framework.subsystemFactories = { };
  1288		}
  1289
  1290		if ( !structKeyExists(framework, 'subsystems') ) {
  1291			framework.subsystems = { };
  1292		}
  1293
  1294	}
  1295
  1296	private void function failure( any exception, string event, boolean indirect = false, boolean early = false ) {
  1297		var h = indirect ? 3 : 1;
  1298		if ( structKeyExists(exception, 'rootCause') ) {
  1299			exception = exception.rootCause;
  1300		}
  1301		getPageContext().getResponse().setStatus( 500 );
  1302		if ( arguments.early ) {
  1303		    writeOutput( '<h1>Exception occured before FW/1 was initialized</h1>');
  1304		} else {
  1305			writeOutput( '<h#h#>' & ( indirect ? 'Original exception ' : 'Exception' ) & ' in #event#</h#h#>' );
  1306			if ( structKeyExists( request, 'failedAction' ) ) {
  1307				writeOutput( '<p>The action #request.failedAction# failed.</p>' );
  1308			}
  1309			writeOutput( '<h#1+h#>#exception.message#</h#1+h#>' );
  1310		}
  1311		
  1312		writeOutput( '<p>#exception.detail# (#exception.type#)</p>' );
  1313		dumpException(exception);
  1314
  1315	}
  1316
  1317	private struct function findImplicitAndExplicitSetters( any cfc ) {
  1318		var baseMetadata = getMetadata( cfc );
  1319		var setters = { };
  1320		// is it already attached to the CFC metadata?
  1321		if ( structKeyExists( baseMetadata, '__fw1_setters' ) )  {
  1322			setters = baseMetadata.__fw1_setters;
  1323		} else {
  1324			var md = { extends = baseMetadata };
  1325			do {
  1326				md = md.extends;
  1327				var implicitSetters = false;
  1328				// we have implicit setters if: accessors="true" or persistent="true"
  1329				if ( structKeyExists( md, 'persistent' ) && isBoolean( md.persistent ) ) {
  1330					implicitSetters = md.persistent;
  1331				}
  1332				if ( structKeyExists( md, 'accessors' ) && isBoolean( md.accessors ) ) {
  1333					implicitSetters = implicitSetters || md.accessors;
  1334				}
  1335				if ( structKeyExists( md, 'properties' ) ) {
  1336					// due to a bug in ACF9.0.1, we cannot use var property in md.properties,
  1337					// instead we must use an explicit loop index... ugh!
  1338					var n = arrayLen( md.properties );
  1339					for ( var i = 1; i <= n; ++i ) {
  1340						var property = md.properties[ i ];
  1341						if ( implicitSetters ||
  1342								structKeyExists( property, 'setter' ) && isBoolean( property.setter ) && property.setter ) {
  1343							setters[ property.name ] = 'implicit';
  1344						}
  1345					}
  1346				}
  1347			} while ( structKeyExists( md, 'extends' ) );
  1348			// cache it in the metadata (note: in Railo 3.2 metadata cannot be modified
  1349			// which is why we return the local setters structure - it has to be built
  1350			// on every controller call; fixed in Railo 3.3)
  1351			baseMetadata.__fw1_setters = setters;
  1352		}
  1353		// gather up explicit setters as well
  1354		for ( var member in cfc ) {
  1355			var method = cfc[ member ];
  1356			var n = len( member );
  1357			if ( isCustomFunction( method ) && left( member, 3 ) == 'set' && n > 3 ) {
  1358				var property = right( member, n - 3 );
  1359				setters[ property ] = 'explicit';
  1360			}
  1361		}
  1362		return setters;
  1363	}
  1364
  1365    private void function frameworkTrace( string message, string subsystem = '', string section = '', string item = '' ) {
  1366        if ( variables.framework.trace ) {
  1367            try {
  1368                if ( isDefined( 'session._fw1_trace' ) &&
  1369                     structKeyExists( session, '_fw1_trace' ) ) {
  1370                    request._fw1.trace = session._fw1_trace;
  1371                    structDelete( session, '_fw1_trace' );
  1372                }
  1373            } catch ( any _ ) {
  1374                // ignore if session is not enabled
  1375            }
  1376            arrayAppend( request._fw1.trace, { tick = getTickCount(), msg = message, sub = subsystem, s = section, i = item } );
  1377        }
  1378    }
  1379
  1380    private void function frameworkTraceRender() {
  1381        if ( variables.framework.trace && arrayLen( request._fw1.trace ) ) {
  1382            var startTime = request._fw1.trace[1].tick;
  1383            var font = 'font-family: verdana, helvetica;';
  1384            writeOutput( '<hr /><div style="background: ##ccdddd; color: black; border: 1px solid; border-color: black; padding: 5px; #font#">' );
  1385            writeOutput( '<div style="#font# font-weight: bold; font-size: large; float: left;">Framework Lifecycle Trace</div><div style="clear: both;"></div>' );
  1386            var table = '<table style="border: 1px solid; border-color: black; color: black; #font#" width="100%">';
  1387            writeOutput( table );
  1388            var colors = [ '##ccd4dd', '##ccddcc' ];
  1389            var row = 0;
  1390            var n = arrayLen( request._fw1.trace );
  1391            for ( var i = 1; i <= n; ++i ) {
  1392                var trace = request._fw1.trace[i];
  1393                var action = '';
  1394                if ( trace.s == variables.magicApplicationController || trace.sub == variables.magicApplicationSubsystem ) {
  1395                    action = '<em>Application.cfc</em>';
  1396                    if ( right( trace.i, len( variables.magicApplicationAction ) ) == variables.magicApplicationAction ) {
  1397                        continue;
  1398                    }
  1399                } else {
  1400                    action = trace.sub;
  1401                    if ( action != '' && trace.s != '' ) {
  1402                        action &= variables.framework.subsystemDelimiter;
  1403                    }
  1404                    action &= trace.s;
  1405                    if ( trace.s != '' ) {
  1406                        action &= '.';
  1407                    }
  1408                    action &= trace.i;
  1409                }
  1410                ++row;
  1411                writeOutput( '<tr style="border: 0; background: #colors[1 + row mod 2]#;">' );
  1412                writeOutput( '<td style="border: 0; color: black; #font# font-size: small;" width="5%">#trace.tick - startTime#ms</td>' );
  1413                writeOutput( '<td style="border: 0; color: black; #font# font-size: small;" width="10%">#action#</td>' );
  1414                var color =
  1415                    trace.msg.startsWith( 'no ' ) ? '##cc8888' :
  1416                        trace.msg.startsWith( 'onError( ' ) ? '##cc0000' : '##0000';
  1417                writeOutput( '<td style="border: 0; color: #color#; #font# font-size: small;">#trace.msg#</td>' );
  1418                writeOutput( '</tr>' );
  1419                if ( trace.msg.startsWith( 'redirecting ' ) ) {
  1420                    writeOutput( '</table>#table#' );
  1421                    if ( i < n ) startTime = request._fw1.trace[i+1].tick;
  1422                }
  1423            }
  1424            writeOutput( '<table></div>' );
  1425        }
  1426    }
  1427
  1428	private any function getCachedComponent( string type, string subsystem, string section ) {
  1429
  1430		setupSubsystemWrapper( subsystem );
  1431		var cache = application[variables.framework.applicationKey].cache;
  1432		var types = type & 's';
  1433		var cfc = 0;
  1434		var subsystemDir = getSubsystemDirPrefix( subsystem );
  1435		var subsystemDot = replace( subsystemDir, '/', '.', 'all' );
  1436		var subsystemUnderscore = replace( subsystemDir, '/', '_', 'all' );
  1437		var componentKey = subsystemUnderscore & section;
  1438		var beanName = section & type;
  1439		
  1440		if ( !structKeyExists( cache[ types ], componentKey ) ) {
  1441			lock name="fw1_#application.applicationName#_#variables.framework.applicationKey#_#type#_#componentKey#" type="exclusive" timeout="30" {
  1442				if ( !structKeyExists( cache[ types ], componentKey ) ) {
  1443					if ( usingSubsystems() && hasSubsystemBeanFactory( subsystem ) && getSubsystemBeanFactory( subsystem ).containsBean( beanName ) ) {
  1444						cfc = getSubsystemBeanFactory( subsystem ).getBean( beanName );
  1445					} else if ( !usingSubsystems() && hasDefaultBeanFactory() && getDefaultBeanFactory().containsBean( beanName ) ) {
  1446						cfc = getDefaultBeanFactory().getBean( beanName );
  1447					} else {
  1448						if ( type == 'controller' && section == variables.magicApplicationController ) {
  1449							// treat this (Application.cfc) as a controller:
  1450							cfc = this;
  1451						} else if ( cachedFileExists( cfcFilePath( request.cfcbase ) & subsystemDir & types & '/' & section & '.cfc' ) ) {
  1452							// we call createObject() rather than new so we can control initialization:
  1453							if ( request.cfcbase == '' ) {
  1454								cfc = createObject( 'component', subsystemDot & types & '.' & section );
  1455							} else {
  1456								cfc = createObject( 'component', request.cfcbase & '.' & subsystemDot & types & '.' & section );
  1457							}
  1458							if ( structKeyExists( cfc, 'init' ) ) {
  1459								if ( type == 'controller' ) {
  1460									cfc.init( this );
  1461								} else {
  1462									cfc.init();
  1463								}
  1464							}
  1465						}
  1466						if ( isObject( cfc ) && ( hasDefaultBeanFactory() || hasSubsystemBeanFactory( subsystem ) ) ) {
  1467							autowire( cfc, getBeanFactory( subsystem ) );
  1468						}
  1469					}
  1470					if ( isObject( cfc ) ) {
  1471                        if ( type == 'controller' ) injectFramework( cfc );
  1472						cache[ types ][ componentKey ] = cfc;
  1473					}
  1474				}
  1475			}
  1476		}
  1477
  1478		if ( structKeyExists( cache[ types ], componentKey ) ) {
  1479			return cache[ types ][ componentKey ];
  1480		}
  1481		// else "return null" effectively
  1482	}
  1483	
  1484	private any function getController( string section, string subsystem = getDefaultSubsystem() ) {
  1485		var _controller_fw1 = getCachedComponent( 'controller', subsystem, section );
  1486		if ( isDefined( '_controller_fw1' ) ) {
  1487			return _controller_fw1;
  1488		}
  1489	}
  1490	
  1491	private string function getNextPreserveKeyAndPurgeOld() {
  1492		var nextPreserveKey = '';
  1493		var oldKeyToPurge = '';
  1494        try {
  1495		    if ( variables.framework.maxNumContextsPreserved > 1 ) {
  1496			    lock scope="session" type="exclusive" timeout="30" {
  1497				    param name="session.__fw1NextPreserveKey" default="1";
  1498				    nextPreserveKey = session.__fw1NextPreserveKey;
  1499				    session.__fw1NextPreserveKey = session.__fw1NextPreserveKey + 1;
  1500			    }
  1501			    oldKeyToPurge = nextPreserveKey - variables.framework.maxNumContextsPreserved;
  1502		    } else {
  1503			    lock scope="session" type="exclusive" timeout="30" {
  1504				    session.__fw1PreserveKey = '';
  1505				    nextPreserveKey = session.__fw1PreserveKey;
  1506			    }
  1507			    oldKeyToPurge = '';
  1508            }
  1509		} catch ( any e ) {
  1510            // ignore - assume session scope is disabled
  1511        }
  1512		var key = getPreserveKeySessionKey( oldKeyToPurge );
  1513		if ( structKeyExists( session, key ) ) {
  1514			structDelete( session, key );
  1515		}
  1516		return nextPreserveKey;
  1517	}
  1518	
  1519	private string function getPreserveKeySessionKey( string preserveKey ) {
  1520		return '__fw1' & preserveKey;
  1521	}
  1522	
  1523	private any function getService( string section, string subsystem = getDefaultSubsystem() ) {
  1524		var _service_fw1 = getCachedComponent( 'service', subsystem, section );
  1525		if ( isDefined( '_service_fw1' ) ) {
  1526			return _service_fw1;
  1527		}
  1528	}
  1529	
  1530	private string function getSubsystemDirPrefix( string subsystem ) {
  1531
  1532		if ( subsystem eq '' ) {
  1533			return '';
  1534		}
  1535
  1536		return subsystem & '/';
  1537	}
  1538	
  1539	private void function injectFramework( any cfc ) {
  1540		var args = { };
  1541		if ( structKeyExists( cfc, 'setFramework' ) || structKeyExists( cfc, 'onMissingMethod' ) ) {
  1542			args.framework = this;
  1543			// allow alternative spellings
  1544			args.fw = this;
  1545			args.fw1 = this;
  1546			evaluate( 'cfc.setFramework( argumentCollection = args )' );
  1547		}
  1548	}
  1549	
  1550	private string function internalLayout( string layoutPath, string body ) {
  1551		var rc = request.context;
  1552		var $ = { };
  1553		// integration point with Mura:
  1554		if ( structKeyExists( rc, '$' ) ) {
  1555			$ = rc.$;
  1556		}
  1557		if ( !structKeyExists( request._fw1, 'controllerExecutionComplete' ) ) {
  1558			raiseException( type='FW1.layoutExecutionFromController', message='Invalid to call the layout method at this point.',
  1559				detail='The layout method should not be called prior to the completion of the controller execution phase.' );
  1560		}
  1561		var response = '';
  1562		savecontent variable="response" {
  1563			include '#layoutPath#';
  1564		}
  1565		return response;
  1566	}
  1567	
  1568	private string function internalView( string viewPath, struct args = { } ) {
  1569		var rc = request.context;
  1570		var $ = { };
  1571		// integration point with Mura:
  1572		if ( structKeyExists( rc, '$' ) ) {
  1573			$ = rc.$;
  1574		}
  1575		structAppend( local, args );
  1576		if ( !structKeyExists( request._fw1, 'serviceExecutionComplete') &&
  1577             structKeyExists( request._fw1, 'services' ) && arrayLen( request._fw1.services ) != 0 ) {
  1578			raiseException( type='FW1.viewExecutionFromController', message='Invalid to call the view method at this point.',
  1579				detail='The view method should not be called prior to the completion of the service execution phase.' );
  1580		}
  1581		var response = '';
  1582		savecontent variable="response" {
  1583			include '#viewPath#';
  1584		}
  1585		return response;
  1586	}
  1587	
  1588	private boolean function isFrameworkInitialized() {
  1589		return structKeyExists( variables, 'framework' ) &&
  1590            structKeyExists( application, variables.framework.applicationKey );
  1591	}
  1592
  1593	private boolean function isFrameworkReloadRequest() {
  1594		return ( isDefined( 'URL' ) &&
  1595					structKeyExists( URL, variables.framework.reload ) &&
  1596					URL[ variables.framework.reload ] == variables.framework.password ) ||
  1597				variables.framework.reloadApplicationOnEveryRequest;
  1598	}
  1599
  1600	private boolean function isSubsystemInitialized( string subsystem ) {
  1601
  1602		ensureNewFrameworkStructsExist();
  1603
  1604		return structKeyExists( application[ variables.framework.applicationKey ].subsystems, subsystem );
  1605
  1606	}
  1607
  1608	private string function parseViewOrLayoutPath( string path, string type ) {
  1609
  1610		var pathInfo = { };
  1611		var subsystem = getSubsystem( path );
  1612
  1613		// allow for :section/action to simplify logic in setupRequestWrapper():
  1614		pathInfo.path = listLast( path, variables.framework.subsystemDelimiter );
  1615		pathInfo.base = request.base;
  1616		pathInfo.subsystem = subsystem;
  1617		if ( usingSubsystems() ) {
  1618			pathInfo.base = pathInfo.base & getSubsystemDirPrefix( subsystem );
  1619		}
  1620
  1621		return customizeViewOrLayoutPath( pathInfo, type, '#pathInfo.base##type#s/#pathInfo.path#.cfm' );
  1622
  1623	}
  1624	
  1625	private struct function processRouteMatch( string route, string target, string path ) {
  1626		var regExCache = isFrameworkInitialized() ? application[ variables.framework.applicationKey ].cache.routes.regex : { };
  1627		var cacheKey = hash( route & target );
  1628		if ( !structKeyExists( regExCache, cacheKey ) ) {
  1629			var routeRegEx = { redirect = false, method = '', pattern = route, target = target };
  1630			// if target has numeric prefix, strip it and set redirect:
  1631			var prefix = listFirst( routeRegEx.target, ':' );
  1632			if ( isNumeric( prefix ) ) {
  1633				routeRegEx.redirect = true;
  1634				routeRegEx.statusCode = prefix;
  1635				routeRegEx.target = listRest( routeRegEx.target, ':' );
  1636			}
  1637			// special routes begin with $METHOD, * is also a wildcard
  1638			var routeLen = len( routeRegEx.pattern );
  1639			if ( routeLen ) {
  1640				if ( left( routeRegEx.pattern, 1 ) == '$' ) {
  1641					// check HTTP method
  1642					routeRegEx.method = listFirst( routeRegEx.pattern, '*/' );
  1643					var methodLen = len( routeRegEx.method );
  1644					if ( routeLen == methodLen ) {
  1645						routeRegEx.pattern = '*';
  1646					} else {
  1647						routeRegEx.pattern = right( routeRegEx.pattern, routeLen - methodLen );
  1648					}
  1649				}
  1650				if ( routeRegEx.pattern == '*' ) {
  1651					routeRegEx.pattern = '/';
  1652				} else if ( right( routeRegEx.pattern, 1 ) != '/' && right( routeRegEx.pattern, 1 ) != '$' ) {
  1653					// only add the closing backslash if last position is not already a "/" or a "$" to respect regex end of string
  1654					routeRegEx.pattern &= '/';
  1655				}
  1656			} else {
  1657				routeRegEx.pattern = '/';
  1658			}
  1659			if ( !len( routeRegEx.target ) || right( routeRegEx.target, 1) != '/' ) routeRegEx.target &= '/';
  1660			// walk for self defined (regex) and :var -  replace :var with ([^/]*) in route and back reference in target:
  1661			var n = 1;
  1662			var placeholders = rematch( '(:[^/]+)|(\([^\)]+)', routeRegEx.pattern );
  1663			for ( var placeholder in placeholders ) {
  1664				if ( left( placeholder, 1 ) == ':') {
  1665					routeRegEx.pattern = replace( routeRegEx.pattern, placeholder, '([^/]*)' );
  1666					routeRegEx.target = replace( routeRegEx.target, placeholder, chr(92) & n );
  1667				}
  1668				++n;
  1669			}
  1670			// add trailing match/back reference: if last character is not "$" to respect regex end of string
  1671			if (right( routeRegEx.pattern, 1 ) != '$')
  1672				routeRegEx.pattern &= '(.*)';
  1673			routeRegEx.target &= chr(92) & n;
  1674			regExCache[ cacheKey ] = routeRegEx;
  1675		}
  1676		// end of preprocessing section
  1677		var routeMatch = { matched = false };
  1678		structAppend( routeMatch, regExCache[ cacheKey ] );
  1679		if ( !len( path ) || right( path, 1) != '/' ) path &= '/';
  1680		var matched = len( routeMatch.method ) ? ( '$' & request._fw1.cgiRequestMethod == routeMatch.method ) : true;
  1681		if ( matched && reFind( routeMatch.pattern, path ) ) {
  1682			routeMatch.matched = true;
  1683			routeMatch.route = route;
  1684			routeMatch.path = path;
  1685		}
  1686		return routeMatch;
  1687	}
  1688
  1689	private array function getResourceRoutes( any resourcesToRoute, string subsystem = '', string pathRoot = '', string targetAppend = '' ) {
  1690		var resourceCache = isFrameworkInitialized() ? application[ variables.framework.applicationKey ].cache.routes.resources : { };
  1691		var cacheKey = hash( serializeJSON( resourcesToRoute ) );
  1692		if ( !structKeyExists( resourceCache, cacheKey ) ) {
  1693			// get passed in resourcesToRoute (string,array,struct) to match following struct
  1694			var resources = { resources = [ ], subsystem = subsystem, pathRoot = pathRoot, methods = [ ], nested = [ ] };
  1695			if ( isStruct( resourcesToRoute ) ) {
  1696				structAppend( resources, resourcesToRoute );
  1697				if ( !isArray( resources.resources ) ) resources.resources = listToArray( resources.resources );
  1698				if ( !isArray( resources.methods ) ) resources.methods = listToArray( resources.methods );
  1699				// if this is a recursive (nested) function call, don't let pathRoot or subsystem be overwritten
  1700				if ( len( pathRoot ) ) {
  1701					resources.pathRoot = pathRoot;
  1702					resources.subsystem = subsystem;
  1703				}
  1704			} else {
  1705				resources.resources = isArray( resourcesToRoute ) ? resourcesToRoute : listToArray( resourcesToRoute );
  1706			}
  1707			// create the routes
  1708			var routes = [ ];
  1709			for ( var resource in resources.resources  ) {
  1710				// take possible subsystem into account by qualifying resource name with subsystem name (if necessary)
  1711				var subsystemResource = ( len( resources.subsystem ) && !len( pathRoot ) ? '#resources.subsystem#/' : '' ) & resource;
  1712				var subsystemResourceTarget = ( len( resources.subsystem ) ? '#resources.subsystem#:' : '' ) & resource;
  1713				for ( var routeTemplate in getResourceRouteTemplates() ) {
  1714					// if method names were passed in, only use templates with matching method names
  1715					if ( arrayLen( resources.methods ) && !arrayFindNoCase( resources.methods, routeTemplate.method ) ) continue;
  1716					var routePack = { };
  1717					for ( var httpMethod in routeTemplate.httpMethods ) {
  1718						// build the route
  1719						var route = '#httpMethod##resources.pathRoot#/#subsystemResource#';
  1720						if ( structKeyExists( routeTemplate, 'includeId' ) && routeTemplate.includeId ) route &= '/:id';
  1721						if ( structKeyExists( routeTemplate, 'routeSuffix' ) ) route &= routeTemplate.routeSuffix;
  1722						route &= '/$';
  1723						// build the target
  1724						var target = '/#subsystemResourceTarget#/#routeTemplate.method#';
  1725						if ( structKeyExists( routeTemplate, 'includeId' ) && routeTemplate.includeId ) target &= '/id/:id';
  1726						if ( structKeyExists( routeTemplate, 'targetSuffix' ) ) target &= routeTemplate.targetSuffix;
  1727						target &= targetAppend; 
  1728						routePack[ route ] = target;
  1729					}
  1730					arrayAppend( routes, routePack );
  1731				}
  1732				// nested routes
  1733				var nestedPathRoot = '#resources.pathRoot#/#subsystemResource#/:#resource#_id';
  1734				var nestedTargetAppend = '#targetAppend#/#resource#_id/:#resource#_id';
  1735				var nestedRoutes = getResourceRoutes( resources.nested, resources.subsystem, nestedPathRoot, nestedTargetAppend );
  1736				// wish I could concatenate the arrays...not sure about using java -> routes.addAll( nestedRoutes )
  1737				for ( var nestedPack in nestedRoutes ) {
  1738					arrayAppend( routes, nestedPack );
  1739				}
  1740			}
  1741			resourceCache[ cacheKey ] = routes;
  1742		}
  1743		return resourceCache[ cacheKey ];
  1744	}
  1745
  1746	private struct function processRoutes( string path, array routes = getRoutes() ) {
  1747		for ( var routePack in routes ) {
  1748			for ( var route in routePack ) {
  1749				if ( route == 'hint' ) continue;
  1750				if ( route == '$RESOURCES' ) {
  1751					var routeMatch = processRoutes( path, getResourceRoutes( routePack[ route ] ) );
  1752				} else {
  1753					var routeMatch = processRouteMatch( route, routePack[ route ], path );
  1754				}
  1755				if ( routeMatch.matched ) return routeMatch;
  1756			}
  1757		}
  1758		return { matched = false };
  1759	}
  1760
  1761	private void function raiseException( string type, string message, string detail ) {
  1762		throw( type = type, message = message, detail = detail );
  1763	}
  1764
  1765    private string function renderDataWithContentType() {
  1766        var out = '';
  1767        var contentType = '';
  1768        var type = request._fw1.renderData.type;
  1769        var data = request._fw1.renderData.data;
  1770        switch ( type ) {
  1771        case 'json':
  1772            contentType = 'application/javascript; charset=utf-8';
  1773            out = serializeJSON( data );
  1774            break;
  1775        case 'xml':
  1776            contentType = 'text/xml; charset=utf-8';
  1777            if ( isXML( data ) ) {
  1778                if ( isSimpleValue( data ) ) {
  1779                    // XML as string already
  1780                    out = data;
  1781                } else {
  1782                    // XML object
  1783                    out = toString( data );
  1784                }
  1785            } else {
  1786                throw( type = 'FW1.UnsupportXMLRender',
  1787                       message = 'Data is not XML',
  1788                       detail = 'renderData() called with XML type but unrecognized data format' );
  1789            }
  1790            break;
  1791        case 'text':
  1792            contentType = 'text/plain; charset=utf-8';
  1793            out = data;
  1794            break;
  1795        default:
  1796            throw( type = 'FW1.UnsupportedRenderType',
  1797                   message = 'Only JSON, XML, and TEXT are supported',
  1798                   detail = 'renderData() called with unknown type: ' & type );
  1799            break;
  1800        }
  1801        // set the content type header portably:
  1802        getPageContext().getResponse().setContentType( contentType );
  1803        return out;
  1804    }
  1805
  1806	private void function restoreFlashContext() {
  1807		if ( variables.framework.maxNumContextsPreserved > 1 ) {
  1808			if ( !structKeyExists( URL, variables.framework.preserveKeyURLKey ) ) {
  1809				return;
  1810			}
  1811			var preserveKey = URL[ variables.framework.preserveKeyURLKey ];
  1812			var preserveKeySessionKey = getPreserveKeySessionKey( preserveKey );
  1813		} else {
  1814			var preserveKeySessionKey = getPreserveKeySessionKey( '' );
  1815		}
  1816		try {
  1817			if ( structKeyExists( session, preserveKeySessionKey ) ) {
  1818				structAppend( request.context, session[ preserveKeySessionKey ], false );
  1819				if ( variables.framework.maxNumContextsPreserved == 1 ) {
  1820					/*
  1821						When multiple contexts are preserved, the oldest context is purged
  1822					 	within getNextPreserveKeyAndPurgeOld once the maximum is reached.
  1823					 	This allows for a browser refresh after the redirect to still receive
  1824					 	the same context.
  1825					*/
  1826					structDelete( session, preserveKeySessionKey );
  1827				}
  1828			}
  1829		} catch ( any e ) {
  1830			// session scope not enabled, do nothing
  1831		}
  1832	}
  1833	
  1834	private string function saveFlashContext( string keys ) {
  1835		var curPreserveKey = getNextPreserveKeyAndPurgeOld();
  1836		var preserveKeySessionKey = getPreserveKeySessionKey( curPreserveKey );
  1837		try {
  1838			param name="session.#preserveKeySessionKey#" default="#{ }#";
  1839			if ( keys == 'all' ) {
  1840				structAppend( session[ preserveKeySessionKey ], request.context );
  1841			} else {
  1842				var key = 0;
  1843				var keyNames = listToArray( keys );
  1844				for ( key in keyNames ) {
  1845					if ( structKeyExists( request.context, key ) ) {
  1846						session[ preserveKeySessionKey ][ key ] = request.context[ key ];
  1847					}
  1848				}
  1849			}
  1850		} catch ( any ex ) {
  1851			// session scope not enabled, do nothing
  1852		}
  1853		return curPreserveKey;
  1854	}
  1855
  1856	private void function setCfcMethodFailureInfo( any cfc, string method ) {
  1857		var meta = getMetadata( cfc );
  1858		if ( structKeyExists( meta, 'fullname' ) ) {
  1859			request.failedCfcName = meta.fullname;
  1860		} else {
  1861			request.failedCfcName = meta.name;
  1862		}
  1863		request.failedMethod = method;
  1864	}
  1865	
  1866	private void function setupApplicationWrapper() {
  1867		/*
  1868			since this can be called on a reload, we need to lock it to prevent other threads
  1869			trying to reload the app at the same time since we're messing with the main application
  1870			data struct... if the application is already running, we don't blow away the factories
  1871			because we don't want to affect other threads that may be running at this time
  1872		*/
  1873		var frameworkCache = { };
  1874		var framework = { };
  1875		var isReload = true;
  1876		frameworkCache.lastReload = now();
  1877		frameworkCache.fileExists = { };
  1878		frameworkCache.controllers = { };
  1879		frameworkCache.services = { };
  1880		frameworkCache.routes = { regex = { }, resources = { } };
  1881		lock name="fw1_#application.applicationName#_#variables.framework.applicationKey#_initialization" type="exclusive" timeout="10" {
  1882			if ( structKeyExists( application, variables.framework.applicationKey ) ) {
  1883				// application is already loaded, just reset the cache and trigger re-initialization of subsystems
  1884				application[variables.framework.applicationKey].cache = frameworkCache;
  1885				application[variables.framework.applicationKey].subsystems = { };
  1886			} else {
  1887				// must be first request so we need to set up the entire structure
  1888				isReload = false;
  1889				framework.cache = frameworkCache;
  1890				framework.subsystems = { };
  1891				framework.subsystemFactories = { }; 
  1892				application[variables.framework.applicationKey] = framework;
  1893			}
  1894		}
  1895		
  1896		// this will recreate the main bean factory on a reload:
  1897        frameworkTrace( 'setupApplication() called' );
  1898		setupApplication();
  1899		
  1900		if ( isReload ) {
  1901			/*
  1902				it's possible that the cache got populated by another thread between resetting the cache above
  1903				and the factory getting recreated by the user code in setupApplication() so we flush the cache
  1904				again here to be safe / paranoid! 
  1905			*/
  1906			frameworkCache = { };
  1907			frameworkCache.lastReload = now();
  1908			frameworkCache.fileExists = { };
  1909			frameworkCache.controllers = { };
  1910			frameworkCache.services = { };
  1911			frameworkCache.routes = { regex = { }, resources = { } };
  1912			application[variables.framework.applicationKey].cache = frameworkCache;
  1913			application[variables.framework.applicationKey].subsystems = { };
  1914		}
  1915	
  1916	}
  1917	
  1918	private void function setupFrameworkDefaults() {
  1919
  1920		// default values for Application::variables.framework structure:
  1921		if ( !structKeyExists(variables, 'framework') ) {
  1922			variables.framework = { };
  1923		}
  1924		if ( !structKeyExists(variables.framework, 'action') ) {
  1925			variables.framework.action = 'action';
  1926		}
  1927		if ( !structKeyExists(variables.framework, 'base') ) {
  1928			variables.framework.base = getDirectoryFromPath( variables.cgiScriptName );
  1929		} else if ( right( variables.framework.base, 1 ) != '/' ) {
  1930			variables.framework.base = variables.framework.base & '/';
  1931		}
  1932		variables.framework.base = replace( variables.framework.base, chr(92), '/', 'all' );
  1933		if ( !structKeyExists(variables.framework, 'cfcbase') ) {
  1934			if ( len( variables.framework.base ) eq 1 ) {
  1935				variables.framework.cfcbase = '';
  1936			} else {
  1937				variables.framework.cfcbase = replace( mid( variables.framework.base, 2, len(variables.framework.base)-2 ), '/', '.', 'all' );
  1938			}
  1939		}
  1940		if ( !structKeyExists(variables.framework, 'usingSubsystems') ) {
  1941			variables.framework.usingSubsystems = structKeyExists(variables.framework,'defaultSubsystem') || structKeyExists(variables.framework,'sitewideLayoutSubsystem');
  1942		}
  1943		if ( !structKeyExists(variables.framework, 'defaultSubsystem') ) {
  1944			variables.framework.defaultSubsystem = 'home';
  1945		}
  1946		if ( !structKeyExists(variables.framework, 'defaultSection') ) {
  1947			variables.framework.defaultSection = 'main';
  1948		}
  1949		if ( !structKeyExists(variables.framework, 'defaultItem') ) {
  1950			variables.framework.defaultItem = 'default';
  1951		}
  1952		if ( !structKeyExists(variables.framework, 'subsystemDelimiter') ) {
  1953			variables.framework.subsystemDelimiter = ':';
  1954		}
  1955		if ( !structKeyExists(variables.framework, 'siteWideLayoutSubsystem') ) {
  1956			variables.framework.siteWideLayoutSubsystem = 'common';
  1957		}
  1958		if ( structKeyExists(variables.framework, 'home') ) {
  1959            if (usingSubsystems()) {
  1960                if ( !find( variables.framework.subsystemDelimiter, variables.framework.home ) ) {
  1961                    raiseException( type = "FW1.configuration.home", message = "You are using subsystems but framework.home does not specify a subsystem.", detail = "You should set framework.home to #variables.framework.defaultSubsystem##variables.framework.subsystemDelimiter##variables.framework.home#" );
  1962                }
  1963            }
  1964        } else {
  1965			if (usingSubsystems()) {
  1966				variables.framework.home = variables.framework.defaultSubsystem & variables.framework.subsystemDelimiter & variables.framework.defaultSection & '.' & variables.framework.defaultItem;
  1967			} else {
  1968				variables.framework.home = variables.framework.defaultSection & '.' & variables.framework.defaultItem;
  1969			}
  1970		}
  1971		if ( !structKeyExists(variables.framework, 'error') ) {
  1972			if (usingSubsystems()) {
  1973				variables.framework.error = variables.framework.defaultSubsystem & variables.framework.subsystemDelimiter & variables.framework.defaultSection & '.error';
  1974			} else {
  1975				variables.framework.error = variables.framework.defaultSection & '.error';
  1976			}
  1977		}
  1978		if ( !structKeyExists(variables.framework, 'reload') ) {
  1979			variables.framework.reload = 'reload';
  1980		}
  1981		if ( !structKeyExists(variables.framework, 'password') ) {
  1982			variables.framework.password = 'true';
  1983		}
  1984		if ( !structKeyExists(variables.framework, 'reloadApplicationOnEveryRequest') ) {
  1985			variables.framework.reloadApplicationOnEveryRequest = false;
  1986		}
  1987		if ( !structKeyExists(variables.framework, 'preserveKeyURLKey') ) {
  1988			variables.framework.preserveKeyURLKey = 'fw1pk';
  1989		}
  1990		if ( !structKeyExists(variables.framework, 'maxNumContextsPreserved') ) {
  1991			variables.framework.maxNumContextsPreserved = 10;
  1992		}
  1993		if ( !structKeyExists(variables.framework, 'baseURL') ) {
  1994			variables.framework.baseURL = 'useCgiScriptName';
  1995		}
  1996		if ( !structKeyExists(variables.framework, 'generateSES') ) {
  1997			variables.framework.generateSES = false;
  1998		}
  1999		if ( !structKeyExists(variables.framework, 'SESOmitIndex') ) {
  2000			variables.framework.SESOmitIndex = false;
  2001		}
  2002		// NOTE: unhandledExtensions is a list of file extensions that are not handled by FW/1
  2003		if ( !structKeyExists(variables.framework, 'unhandledExtensions') ) {
  2004			variables.framework.unhandledExtensions = 'cfc';
  2005		}
  2006		// NOTE: you can provide a comma delimited list of paths.  Since comma is the delim, it can not be part of your path URL to exclude
  2007		if ( !structKeyExists(variables.framework, 'unhandledPaths') ) {
  2008			variables.framework.unhandledPaths = '/flex2gateway';
  2009		}				
  2010        if ( !structKeyExists( variables.framework, 'unhandledErrorCaught' ) ) {
  2011            variables.framework.unhandledErrorCaught = false;
  2012        }
  2013		// convert unhandledPaths to regex:
  2014		variables.framework.unhandledPathRegex = replaceNoCase(
  2015			REReplace( variables.framework.unhandledPaths, '(\+|\*|\?|\.|\[|\^|\$|\(|\)|\{|\||\\)', '\\\1', 'all' ),
  2016			',', '|', 'all' );
  2017		if ( !structKeyExists(variables.framework, 'applicationKey') ) {
  2018			variables.framework.applicationKey = 'org.corfield.framework';
  2019		}
  2020		if ( !structKeyExists( variables.framework, 'suppressImplicitService' ) ) {
  2021			variables.framework.suppressImplicitService = true;
  2022		}
  2023		if ( !structKeyExists( variables.framework, 'cacheFileExists' ) ) {
  2024			variables.framework.cacheFileExists = false;
  2025		}
  2026		if ( !structKeyExists( variables.framework, 'routes' ) ) {
  2027			variables.framework.routes = [ ];
  2028		}
  2029		if ( !structKeyExists( variables.framework, 'resourceRouteTemplates' ) ) {
  2030			variables.framework.resourceRouteTemplates = [
  2031				{ method = 'default', httpMethods = [ '$GET' ] },
  2032				{ method = 'new', httpMethods = [ '$GET' ], routeSuffix = '/new' },
  2033				{ method = 'create', httpMethods = [ '$POST' ] },
  2034				{ method = 'show', httpMethods = [ '$GET' ], includeId = true },
  2035				{ method = 'update', httpMethods = [ '$PUT','$PATCH' ], includeId = true },
  2036				{ method = 'destroy', httpMethods = [ '$DELETE' ], includeId = true }
  2037			];
  2038		}
  2039		if ( !structKeyExists( variables.framework, 'noLowerCase' ) ) {
  2040			variables.framework.noLowerCase = false;
  2041		}
  2042		if ( !structKeyExists( variables.framework, 'subsystems' ) ) {
  2043			variables.framework.subsystems = { };
  2044		}
  2045		if ( !structKeyExists( variables.framework, 'trace' ) ) {
  2046			variables.framework.trace = false;
  2047		}
  2048	    variables.framework.version = '2.2_rc1';
  2049        setupFrameworkEnvironments();
  2050	}
  2051
  2052    private void function setupFrameworkEnvironments() {
  2053        var env = getEnvironment();
  2054        if ( structKeyExists( variables.framework, 'environments' ) ) {
  2055            var envs = variables.framework.environments;
  2056            var tier = listFirst( env, '-' );
  2057            if ( structKeyExists( envs, tier ) ) {
  2058                structAppend( variables.framework, envs[ tier ] );
  2059            }
  2060            if ( structKeyExists( envs, env ) ) {
  2061                structAppend( variables.framework, envs[ env ] );
  2062            }
  2063        }
  2064        setupEnvironment( env );
  2065    }
  2066
  2067	private void function setupRequestDefaults() {
  2068        if ( !request._fw1.requestDefaultsInitialized ) {
  2069            var pathInfo = variables.cgiPathInfo;
  2070            request.base = variables.framework.base;
  2071            request.cfcbase = variables.framework.cfcbase;
  2072
  2073            if ( !structKeyExists(request, 'context') ) {
  2074                request.context = { };
  2075            }
  2076            // SES URLs by popular request :)
  2077            if ( len( pathInfo ) > len( variables.cgiScriptName ) && left( pathInfo, len( variables.cgiScriptName ) ) == variables.cgiScriptName ) {
  2078                // canonicalize for IIS:
  2079                pathInfo = right( pathInfo, len( pathInfo ) - len( variables.cgiScriptName ) );
  2080            } else if ( len( pathInfo ) > 0 && pathInfo == left( variables.cgiScriptName, len( pathInfo ) ) ) {
  2081                // pathInfo is bogus so ignore it:
  2082                pathInfo = '';
  2083            }
  2084            var routeMatch = processRoutes( pathInfo );
  2085						if ( routeMatch.matched ) {
  2086							pathInfo = rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target );
  2087							if ( routeMatch.redirect ) {
  2088								location( pathInfo, false, routeMatch.statusCode ); 
  2089							} else {
  2090								request._fw1.route = routeMatch.route;
  2091							}
  2092						}
  2093            try {
  2094                // we use .split() to handle empty items in pathInfo - we fallback to listToArray() on
  2095                // any system that doesn't support .split() just in case (empty items won't work there!)
  2096                if ( len( pathInfo ) > 1 ) {
  2097                    // Strip leading "/" if present.
  2098                    if ( left( pathInfo, 1 ) EQ '/' ) {
  2099                        pathInfo = right( pathInfo, len( pathInfo ) - 1 );
  2100                    }
  2101                    pathInfo = pathInfo.split( '/' );
  2102                } else {
  2103                    pathInfo = arrayNew( 1 );
  2104                }
  2105            } catch ( any exception ) {
  2106                pathInfo = listToArray( pathInfo, '/' );
  2107            }
  2108            var sesN = arrayLen( pathInfo );
  2109            if ( ( sesN > 0 || variables.framework.generateSES ) && getBaseURL() != 'useRequestURI' ) {
  2110                request._fw1.generateSES = true;
  2111            }
  2112            for ( var sesIx = 1; sesIx <= sesN; sesIx = sesIx + 1 ) {
  2113                if ( sesIx == 1 ) {
  2114                    request.context[variables.framework.action] = pathInfo[sesIx];
  2115                } else if ( sesIx == 2 ) {
  2116                    request.context[variables.framework.action] = pathInfo[sesIx-1] & '.' & pathInfo[sesIx];
  2117                } else if ( sesIx mod 2 == 1 ) {
  2118                    request.context[ pathInfo[sesIx] ] = '';
  2119                } else {
  2120                    request.context[ pathInfo[sesIx-1] ] = pathInfo[sesIx];
  2121                }
  2122            }
  2123            // certain remote calls do not have URL or form scope:
  2124            if ( isDefined('URL') ) structAppend(request.context,URL);
  2125            if ( isDefined('form') ) structAppend(request.context,form);
  2126            // figure out the request action before restoring flash context:
  2127            if ( !structKeyExists(request.context, variables.framework.action) ) {
  2128                request.context[variables.framework.action] = variables.framework.home;
  2129            } else {
  2130                request.context[variables.framework.action] = getFullyQualifiedAction( request.context[variables.framework.action] );
  2131            }
  2132            if ( variables.framework.noLowerCase ) {
  2133                request.action = validateAction( request.context[variables.framework.action] );
  2134            } else {
  2135                request.action = validateAction( lCase(request.context[variables.framework.action]) );
  2136            }
  2137            request._fw1.requestDefaultsInitialized = true;
  2138        }
  2139	}
  2140
  2141	private void function setupRequestWrapper( boolean runSetup ) {
  2142
  2143		request.subsystem = getSubsystem( request.action );
  2144		request.subsystembase = request.base & getSubsystemDirPrefix( request.subsystem );
  2145		request.section = getSection( request.action );
  2146		request.item = getItem( request.action );
  2147		
  2148		if ( runSetup ) {
  2149			rc = request.context;
  2150            if ( usingSubsystems() ) {
  2151			    controller( variables.magicApplicationSubsystem & variables.framework.subsystemDelimiter &
  2152                            variables.magicApplicationController & '.' & variables.magicApplicationAction );
  2153            } else {
  2154			    controller( variables.magicApplicationController & '.' & variables.magicApplicationAction );
  2155            }
  2156			setupSubsystemWrapper( request.subsystem );
  2157            frameworkTrace( 'setupRequest() called' );
  2158			setupRequest();
  2159		}
  2160
  2161		controller( request.action );
  2162		if ( !variables.framework.suppressImplicitService ) {
  2163			service( request.action, getServiceKey( request.action ), { }, false );
  2164		}
  2165	}
  2166
  2167	private void function setupResponseWrapper() {
  2168        frameworkTrace( 'setupResponse() called' );
  2169		setupResponse();
  2170	}
  2171
  2172	private void function setupSessionWrapper() {
  2173        frameworkTrace( 'setupSession() called' );
  2174		setupSession();
  2175	}
  2176
  2177	private void function setupSubsystemWrapper( string subsystem ) {
  2178		if ( !usingSubsystems() ) return;
  2179		lock name="fw1_#application.applicationName#_#variables.framework.applicationKey#_subsysteminit_#subsystem#" type="exclusive" timeout="30" {
  2180			if ( !isSubsystemInitialized( subsystem ) ) {
  2181				application[ variables.framework.applicationKey ].subsystems[ subsystem ] = now();
  2182                frameworkTrace( 'setupSubsystem() called', subsystem );
  2183				setupSubsystem( subsystem );
  2184			}
  2185		}
  2186	}
  2187
  2188	private string function validateAction( string action ) {
  2189		// check for forward and backward slash in the action - using chr() to avoid confusing TextMate (Hi Nathan!)
  2190		if ( findOneOf( chr(47) & chr(92), action ) > 0 ) {
  2191			raiseException( type='FW1.actionContainsSlash', message="Found a slash in the action: '#action#'.",
  2192					detail='Actions are not allowed to embed sub-directory paths.');
  2193		}
  2194		return action;
  2195	}
  2196
  2197	private void function viewNotFound() {
  2198		raiseException( type='FW1.viewNotFound', message="Unable to find a view for '#request.action#' action.",
  2199				detail="'#request.missingView#' does not exist." );
  2200	}
  2201	
  2202}