root / framework / tags / fusebox550 / fuseboxApplication.cfc

Revision 663, 59.2 kB (checked in by scorfield, 1 year ago)

Fixes #304 by tightening up skeleton app code for initialization and application naming. Also makes a
slight change to appinit to allow for the framework being initialized via a remote CFC call. It is not
an ideal change but it solves the basic problem.

  • 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.0" />
71                <!--- <cfset var myVersion = "5.5.0.#REReplace('$LastChangedRevision$','[^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                                       
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
586                <!--- ticket 293 - prevent dynamic do() from executing until prerequisites complete --->
587                <cfif not structKeyExists(request,"__fusebox") or
588                                not structKeyExists(request.__fusebox,"dynamicDoOK") or
589                                        not request.__fusebox.dynamicDoOK>
590                        <cfif structKeyExists(arguments.myFusebox,"originalCircuit") and
591                                        structKeyExists(arguments.myFusebox,"originalFuseaction")>
592                                <cfset output = output & " while executing " & arguments.myFusebox.originalCircuit &
593                                                "." & arguments.myFusebox.originalFuseaction />
594                        </cfif>
595                        <cfthrow type="fusebox.dynamicDoNotAllowed" message="dynamic 'do' not allowed"
596                                        detail="This request did not reach the state where dynamic 'do' is possible#output#." />
597                </cfif>
598               
599                <!--- allow for abbreviated circuitFuseaction form: --->
600                <cfif listLen(arguments.circuitFuseaction,".") lt 2>
601                        <cfset arguments.circuitFuseaction = arguments.myFusebox.thisCircuit & "." & arguments.circuitFuseaction />
602                </cfif>
603                <!--- TODO: check for accessibility --->
604               
605                <cfset parsedFileInfo = compileDynamicDo(arguments.circuitFuseaction,arguments.myFusebox) />
606               
607                <cfif this.debug>
608                        <cfset arguments.myFusebox.trace("Runtime","&lt;do action=""#arguments.circuitFuseaction#""/&gt; -- dynamic") />
609                </cfif>
610                <cfinvoke component="fuseboxExecutionContext" method="__executeDynamicDo" returnvariable="output"
611                                        parsedFileInfo="#parsedFileInfo#" myFusebox="#arguments.myFusebox#" returnOutput="#arguments.returnOutput#" />
612               
613                <cfreturn output />
614               
615        </cffunction>
616
617        <cffunction name="compileDynamicDo" returntype="struct" access="private" output="false"
618                                hint="I compile a specific fuseaction as a dynamic request.">
619                <cfargument name="circuitFuseaction" type="string" required="true"
620                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
621                <cfargument name="myFusebox" type="myFusebox" required="true"
622                                        hint="I am the myFusebox data structure." />
623
624                <cfset var circuit = "" />
625                <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
626                <cfset var i = 0 />
627                <cfset var n = 0 />
628                <cfset var needTryOnFuseaction = false />
629                <cfset var parsedName = "do.#lCase(arguments.circuitFuseaction)#.cfm" />
630                <cfset var parsedFile = "#this.parsePath##parsedName#" />
631                <cfset var fullParsedFile = "#this.expandFuseboxPath(this.parsePath)##parsedName#" />
632                <cfset var result = structNew() />
633                <cfset var writer = 0 />
634               
635                <!--- validate format of the fuseaction: --->
636                <cfif listLen(arguments.circuitFuseaction,".") lt 2>
637                        <cfthrow type="fusebox.malformedFuseaction"
638                                        message="malformed Fuseaction"
639                                        detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />     
640                </cfif>
641               
642                <cfset circuit = left(arguments.circuitFuseaction,len(arguments.circuitFuseaction)-len(fuseaction)-1) />
643
644                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
645                        <cflock name="#fullParsedFile#" type="exclusive" timeout="300">
646                                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
647                                        <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
648                                        <cfset writer.open(parsedName) />
649                                        <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
650                                        <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
651                                        <cfset writer.setCircuit(circuit) />
652                                        <cfset writer.setFuseaction(fuseaction) />
653                                        <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
654                                                        arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
655                                                        not request.__fusebox.SuppressPlugins>
656                                                <cfset needTryOnFuseaction = true />
657                                                <cfset writer.rawPrintln("<cftry>") />
658                                        </cfif>
659                                        <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
660                                                <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
661                                                <cfloop from="1" to="#n#" index="i">
662                                                        <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
663                                                </cfloop>
664                                        </cfif>
665                                        <cfset writer.setPhase("requestedFuseaction") />
666                                        <cfset compile(writer,circuit,fuseaction) />
667                                        <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
668                                                <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
669                                                <cfloop from="1" to="#n#" index="i">
670                                                        <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
671                                                </cfloop>
672                                        </cfif>
673                                        <cfif needTryOnFuseaction>
674                                                <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
675                                                <cfloop from="1" to="#n#" index="i">
676                                                        <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
677                                                </cfloop>
678                                                <cfset writer.rawPrintln("</cftry>") />
679                                        </cfif>
680                                        <cfset writer.close(variables.parsedFileCache) />
681                                </cfif>
682                        </cflock>
683                </cfif>
684               
685                <cfset result.parsedName = parsedName />
686                <cfif left(parsedFile,1) is "/">
687                        <cfset result.parsedFile = parsedFile />
688                <cfelse>
689                        <cfset result.parsedFile = this.getCoreToAppRootPath() & parsedFile />
690                </cfif>
691                <cfset result.lockName = fullParsedFile />
692               
693                <cfreturn result />
694               
695        </cffunction>
696       
697        <cffunction name="compile" returntype="void" access="public" output="false"
698                                hint="I compile a specific fuseaction during a request (such as for a 'do' verb).">
699                <cfargument name="writer" type="any" required="false"
700                                        hint="I am the parsed file writer object. I am required but it's faster to specify that I am not required." />
701                <cfargument name="circuit" type="any" required="false"
702                                        hint="I am the circuit name. I am required but it's faster to specify that I am not required." />
703                <cfargument name="fuseaction" type="any" required="false"
704                                        hint="I am the fuseaction name, within the specified circuit." />
705                <cfargument name="topLevel" type="boolean" default="false"
706                                        hint="I specify whether or not this is a top-level (public) request." />
707       
708                <cfset var c = "" />
709
710                <cfif not structKeyExists(this.circuits,arguments.circuit)>
711                        <cfif this.allowImplicitCircuits>
712                                <!--- FB55: attempt to create an implicit circuit --->
713                                <cfset this.circuits[arguments.circuit] =
714                                                createObject("component","fuseboxImplicitCircuit")
715                                                        .init(this,arguments.circuit,arguments.writer.getMyFusebox()) />
716                        <cfelse>
717                                <cfthrow type="fusebox.undefinedCircuit"
718                                                message="undefined Circuit"
719                                                detail="You specified a Circuit of #arguments.circuit# which is not defined." />
720                        </cfif>
721                </cfif>
722                <!--- FB5: development-circuit-load only reloads the requested circuit --->
723                <cfif this.mode is "development-circuit-load">
724                        <!--- FB5: ensure we only reload each circuit once per request --->
725                        <cfif not structKeyExists(request.__fusebox.CircuitsLoaded,arguments.circuit)>
726                                <cfset request.__fusebox.CircuitsLoaded[arguments.circuit] = true />
727                                <cfset this.circuits[arguments.circuit].reload(arguments.writer.getMyFusebox()) />
728                        </cfif>
729                </cfif>
730       
731                <cfset c = arguments.writer.setCircuit(arguments.circuit) />
732                <cfset this.circuits[arguments.circuit]
733                                .compile(arguments.writer,arguments.fuseaction,arguments.topLevel) />
734                <cfset arguments.writer.setCircuit(c) />
735               
736        </cffunction>
737       
738        <cffunction name="handleFuseboxException" returntype="boolean" access="public" output="true"
739                                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.">
740                <cfargument name="cfcatch" type="any" required="true"
741                                        hint="I am the original cfcatch structure from the exception that fusebox5.cfm caught." />
742                <cfargument name="attributes" type="struct" required="true"
743                                        hint="I am the attributes 'scope'." />
744                <cfargument name="myFusebox" type="any" required="true"
745                                        hint="I am the myFusebox object." />
746                <cfargument name="appKey" type="string" required="true"
747                                        hint="I am the application key object." />
748               
749                <cfset var __handled = false />
750                <cfset var __type = arguments.cfcatch.type />
751                <cfset var __ext = "." & this.scriptFileDelimiter />
752                <cfset var __errorFile = this.errortemplatesPath & __type & __ext />
753                <cfset var __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
754                <cfset var FUSEBOX_APPLICATION_KEY = arguments.appKey />
755               
756                <cfloop condition="not __handlerExists and len(__type) gt 0">
757                        <cfset __type = listDeleteAt(__type,listLen(__type,"."),".") />
758                        <cfset __errorFile = this.errortemplatesPath & __type & __ext />
759                        <cfset __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
760                </cfloop>
761                <cfif __handlerExists>
762                        <cfif left(__errorFile,1) is "/">
763                                <cfinclude template="#__errorFile#" />
764                        <cfelse>
765                                <cfinclude template="#getCoreToAppRootPath()##__errorFile#" />
766                        </cfif>
767                        <cfset __handled = true />
768                </cfif>
769               
770                <cfreturn __handled />
771               
772        </cffunction>
773       
774        <cffunction name="getFuseactionFactory" returntype="any" access="public" output="false"
775                                hint="I return the factory object that makes fuseaction objects for the framework.">
776               
777                <cfreturn variables.factory />
778
779        </cffunction>
780       
781        <cffunction name="getClassDefinition" returntype="struct" access="public" output="false"
782                                hint="I return the class declaration for a given class. I throw an exception if the class has no declaration.">
783                <cfargument name="className" type="string" required="true"
784                                        hint="I am the name of the class whose declaration should be returned." />
785               
786                <cfreturn this.classes[arguments.className] />
787
788        </cffunction>
789       
790        <cffunction name="getLexiconDefinition" returntype="any" access="public" output="false"
791                                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.">
792                <cfargument name="namespace" type="any" required="false"
793                                        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." />
794               
795                <cfif arguments.namespace is variables.fuseboxLexicon.namespace>
796                        <cfreturn variables.fuseboxLexicon />
797                <cfelse>
798                        <cfreturn variables.fb41Lexicons[arguments.namespace] />
799                </cfif>
800
801        </cffunction>
802       
803        <cffunction name="getVersion" returntype="string" access="public" output="false"
804                                hint="I return the version of this Fusebox 5 object. This is the preferred way to obtain the version in Fusebox 5.">
805       
806                <cfreturn variables.fuseboxVersion />
807               
808        </cffunction>
809       
810        <cffunction name="getAlias" returntype="any" access="public" output="false"
811                                hint="I return the fake circuit alias for the application.">
812       
813                <cfreturn "$fusebox" />
814       
815        </cffunction>
816       
817        <cffunction name="getApplication" returntype="any" access="public" output="false"
818                                hint="I return the fusebox application object.">
819       
820                <cfreturn this />
821       
822        </cffunction>
823       
824        <cffunction name="getCustomAttributes" returntype="struct" access="public" output="false"
825                                hint="I return any custom attributes for the specified namespace prefix.">
826                <cfargument name="ns" type="string" required="true"
827                                        hint="I am the namespace for which to return custom attributes." />
828               
829                <cfif structKeyExists(variables.customAttributes,arguments.ns)>
830                        <!--- we structCopy() this so folks can't poke values back into the metadata! --->
831                        <cfreturn structCopy(variables.customAttributes[arguments.ns]) />
832                <cfelse>
833                        <cfreturn structNew() />
834                </cfif>
835               
836        </cffunction>
837       
838        <cffunction name="getFuseboxFileExtension" returntype="string" access="public" output="false"
839                                hint="I return the fusebox.xml file extension: either xml or cfm.">
840                                       
841                <cfreturn variables.fuseboxFileExtension />
842               
843        </cffunction>
844       
845        <cffunction name="deleteParsedFiles" returntype="void" access="private" output="false"
846                                hint="I delete all the script files in the parsed/ directory.">
847       
848                <cfset var fileQuery = 0 />
849                <cfset var parseDir = expandFuseboxPath(this.parsePath) />
850               
851                <cftry>
852                        <cfdirectory action="list" directory="#parseDir#"
853                                                filter="*.#this.scriptFileDelimiter#" name="fileQuery" />
854                        <cfloop query="fileQuery">
855                                <cffile action="delete" file="#parseDir##fileQuery.name#" />
856                        </cfloop>
857                <cfcatch />
858                </cftry>
859       
860        </cffunction>
861       
862        <cffunction name="loadCircuits" returntype="void" access="private" output="false"
863                                hint="I (re)load all the circuits in an application.">
864                <cfargument name="fbCode" type="any" required="true"
865                                        hint="I am the parsed XML representation of the fusebox.xml file." />
866                <cfargument name="myFusebox" type="myFusebox" required="true"
867                                        hint="I am the myFusebox data structure." />
868               
869                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/circuits/circuit") />
870                <cfset var i = 0 />
871                <cfset var n = arrayLen(children) />
872                <cfset var previousCircuits = this.circuits />
873                <cfset var alias = "" />
874                <cfset var parent = "" />
875                <cfset var relative = true />
876                <cfset var nAttrs = 0 />
877               
878                <cfset this.circuits = structNew() />
879               
880                <!--- pass 1: build the circuits --->
881                <cfloop from="1" to="#n#" index="i">
882                        <cfif not structKeyExists(children[i].xmlAttributes,"alias")>
883                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
884                                                message="Required attribute is missing"
885                                                detail="The attribute 'alias' is required, for a 'circuit' declaration in fusebox.xml." />
886                        </cfif>
887                        <cfif not structKeyExists(children[i].xmlAttributes,"path")>
888                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
889                                                message="Required attribute is missing"
890                                                detail="The attribute 'path' is required, for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
891                        </cfif>
892                        <cfif structKeyExists(children[i].xmlAttributes,"parent")>
893                                <cfset parent = children[i].xmlAttributes.parent />
894                                <cfset nAttrs = 3 />
895                        <cfelse>
896                                <cfset parent = "" />
897                                <cfset nAttrs = 2 />
898                        </cfif>
899                        <cfif structKeyExists(children[i].xmlAttributes,"relative")>
900                                <cfif listFind("true,false,yes,no",children[i].xmlAttributes.relative) eq 0>
901                                        <cfthrow type="fusebox.badGrammar.invalidAttributeValue"
902                                                        message="Attribute has invalid value"
903