root / framework / branches / dev / fuseboxApplication.cfc

Revision 452, 55.8 kB (checked in by scorfield, 2 years ago)

Added detection of recursive dynamic do and some logging to debug it (since it doesn't
work properly right now).

  • Property svn:keywords set to LastChangedRevision
Line 
1<!---
2Copyright 2006 TeraTech, Inc. http://teratech.com/
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15--->
16<cfcomponent output="false" hint="I am the Fusebox application object, formerly the application.fusebox data structure.">
17
18        <cfscript>
19        // initialize the fusebox (available to be read by developers but not to be written to)
20        this.isFullyLoaded = false;
21        this.circuits = structNew();
22        this.classes = structNew();
23        this.lexicons = structNew();
24        this.plugins = structNew();
25        this.pluginphases = structNew();
26        this.nonFatalExceptionPrefix = "INFORMATION (can be ignored): ";
27
28        // this always has to be overridden:
29        this.defaultFuseaction = "";
30
31        this.precedenceFormOrURL = "form";
32        this.fuseactionVariable = "fuseaction";
33
34        // these are all ignored:
35        this.parseWithComments = false;
36        this.ignoreBadGrammar = true;
37        this.allowLexicon = true;
38        this.useAssertions = true;
39        this.conditionalParse = false;
40       
41        this.password = "";
42        this.mode = "production";
43        this.scriptLanguage = "cfmx";
44        this.scriptFileDelimiter = "cfm";
45        this.maskedFileDelimiters = "htm,cfm,cfml,php,php4,asp,aspx";
46        this.characterEncoding = "utf-8";
47        // this is ignored:
48        this.parseWithIndentation = this.parseWithComments;
49        this.strictMode = false;
50        this.allowImplicitFusebox = false;
51        this.allowImplicitCircuits = false;
52        this.debug = false;
53        </cfscript>
54       
55        <cffunction name="init" returntype="fuseboxApplication" access="public" output="false"
56                                hint="I am the constructor.">
57                <cfargument name="appKey" type="string" required="true"
58                                        hint="I am FUSEBOX_APPLICATION_KEY." />
59                <cfargument name="appPath" type="string" required="true"
60                                        hint="I am FUSEBOX_APPLICATION_PATH." />
61                <cfargument name="myFusebox" type="myFusebox" required="true"
62                                        hint="I am the myFusebox data structure." />
63                <cfargument name="callerPath" type="string" required="true"
64                                        hint="I am FUSEBOX_CALLER_PATH." />
65                <cfargument name="appParameters" type="struct" required="true"
66                                        hint="I am FUSEBOX_PARAMETERS." />
67               
68                <!--- tidied up circuit-as-cfc --->
69                <cfset var myVersion = "6.0.0.#REReplace('$LastChangedRevision$','[^0-9]','','all')#" />
70
71                <cfset variables.factory = createObject("component","fuseboxFactory").init() />
72                <cfset variables.fuseboxLexicon = variables.factory.getBuiltinLexicon() />
73                <cfset variables.customAttributes = structNew() />
74               
75                <cfset variables.fuseboxFileExtension = "" />
76
77                <cfset variables.fuseboxVersion = myVersion />
78               
79                <cfset variables.appKey = arguments.appKey />
80                <cfset this.webrootdirectory = replace(getDirectoryFromPath(getBaseTemplatePath()),"\","/","all") />
81                <cfset variables.coreRoot = replace(getDirectoryFromPath(getCurrentTemplatePath()),"\","/","all") />
82
83                <cfset this.approotdirectory = getCanonicalPath(normalizePartialPath(arguments.callerPath) & normalizePartialPath(arguments.appPath)) />
84
85                <!--- this works on all platforms: --->
86                <cfset this.osdelimiter = "/" />
87
88                <cfset this.coreToAppRootPath = relativePath(variables.coreRoot,this.approotdirectory) />
89                <cfset this.appRootPathToCore = relativePath(this.approotdirectory,variables.coreRoot) />
90                <cfset this.coreToWebRootPath = relativePath(variables.coreRoot,this.webrootdirectory) />
91                <cfset this.WebRootPathToCore = relativePath(this.webrootdirectory,variables.coreRoot) />
92               
93                <cfset this.parsePath = "parsed/" />
94                <cfset this.parseRootPath = "../" />
95                <cfset this.pluginsPath = "plugins/" />
96                <cfset this.lexiconPath = "lexicon/" />
97                <cfset this.errortemplatesPath = "errortemplates/" />
98               
99                <!--- new in Fusebox 5.1: --->
100                <cfset this.self = "index.cfm" />
101                <cfset this.queryStringStart = "?" />
102                <cfset this.queryStringSeparator = "&" />
103                <cfset this.queryStringEqual = "=" />
104               
105                <cfset this.circuits = structNew() />
106                <cfset reload(arguments.appKey,arguments.appPath,arguments.myFusebox,arguments.appParameters) />
107
108                <cfif this.strictMode>
109                        <!--- rootdirectory was deprecated in Fusebox 5 so we no longer set it it strict mode: --->
110                        <cfset structDelete(this,"rootdirectory") />
111                <cfelse>
112                        <!--- for FB4.0 compatibility: --->
113                        <cfset this.rootdirectory = this.approotdirectory />
114                </cfif>
115               
116                <cfreturn this />
117
118        </cffunction>
119
120        <cffunction name="reload" returntype="void" access="public" output="false"
121                                hint="I (re)load the fusebox.xml file into memory and (re)load all of the application components referenced by that.">
122                <cfargument name="appKey" type="string" required="true"
123                                        hint="I am FUSEBOX_APPLICATION_KEY." />
124                <cfargument name="appPath" type="string" required="true"
125                                        hint="I am FUSEBOX_APPLICATION_PATH." />
126                <cfargument name="myFusebox" type="myFusebox" required="true"
127                                        hint="I am the myFusebox data structure." />
128                <cfargument name="appParameters" type="struct" required="true"
129                                        hint="I am FUSEBOX_PARAMETERS." />
130               
131                <cfset var fbFile = "fusebox.xml.cfm" />
132                <cfset var fbFileAlt = "fusebox.xml" />
133                <cfset var fbFileExists = false />
134                <cfset var fbXML = "" />
135                <cfset var fbCode = "" />
136                <cfset var encodings = 0 />
137                <cfset var needToLoad = true />
138                <cfset var fuseboxFiles = 0 />
139                <cfset var myFuseboxFilePath = "" />
140                <cfset var jFuseboxFile = "" />
141                <cfset var dtLastModified = "" />
142                <cfset var p = "" />
143               
144                <!--- FB6: can override fusebox.xml parameters programmatically --->
145                <!--- this sets up the defaults for the reload - prior to actually reading parameters --->
146                <cfloop collection="#arguments.appParameters#" item="p">
147                        <cfset this[p] = arguments.appParameters[p] />
148                </cfloop>
149
150                <!---
151                        since we need to check the file, regardless of whether we load it,
152                        we might as well do the test up front and perform the strict check
153                        that just one version exists (ticket 135)
154                --->
155                <cfset fbFileExists = fileExists(this.approotdirectory & fbFile) />
156                <cfif fbFileExists>
157                        <cfif this.strictMode and fileExists(this.approotdirectory & fbFileAlt)>
158                                <cfthrow type="fusebox.multipleFuseboxXML"
159                                                message="Both 'fusebox.xml' and 'fusebox.xml.cfm' exist"
160                                                detail="'fusebox.xml.cfm' will be used but 'fusebox.xml' also exists in '#this.approotdirectory#." />
161                        </cfif>
162                <cfelse>
163                        <cfset fbFile = fbFileAlt />
164                        <cfset fbFileExists = fileExists(this.approotdirectory & fbFile) />
165                </cfif>
166
167                <cfif structKeyExists(this,"timestamp")>
168                        <cfif fbFileExists>
169                                <!--- we only check the time stamp if the file exists --->
170                                <!--- Java timestamp solution provided by Daniel Schmid --->
171                                <cfset myFuseboxFilePath = this.approotdirectory & fbFile />
172                                <cfset jFuseboxFile = createObject("java","java.io.File").init(myFuseboxFilePath) />
173                                <cfset dtLastModified = createObject("java","java.util.Date").init(jFuseboxFile.lastModified()) />
174                                <cfset needToLoad = parseDateTime(dtLastModified) gt parseDateTime(this.timestamp) />
175                        <cfelse>
176                                <!---
177                                        if we loaded the application and there is no fusebox.xml file,
178                                        then we must have loaded an implicit fusebox.xml file so there
179                                        is no need to reload it again because an implicit file can't change!
180                                --->
181                                <cfset needToLoad = false />
182                        </cfif>
183                </cfif>
184
185                <cfif needToLoad>
186
187                        <cfif this.debug>
188                                <cfset arguments.myFusebox.trace("Compiler","Loading fusebox.xml file") />
189                        </cfif>
190
191                        <!--- attempt to load fusebox.xml(.cfm): --->
192                        <cfif fbFileExists>
193                                <cftry>
194                                       
195                                        <cffile action="read" file="#this.approotdirectory##fbFile#"
196                                                        variable="fbXML"
197                                                        charset="#this.characterEncoding#" />
198                                                       
199                                        <cfset variables.fuseboxFileExtension = listLast(fbFile,".") />
200                                       
201                                        <cfcatch type="any">
202                                                <!--- the file exists but we cannot read it --->
203                                                <cfthrow type="fusebox.missingFuseboxXML"
204                                                                message="missing fusebox.xml"
205                                                                detail="The file '#fbFile#' in the directory #this.approotdirectory# could not be read."
206                                                                extendedinfo="#cfcatch.detail#" />
207                                        </cfcatch>
208                                       
209                                </cftry>
210
211                        <cfelse>
212
213                                <cfif this.allowImplicitFusebox>
214
215                                        <cfif this.debug>
216                                                <cfset arguments.myFusebox.trace("Compiler","Implicit fusebox.xml(.cfm) identified") />
217                                        </cfif>
218                                        <cfset fbXML = "<fusebox/>" />
219
220                                <cfelse>
221
222                                        <cfthrow type="fusebox.missingFuseboxXML"
223                                                        message="missing fusebox.xml"
224                                                        detail="The file '#fbFile#' could not be found in the directory #this.approotdirectory#."
225                                                        extendedinfo="#cfcatch.detail#" />
226
227                                </cfif>
228
229                        </cfif>
230                       
231                        <cftry>
232                               
233                                <cfset fbCode = xmlParse(fbXML) />
234                               
235                                <!--- see if we need to re-read based on the encoding being different to our default --->
236                                <cfset encodings = xmlSearch(fbCode,"/fusebox/parameters/parameter[@name='characterEncoding']") />
237                                <cfif arrayLen(encodings) eq 1 and structKeyExists(encodings[1].xmlAttributes,"value")>
238                                        <cfif encodings[1].xmlAttributes.value is not this.characterEncoding>
239                                                <cfset this.characterEncoding = encodings[1].xmlAttributes.value />
240                                                <!--- now re-read the file in case anything is changed in that new encoding --->
241                                                <cffile action="read" file="#this.approotdirectory##fbFile#"
242                                                                variable="fbXML"
243                                                                charset="#this.characterEncoding#" />
244                                                <cfset fbCode = xmlParse(fbXML) />
245                                        </cfif>
246                                </cfif>
247                               
248                                <cfcatch type="any">
249                                        <cfthrow type="fusebox.fuseboxXMLError"
250                                                        message="Error reading fusebox.xml"
251                                                        detail="A problem was encountered while reading the #fbFile# file. This is usually caused by unmatched XML tags (a &lt;tag&gt; without a &lt;/tag&gt; or without use of the &lt;tag/&gt; short-cut.)"
252                                                        extendedinfo="#cfcatch.detail#" />
253                                </cfcatch>
254                               
255                        </cftry>
256                       
257                        <cfif fbCode.xmlRoot.xmlName is not "fusebox">
258                                <cfthrow type="fusebox.badGrammar.badFuseboxFile"
259                                                detail="Fusebox file does contain 'fusebox' XML"
260                                                message="Fusebox file #fbFile# does not contain 'fusebox' as the root XML node." />
261                        </cfif>
262
263                        <cfset loadParameters(fbCode) />
264
265                        <!--- FB6: can override fusebox.xml parameters programmatically --->
266                        <cfloop collection="#arguments.appParameters#" item="p">
267                                <cfset this[p] = arguments.appParameters[p] />
268                        </cfloop>
269
270                        <cfset loadLexicons(fbCode) />
271                        <cfset loadClasses(fbCode) />
272                        <cfset loadPlugins(fbCode) />
273                        <cfset loadGlobalPreAndPostProcess(fbCode) />
274                        <!--- save fusebox.xml DOM internally for (re-)loading circuits --->
275                        <cfset variables.fbCode = fbCode />
276                        <cfset variables.fbFile = fbFile />
277                        <cfset this.timestamp = now() />
278                </cfif>
279               
280                <!--- to track circuit loads on this request --->
281                <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />             
282                <cfset loadCircuits(variables.fbCode,arguments.myFusebox) />
283               
284                <!--- FB5: fusebox.loadclean will delete all the parsed files --->
285                <cfif arguments.myFusebox.parameters.clean>
286                        <cfset deleteParsedFiles() />
287                </cfif>
288               
289                <!--- application data available to developers via getApplicationData() method: --->
290                <cfset variables.data = structNew() />
291               
292                <cfset this.isFullyLoaded = true />
293                <cfset this.applicationStarted = false />
294                <cfset this.dateLastLoaded = now() />
295               
296                <!---
297                        The following documented parts of application.fusebox are not supported in Fusebox 5:
298                        - application.fusebox.xml
299                        - application.fusebox.globalfuseactions.*
300                        - application.fusebox.circuits.*.xml
301                        - application.fusebox.circuits.preFuseaction.*
302                        - application.fusebox.circuits.postFuseaction.*
303                        - application.fusebox.circuits.*.fuseactions.*.xml
304                --->
305               
306        </cffunction>
307       
308        <cffunction name="getPluginsPath" returntype="string" access="public" output="false"
309                                hint="I am a convenience method to return the location of the plugins.">
310       
311                <cfreturn this.pluginsPath />
312       
313        </cffunction>
314       
315        <cffunction name="getApplicationData" returntype="struct" access="public" output="false"
316                                hint="I return a reference to the application data cache. This is a new concept in Fusebox 5.">
317       
318                <cfreturn variables.data />
319       
320        </cffunction>
321       
322        <cffunction name="getApplicationRoot" returntype="any" access="public" output="false"
323                                hint="I am a convenience method to return the full application root directory path.">
324       
325                <cfreturn this.approotdirectory />
326       
327        </cffunction>
328       
329        <cffunction name="expandFuseboxPath" returntype="any" access="public" output="false"
330                                hint="I expand a path in the context of a Fusebox application.">
331                <cfargument name="partialPath" type="any" required="true"
332                                        hint="I am the partial path to expand. If I do not begin with a /, prepend the Fusebox application root." />
333
334                <cfif left(arguments.partialPath,1) is "/">
335                        <!--- absolute path, i.e., root-relative or mapped --->
336                        <cfreturn replace(expandPath(arguments.partialPath),"\","/","all") />
337                <cfelse>
338                        <!--- relative, i.e., relative to the Fusebox application root --->
339                        <cfreturn getApplicationRoot() & arguments.partialPath />
340                </cfif>
341               
342        </cffunction>
343       
344        <cffunction name="getFuseboxXMLFilename" returntype="string" access="public" output="false"
345                                hint="I return the actual name of the fusebox.xml(.cfm) file.">
346       
347                <cfreturn variables.fbFile />
348       
349        </cffunction>
350       
351        <cffunction name="getCoreToAppRootPath" returntype="any" access="public" output="false"
352                                hint="I am a convenience method to return the relative path from the core files to the application root.">
353       
354                <cfreturn this.coreToAppRootPath />
355       
356        </cffunction>
357       
358        <cffunction name="compileAll" returntype="void" access="public" output="false"
359                                hint="I compile all the public fuseactions in the application.">
360                <cfargument name="myFusebox" type="myFusebox" required="true"
361                                        hint="I am the myFusebox data structure." />
362
363                <cfset var c = 0 />
364                <cfset var a = 0 />
365                <cfset var f = 0 />
366       
367                <cfloop collection="#this.circuits#" item="c">
368                        <cfset a = this.circuits[c].getFuseactions() />
369                        <cfloop collection="#a#" item="f">
370                                <cfif a[f].access is "public">
371                                        <cfset compileRequest(c & "." & f,arguments.myFusebox) />
372                                </cfif>
373                        </cfloop>
374                </cfloop>
375
376        </cffunction>
377       
378        <cffunction name="compileRequest" returntype="struct" access="public" output="false"
379                                hint="I compile a specific (public) fuseaction as an external request.">
380                <cfargument name="circuitFuseaction" type="string" required="true"
381                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
382                <cfargument name="myFusebox" type="myFusebox" required="true"
383                                        hint="I am the myFusebox data structure." />
384
385                <cfset var myVersion = getVersion() />
386                <cfset var circuit = listFirst(arguments.circuitFuseaction,".") />
387                <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
388                <cfset var i = 0 />
389                <cfset var n = 0 />
390                <cfset var needRethrow = true />
391                <cfset var needTryOnFuseaction = false />
392                <cfset var parsedName = "#lCase(arguments.circuitFuseaction)#.cfm" />
393                <cfset var parsedFile = "#this.parsePath##parsedName#" />
394                <cfset var fullParsedFile = "#this.expandFuseboxPath(this.parsePath)##parsedName#" />
395                <cfset var result = structNew() />
396                <cfset var writer = 0 />
397               
398                <!--- validate format of the fuseaction: --->
399                <cfif listLen(arguments.circuitFuseaction,".") neq 2>
400                        <cfthrow type="fusebox.malformedFuseaction"
401                                        message="malformed Fuseaction"
402                                        detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />     
403                </cfif>
404               
405                <!--- to track reloads on this request --->
406                <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />
407                <cfparam name="request.__fusebox.fuseactionsDone" default="#structNew()#" />
408               
409                <!--- set up myFusebox values for this request: --->
410                <cfset arguments.myFusebox.originalCircuit = circuit />
411                <cfset arguments.myFusebox.originalFuseaction = fuseaction />
412                <cfloop collection="#this.plugins#" item="i">
413                        <cfset arguments.myFusebox.plugins[i] = structNew() />
414                </cfloop>
415
416                <!--- note that in Fusebox 5, these are really all the same set of files --->
417                <cfset arguments.myFusebox.version.loader = myVersion />
418                <cfset arguments.myFusebox.version.parser = myVersion />
419                <cfset arguments.myFusebox.version.transformer = myVersion />
420                <!--- legacy test from FB41 although it's a bit pointless --->
421                <cfif myFusebox.version.runtime neq myFusebox.version.loader>
422                        <cfthrow type="fusebox.versionMismatchException"
423                                        message="The loader is not the same version as the runtime" />
424                </cfif>
425               
426                <!--- check access on request - if the circuit/fuseaction doesn't exist we trap it later --->
427                <cfif structKeyExists(this.circuits,circuit) and
428                                structKeyExists(this.circuits[circuit].fuseactions,fuseaction) and
429                                this.circuits[circuit].fuseactions[fuseaction].getAccess() is not "public">
430                        <cfthrow type="fusebox.invalidAccessModifier"
431                                        message="Invalid Access Modifier"
432                                        detail="You tried to access #circuit#.#fuseaction# which does not have access modifier of public. A Fuseaction which is to be accessed from anywhere outside the application (such as called via an URL, or a FORM, or as a web service) must have an access modifier of public or if unspecified at least inherit such a modifier from its circuit.">
433                </cfif>
434               
435                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
436                        <cflock name="#fullParsedFile#" type="exclusive" timeout="300">
437                                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
438                                        <cfset request.__fusebox.SuppressPlugins = false />
439                                        <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
440                                        <cfset writer.open(parsedName) />
441                                        <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
442                                        <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
443                                        <cfset writer.rawPrintln("<cftry>") />
444                                        <cfset writer.setCircuit(circuit) />
445                                        <cfset writer.setFuseaction(fuseaction) />
446                                        <cfif variables.hasProcess["appinit"]>
447                                                <cfset writer.setPhase("appinit") />
448                                                <cfset writer.println("<cfif myFusebox.applicationStart>") />
449                                                <cfset writer.println(' <cfif not myFusebox.getApplication().applicationStarted>') />
450                                                <cfset writer.println('         <cflock name="##application.ApplicationName##_fusebox_##FUSEBOX_APPLICATION_KEY##_appinit" type="exclusive" timeout="30">') />
451                                                <cfset writer.println('                 <cfif not myFusebox.getApplication().applicationStarted>') />
452                                                <cfset request.__fusebox.SuppressPlugins = true />
453                                                <cfset variables.process["appinit"].compile(writer) />
454                                                <cfset writer.println('                         <cfset myFusebox.getApplication().applicationStarted = true />') />
455                                                <cfset writer.println('                 </cfif>') />
456                                                <cfset writer.println('         </cflock>') />
457                                                <cfset writer.println(' </cfif>') />
458                                                <cfset writer.println("</cfif>") />
459                                        </cfif>
460                                        <cfset request.__fusebox.SuppressPlugins = false />
461                                        <cfif structKeyExists(this.pluginPhases,"preProcess")>
462                                                <cfset n = arrayLen(this.pluginPhases["preProcess"]) />
463                                                <cfloop from="1" to="#n#" index="i">
464                                                        <cfset this.pluginPhases["preProcess"][i].compile(writer) />
465                                                </cfloop>
466                                        </cfif>
467                                        <cfset writer.setPhase("preprocessFuseactions") />
468                                        <cfif variables.hasProcess["preprocess"]>
469                                                <cfset variables.process["preprocess"].compile(writer) />
470                                        </cfif>
471                                        <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
472                                                        arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
473                                                        not request.__fusebox.SuppressPlugins>
474                                                <cfset needTryOnFuseaction = true />
475                                                <cfset writer.rawPrintln("<cftry>") />
476                                        </cfif>
477                                        <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
478                                                <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
479                                                <cfloop from="1" to="#n#" index="i">
480                                                        <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
481                                                </cfloop>
482                                        </cfif>
483                                        <cfset writer.setPhase("requestedFuseaction") />
484                                        <cfset compile(writer,circuit,fuseaction,true) />
485                                        <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
486                                                <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
487                                                <cfloop from="1" to="#n#" index="i">
488                                                        <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
489                                                </cfloop>
490                                        </cfif>
491                                        <cfif needTryOnFuseaction>
492                                                <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
493                                                <cfloop from="1" to="#n#" index="i">
494                                                        <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
495                                                </cfloop>
496                                                <cfset writer.rawPrintln("</cftry>") />
497                                        </cfif>
498                                        <cfset writer.setPhase("postprocessFuseactions") />
499                                        <cfif variables.hasProcess["postprocess"]>
500                                                <cfset variables.process["postprocess"].compile(writer) />
501                                        </cfif>
502                                        <cfif structKeyExists(this.pluginPhases,"postProcess")>
503                                                <cfset n = arrayLen(this.pluginPhases["postProcess"]) />
504                                                <cfloop from="1" to="#n#" index="i">
505                                                        <cfset this.pluginPhases["postProcess"][i].compile(writer) />
506                                                </cfloop>
507                                        </cfif>
508                                        <cfif structKeyExists(this.pluginPhases,"processError") and
509                                                        not request.__fusebox.SuppressPlugins>
510                                                <cfset n = arrayLen(this.pluginPhases["processError"]) />
511                                                <cfloop from="1" to="#n#" index="i">
512                                                        <cfset needRethrow = false />
513                                                        <cfset this.pluginPhases["processError"][i].compile(writer) />
514                                                </cfloop>
515                                        </cfif>
516                                        <cfif needRethrow>
517                                                <cfset writer.rawPrintln('<' & 'cfcatch><' & 'cfrethrow><' & '/cfcatch>') />
518                                        </cfif>
519                                        <cfset writer.rawPrintln("</cftry>") />
520                                        <cfset writer.close() />
521                                </cfif>
522                        </cflock>
523                </cfif>
524               
525                <cfset result.parsedName = parsedName />
526                <cfif left(parsedFile,1) is "/">
527                        <cfset result.parsedFile = parsedFile />
528                <cfelse>
529                        <cfset result.parsedFile = this.getCoreToAppRootPath() & parsedFile />
530                </cfif>
531                <cfset result.lockName = fullParsedFile />
532               
533                <cfreturn result />
534               
535        </cffunction>
536       
537        <cffunction name="do" returntype="void" access="public" output="true"
538                                hint="I compile and execute a specific fuseaction.">
539                <cfargument name="circuitFuseaction" type="string" required="true"
540                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
541                <cfargument name="myFusebox" type="myFusebox" required="true"
542                                        hint="I am the myFusebox data structure." />
543
544                <cfset var parsedFileInfo = 0 />
545               
546                <!--- allow for abbreviated circuitFuseaction form: --->
547                <cfif listLen(arguments.circuitFuseaction,".") neq 2>
548                        <cfset arguments.circuitFuseaction = arguments.myFusebox.thisCircuit & "." & arguments.circuitFuseaction />
549                </cfif>
550                <!--- TODO: check for accessibility --->
551               
552                <cflog text="dynamic do: #arguments.circuitFuseaction#" />
553                <cfset parsedFileInfo = compileDynamicDo(arguments.circuitFuseaction,arguments.myFusebox) />
554               
555                <cfif this.debug>
556                        <cfset arguments.myFusebox.trace("Runtime","&lt;do action=""#arguments.circuitFuseaction#""/&gt;") />
557                </cfif>
558                <cfinvoke component="fuseboxExecutionContext" method="__executeDynamicDo" parsedFileInfo="#parsedFileInfo#" myFusebox="#arguments.myFusebox#" />
559               
560        </cffunction>
561
562        <cffunction name="compileDynamicDo" returntype="struct" access="private" output="false"
563                                hint="I compile a specific fuseaction as a dynamic request.">
564                <cfargument name="circuitFuseaction" type="string" required="true"
565                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
566                <cfargument name="myFusebox" type="myFusebox" required="true"
567                                        hint="I am the myFusebox data structure." />
568
569                <cfset var circuit = listFirst(arguments.circuitFuseaction,".") />
570                <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
571                <cfset var i = 0 />
572                <cfset var n = 0 />
573                <cfset var needTryOnFuseaction = false />
574                <cfset var parsedName = "do.#lCase(arguments.circuitFuseaction)#.cfm" />
575                <cfset var parsedFile = "#this.parsePath##parsedName#" />
576                <cfset var fullParsedFile = "#this.expandFuseboxPath(this.parsePath)##parsedName#" />
577                <cfset var result = structNew() />
578                <cfset var writer = 0 />
579               
580                <!--- validate format of the fuseaction: --->
581                <cfif listLen(arguments.circuitFuseaction,".") neq 2>
582                        <cfthrow type="fusebox.malformedFuseaction"
583                                        message="malformed Fuseaction"
584                                        detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />     
585                </cfif>
586               
587                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
588                        <cflock name="#fullParsedFile#" type="exclusive" timeout="300">
589                                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
590                                        <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
591                                        <cfset writer.open(parsedName) />
592                                        <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
593                                        <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
594                                        <cfset writer.setCircuit(circuit) />
595                                        <cfset writer.setFuseaction(fuseaction) />
596                                        <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
597                                                        arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
598                                                        not request.__fusebox.SuppressPlugins>
599                                                <cfset needTryOnFuseaction = true />
600                                                <cfset writer.rawPrintln("<cftry>") />
601                                        </cfif>
602                                        <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
603                                                <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
604                                                <cfloop from="1" to="#n#" index="i">
605                                                        <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
606                                                </cfloop>
607                                        </cfif>
608                                        <cfset writer.setPhase("requestedFuseaction") />
609                                        <cfset compile(writer,circuit,fuseaction) />
610                                        <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
611                                                <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
612                                                <cfloop from="1" to="#n#" index="i">
613                                                        <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
614                                                </cfloop>
615                                        </cfif>
616                                        <cfif needTryOnFuseaction>
617                                                <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
618                                                <cfloop from="1" to="#n#" index="i">
619                                                        <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
620                                                </cfloop>
621                                                <cfset writer.rawPrintln("</cftry>") />
622                                        </cfif>
623                                        <cfset writer.close() />
624                                </cfif>
625                        </cflock>
626                </cfif>
627               
628                <cfset result.parsedName = parsedName />
629                <cfif left(parsedFile,1) is "/">
630                        <cfset result.parsedFile = parsedFile />
631                <cfelse>
632                        <cfset result.parsedFile = this.getCoreToAppRootPath() & parsedFile />
633                </cfif>
634                <cfset result.lockName = fullParsedFile />
635               
636                <cfreturn result />
637               
638        </cffunction>
639       
640        <cffunction name="compile" returntype="void" access="public" output="false"
641                                hint="I compile a specific fuseaction during a request (such as for a 'do' verb).">
642                <cfargument name="writer" type="any" required="false"
643                                        hint="I am the parsed file writer object. I am required but it's faster to specify that I am not required." />
644                <cfargument name="circuit" type="any" required="false"
645                                        hint="I am the circuit name. I am required but it's faster to specify that I am not required." />
646                <cfargument name="fuseaction" type="any" required="false"
647                                        hint="I am the fuseaction name, within the specified circuit." />
648                <cfargument name="topLevel" type="boolean" default="false"
649                                        hint="I specify whether or not this is a top-level (public) request." />
650       
651                <cfset var c = "" />
652
653                <cfif not structKeyExists(this.circuits,arguments.circuit)>
654                        <!--- FB6: attempt to create an implicit circuit --->
655                        <!--- TODO: there needs to be a mode to make this an undefined circuit --->
656                        <cfset this.circuits[arguments.circuit] =
657                                        createObject("component","fuseboxImplicitCircuit")
658                                                .init(this,arguments.circuit,arguments.writer.getMyFusebox()) />
659                </cfif>
660                <!--- FB5: development-circuit-load only reloads the requested circuit --->
661                <cfif this.mode is "development-circuit-load">
662                        <!--- FB5: ensure we only reload each circuit once per request --->
663                        <cfif not structKeyExists(request.__fusebox.CircuitsLoaded,arguments.circuit)>
664                                <cfset request.__fusebox.CircuitsLoaded[arguments.circuit] = true />
665                                <cfset this.circuits[arguments.circuit].reload(arguments.writer.getMyFusebox()) />
666                        </cfif>
667                </cfif>
668       
669                <cfset c = arguments.writer.setCircuit(arguments.circuit) />
670                <cfset this.circuits[arguments.circuit]
671                                .compile(arguments.writer,arguments.fuseaction,arguments.topLevel) />
672                <cfset arguments.writer.setCircuit(c) />
673               
674        </cffunction>
675       
676        <cffunction name="handleFuseboxException" returntype="boolean" access="public" output="true"
677                                hint="I attempt to handle a Fusebox exception by looking for a handler file in the errortemplates/ directory. I return true if I handle the exception, else I return false.">
678                <cfargument name="cfcatch" type="any" required="true"
679                                        hint="I am the original cfcatch structure from the exception that fusebox6.cfm caught." />
680                <cfargument name="attributes" type="struct" required="true"
681                                        hint="I am the attributes 'scope'." />
682                <cfargument name="myFusebox" type="any" required="true"
683                                        hint="I am the myFusebox object." />
684                <cfargument name="appKey" type="string" required="true"
685                                        hint="I am the application key object." />
686               
687                <cfset var __handled = false />
688                <cfset var __type = arguments.cfcatch.type />
689                <cfset var __ext = "." & this.scriptFileDelimiter />
690                <cfset var __errorFile = this.errortemplatesPath & __type & __ext />
691                <cfset var __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
692                <cfset var FUSEBOX_APPLICATION_KEY = arguments.appKey />
693               
694                <cfloop condition="not __handlerExists and len(__type) gt 0">
695                        <cfset __type = listDeleteAt(__type,listLen(__type,"."),".") />
696                        <cfset __errorFile = this.errortemplatesPath & __type & __ext />
697                        <cfset __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
698                </cfloop>
699                <cfif __handlerExists>
700                        <cfif left(__errorFile,1) is "/">
701                                <cfinclude template="#__errorFile#" />
702                        <cfelse>
703                                <cfinclude template="#getCoreToAppRootPath()##__errorFile#" />
704                        </cfif>
705                        <cfset __handled = true />
706                </cfif>
707               
708                <cfreturn __handled />
709               
710        </cffunction>
711       
712        <cffunction name="getFuseactionFactory" returntype="any" access="public" output="false"
713                                hint="I return the factory object that makes fuseaction objects for the framework.">
714               
715                <cfreturn variables.factory />
716
717        </cffunction>
718       
719        <cffunction name="getClassDefinition" returntype="struct" access="public" output="false"
720                                hint="I return the class declaration for a given class. I throw an exception if the class has no declaration.">
721                <cfargument name="className" type="string" required="true"
722                                        hint="I am the name of the class whose declaration should be returned." />
723               
724                <cfreturn this.classes[arguments.className] />
725
726        </cffunction>
727       
728        <cffunction name="getLexiconDefinition" returntype="any" access="public" output="false"
729                                hint="I return the lexicon definition for a given namespace. I return either the internal Fusebox lexicon or a declared (Fusebox 4.1 style) lexicon.">
730                <cfargument name="namespace" type="any" required="false"
731                                        hint="I am the namespace of the lexicon whose definition should be returned. I am required but it's faster to specify that I am not required." />
732               
733                <cfif arguments.namespace is variables.fuseboxLexicon.namespace>
734                        <cfreturn variables.fuseboxLexicon />
735                <cfelse>
736                        <cfreturn variables.fb41Lexicons[arguments.namespace] />
737                </cfif>
738
739        </cffunction>
740       
741        <cffunction name="getVersion" returntype="string" access="public" output="false"
742                                hint="I return the version of this Fusebox 5 object. This is the preferred way to obtain the version in Fusebox 5.">
743       
744                <cfreturn variables.fuseboxVersion />
745               
746        </cffunction>
747       
748        <cffunction name="getAlias" returntype="any" access="public" output="false"
749                                hint="I return the fake circuit alias for the application.">
750       
751                <cfreturn "$fusebox" />
752       
753        </cffunction>
754       
755        <cffunction name="getApplication" returntype="any" access="public" output="false"
756                                hint="I return the fusebox application object.">
757       
758                <cfreturn this />
759       
760        </cffunction>
761       
762        <cffunction name="getCustomAttributes" returntype="struct" access="public" output="false"
763                                hint="I return any custom attributes for the specified namespace prefix.">
764                <cfargument name="ns" type="string" required="true"
765                                        hint="I am the namespace for which to return custom attributes." />
766               
767                <cfif structKeyExists(variables.customAttributes,arguments.ns)>
768                        <!--- we structCopy() this so folks can't poke values back into the metadata! --->
769                        <cfreturn structCopy(variables.customAttributes[arguments.ns]) />
770                <cfelse>
771                        <cfreturn structNew() />
772                </cfif>
773               
774        </cffunction>
775       
776        <cffunction name="getFuseboxFileExtension" returntype="string" access="public" output="false"
777                                hint="I return the fusebox.xml file extension: either xml or cfm.">
778                                       
779                <cfreturn variables.fuseboxFileExtension />
780               
781        </cffunction>
782       
783        <cffunction name="deleteParsedFiles" returntype="void" access="private" output="false"
784                                hint="I delete all the script files in the parsed/ directory.">
785       
786                <cfset var fileQuery = 0 />
787                <cfset var parseDir = expandFuseboxPath(this.parsePath) />
788               
789                <cftry>
790                        <cfdirectory action="list" directory="#parseDir#"
791                                                filter="*.#this.scriptFileDelimiter#" name="fileQuery" />
792                        <cfloop query="fileQuery">
793                                <cffile action="delete" file="#parseDir##fileQuery.name#" />
794                        </cfloop>
795                <cfcatch />
796                </cftry>
797       
798        </cffunction>
799       
800        <cffunction name="loadCircuits" returntype="void" access="private" output="false"
801                                hint="I (re)load all the circuits in an application.">
802                <cfargument name="fbCode" type="any" required="true"
803                                        hint="I am the parsed XML representation of the fusebox.xml file." />
804                <cfargument name="myFusebox" type="myFusebox" required="true"
805                                        hint="I am the myFusebox data structure." />
806               
807                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/circuits/circuit") />
808                <cfset var i = 0 />
809                <cfset var n = arrayLen(children) />
810                <cfset var previousCircuits = this.circuits />
811                <cfset var alias = "" />
812                <cfset var parent = "" />
813                <cfset var relative = true />
814                <cfset var nAttrs = 0 />
815               
816                <cfset this.circuits = structNew() />
817               
818                <!--- pass 1: build the circuits --->
819                <cfloop from="1" to="#n#" index="i">
820                        <cfif not structKeyExists(children[i].xmlAttributes,"alias")>
821                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
822                                                message="Required attribute is missing"
823                                                detail="The attribute 'alias' is required, for a 'circuit' declaration in fusebox.xml." />
824                        </cfif>
825                        <cfif not structKeyExists(children[i].xmlAttributes,"path")>
826                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
827                                                message="Required attribute is missing"
828                                                detail="The attribute 'path' is required, for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
829                        </cfif>
830                        <cfif structKeyExists(children[i].xmlAttributes,"parent")>
831                                <cfset parent = children[i].xmlAttributes.parent />
832                                <cfset nAttrs = 3 />
833                        <cfelse>
834                                <cfset parent = "" />
835                                <cfset nAttrs = 2 />
836                        </cfif>
837                        <cfif structKeyExists(children[i].xmlAttributes,"relative")>
838                                <cfif listFind("true,false,yes,no",children[i].xmlAttributes.relative) eq 0>
839                                        <cfthrow type="fusebox.badGrammar.invalidAttributeValue"
840                                                        message="Attribute has invalid value"
841                                                        detail="The attribute 'relative' must either be ""true"" or ""false"", for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
842                                </cfif>
843                                <cfset relative = children[i].xmlAttributes.relative />
844                                <cfset nAttrs = nAttrs + 1 />
845                        <cfelse>
846                                <cfset relative = true />
847                        </cfif>
848                        <cfif this.strictMode and nAttrs neq structCount(children[i].xmlAttributes)>
849                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
850                                                message="Unexpected attributes"
851                                                detail="Attributes other than 'alias', 'path' and 'parent' were found for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
852                        </cfif>
853                        <cfset alias = children[i].xmlAttributes.alias />
854                        <!--- record each circuit load per request - optimization for development-circuit-load mode --->
855                        <cfset request.__fusebox.CircuitsLoaded[alias] = true />
856                        <cfif structKeyExists(previousCircuits,alias) and
857                                        children[i].xmlAttributes.path is previousCircuits[alias].getOriginalPath() and
858                                        parent is previousCircuits[alias].parent and
859                                        relative eq previousCircuits[alias].getOriginalPathIsRelative()>
860                                <!--- old circuit, we can just reload it --->
861                                <cfset this.circuits[alias] = previousCircuits[alias].reload(arguments.myFusebox) />
862                        <cfelse>
863                                <!--- new circuit, we must create it from scratch --->
864                                <cfset this.circuits[alias] =
865                                                createObject("component","fuseboxCircuit")
866                                                        .init(this,alias,children[i].xmlAttributes.path,parent,arguments.myFusebox,relative) />
867                        </cfif>
868                </cfloop>
869               
870                <!--- pass 2: build the circuit trace for each circuit --->
871                <cfloop collection="#this.circuits#" item="i">
872                        <cfset this.circuits[i].buildCircuitTrace() />
873                </cfloop>
874               
875        </cffunction>
876       
877        <cffunction name="loadLexicons" returntype="void" access="private" output="false"
878                                hint="I load any lexicon declarations (both the Fusebox 4.1 style lexicon declarations and the Fusebox 5 style namespace declarations).">
879                <cfargument name="fbCode" type="any" required="true"
880                                        hint="I am the parsed XML representation of the fusebox.xml file." />
881               
882                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/lexicons/lexicon") />
883                <cfset var i = 0 />
884                <cfset var n = arrayLen(children) />
885                <cfset var aLex = "" />
886                <cfset var attributes = arguments.fbCode.xmlRoot.xmlAttributes />
887                <cfset var attr = "" />
888                <cfset var ns = "" />
889               
890                <cfif n gt 0 and this.strictMode>
891                        <cfthrow type="fusebox.badGrammar.deprecated"
892                                        message="Deprecated feature"
893                                        detail="Using the 'lexicon' declaration in fusebox.xml was deprecated in Fusebox 5." />
894                </cfif>
895
896                <!--- load the legacy FB41 lexicons from the XML --->
897                <cfset variables.fb41Lexicons = structNew() />
898               
899                <cfloop from="1" to="#n#" index="i">
900                        <cfset aLex = structNew() />
901                        <cfset aLex.namespace = children[i].xmlAttributes.namespace />
902                        <cfset aLex.path = normalizePartialPath(children[i].xmlAttributes.path) />
903                        <!--- FB41 lexicons are deprecated so we don't support absolute / mapped paths: --->
904                        <cfset aLex.path = getCor