root / framework / trunk / corefiles / fuseboxApplication.cfc

Revision 755, 63.0 kB (checked in by a.haskell@…, 2 months ago)

Added explicit No XML and alias path finding for xml circuits. I need to make tickets for this stuff....and get mylyn working.

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