- qpscanner/framework.cfc
- 82 KB
- 2202
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}