root / framework / tags / fusebox5RC1 / fuseboxApplication.cfc

Revision 209, 42.4 kB (checked in by scorfield, 3 years ago)

Addresses #84 by setting the version to 5.0.0.RC.1.

Line 
1<!---
2Fusebox Software License
3Version 1.0
4
5Copyright (c) 2003, 2004, 2005, 2006 The Fusebox Corporation. All rights reserved.
6
7Redistribution and use in source and binary forms, with or without modification, are permitted
8provided that the following conditions are met:
9
101. Redistributions of source code must retain the above copyright notice, this list of conditions
11   and the following disclaimer.
12
132. Redistributions in binary form or otherwise encrypted form must reproduce the above copyright
14   notice, this list of conditions and the following disclaimer in the documentation and/or other
15   materials provided with the distribution.
16
173. The end-user documentation included with the redistribution, if any, must include the following
18   acknowledgment:
19
20   "This product includes software developed by the Fusebox Corporation (http://www.fusebox.org/)."
21
22   Alternately, this acknowledgment may appear in the software itself, if and wherever such
23   third-party acknowledgments normally appear.
24
254. The names "Fusebox" and "Fusebox Corporation" must not be used to endorse or promote products
26   derived from this software without prior written (non-electronic) permission. For written
27   permission, please contact fusebox@fusebox.org.
28
295. Products derived from this software may not be called "Fusebox", nor may "Fusebox" appear in
30   their name, without prior written (non-electronic) permission of the Fusebox Corporation. For
31   written permission, please contact fusebox@fusebox.org.
32
33If one or more of the above conditions are violated, then this license is immediately revoked and
34can be re-instated only upon prior written authorization of the Fusebox Corporation.
35
36THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38DISCLAIMED. IN NO EVENT SHALL THE FUSEBOX CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY
39DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
43OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
45-------------------------------------------------------------------------------
46
47This software consists of voluntary contributions made by many individuals on behalf of the
48Fusebox Corporation. For more information on Fusebox, please see <http://www.fusebox.org/>.
49
50--->
51<cfcomponent output="false" hint="I am the Fusebox application object, formerly the application.fusebox data structure.">
52
53        <cfscript>
54        // initialize the fusebox (available to be read by developers but not to be written to)
55        this.isFullyLoaded = false;
56        this.circuits = structNew();
57        this.classes = structNew();
58        this.lexicons = structNew();
59        this.plugins = structNew();
60        this.pluginphases = structNew();
61        this.nonFatalExceptionPrefix = "INFORMATION (can be ignored): ";
62
63        this.precedenceFormOrURL = "form";
64        this.defaultFuseaction = "";
65        this.fuseactionVariable = "fuseaction";
66        // this is ignored:
67        this.parseWithComments = false;
68        this.ignoreBadGrammar = true;
69        this.allowLexicon = true;
70        this.useAssertions = true;
71        this.conditionalParse = false;
72       
73        this.password = "";
74        this.mode = "production";
75        this.scriptLanguage = "cfmx";
76        this.scriptFileDelimiter = "cfm";
77        this.maskedFileDelimiters = "htm,cfm,cfml,php,php4,asp,aspx";
78        this.characterEncoding = "utf-8";
79        // this is ignored:
80        this.parseWithIndentation = this.parseWithComments;
81        this.strictMode = false;
82        this.allowImplicitCircuits = false;
83        this.debug = false;
84        </cfscript>
85       
86        <cffunction name="init" returntype="fuseboxApplication" access="public" output="false"
87                                hint="I am the constructor.">
88                <cfargument name="appKey" type="string" required="true"
89                                        hint="I am FUSEBOX_APPLICATION_KEY." />
90                <cfargument name="appPath" type="string" required="true"
91                                        hint="I am FUSEBOX_APPLICATION_PATH." />
92                <cfargument name="myFusebox" type="myFusebox" required="true"
93                                        hint="I am the myFusebox data structure." />
94               
95                <cfset var myVersion = "5.0.0.RC.1" />
96
97                <cfset variables.factory = createObject("component","fuseboxFactory").init() />
98                <cfset variables.fuseboxLexicon = variables.factory.getBuiltinLexicon() />
99                <cfset variables.customAttributes = structNew() />
100
101                <cfset variables.fuseboxVersion = myVersion />
102               
103                <cfset variables.appKey = arguments.appKey />
104                <cfset this.webrootdirectory = replace(getDirectoryFromPath(getBaseTemplatePath()),"\","/","all") />
105                <cfset variables.coreRoot = replace(getDirectoryFromPath(getCurrentTemplatePath()),"\","/","all") />
106
107                <cfset this.approotdirectory = this.webrootdirectory & replace(arguments.appPath,"\","/","all") />
108                <cfif right(this.approotdirectory,1) is not "/">
109                        <cfset this.approotdirectory = this.approotdirectory & "/" />
110                </cfif>
111                <!--- remove pairs of directory/../ to form canonical path: --->
112                <cfloop condition="find('/../',this.approotdirectory) gt 0">
113                        <cfset this.approotdirectory = REreplace(this.approotdirectory,"[^\.:/]*/\.\./","") />
114                </cfloop>
115                <!--- this works on all platforms: --->
116                <cfset this.osdelimiter = "/" />
117
118                <cfset this.coreToAppRootPath = relativePath(variables.coreRoot,this.approotdirectory) />
119                <cfset this.appRootPathToCore = relativePath(this.approotdirectory,variables.coreRoot) />
120                <cfset this.coreToWebRootPath = relativePath(variables.coreRoot,this.webrootdirectory) />
121                <cfset this.WebRootPathToCore = relativePath(this.webrootdirectory,variables.coreRoot) />
122               
123                <cfset this.parsePath = "parsed/" />
124                <cfset this.parseRootPath = "../" />
125                <cfset this.pluginsPath = "plugins/" />
126                <cfset this.lexiconPath = "lexicon/" />
127                <cfset this.errortemplatesPath = "errortemplates/" />
128               
129                <cfset this.circuits = structNew() />
130                <cfset reload(arguments.appKey,arguments.appPath,arguments.myFusebox) />
131
132                <cfif this.strictMode>
133                        <!--- rootdirectory was deprecated in Fusebox 5 so we no longer set it it strict mode: --->
134                        <cfset structDelete(this,"rootdirectory") />
135                <cfelse>
136                        <!--- for FB4.0 compatibility: --->
137                        <cfset this.rootdirectory = this.approotdirectory />
138                </cfif>
139               
140                <cfreturn this />
141
142        </cffunction>
143
144        <cffunction name="reload" returntype="void" access="public" output="false"
145                                hint="I (re)load the fusebox.xml file into memory and (re)load all of the application components referenced by that.">
146                <cfargument name="appKey" type="string" required="true"
147                                        hint="I am FUSEBOX_APPLICATION_KEY." />
148                <cfargument name="appPath" type="string" required="true"
149                                        hint="I am FUSEBOX_APPLICATION_PATH." />
150                <cfargument name="myFusebox" type="myFusebox" required="true"
151                                        hint="I am the myFusebox data structure." />
152               
153                <cfset var fbFile = "fusebox.xml.cfm" />
154                <cfset var fbXML = "" />
155                <cfset var fbCode = "" />
156                <cfset var encodings = 0 />
157                <cfset var needToLoad = true />
158                <cfset var fuseboxFiles = 0 />
159
160                <cfif structKeyExists(this,"timestamp")>
161                        <cfdirectory action="list" directory="#this.approotdirectory#" filter="fusebox.xml*" name="fuseboxFiles" />
162                        <cfif fuseboxFiles.recordCount eq 1>
163                                <cfset needToLoad = parseDateTime(fuseboxFiles.dateLastModified) gt parseDateTime(this.timestamp) />
164                        <!--- else ignore the ambiguity --->
165                        </cfif>
166                </cfif>
167
168                <cfif needToLoad>
169                        <cfif this.debug>
170                                <cfset arguments.myFusebox.trace("Compiler","Loading fusebox.xml file") />
171                        </cfif>
172                        <!--- attempt to load fusebox.xml(.cfm): --->
173                        <cfif not fileExists(this.approotdirectory & fbFile)>
174                                <cfset fbFile = "fusebox.xml" />
175                        </cfif>
176                        <cftry>
177                               
178                                <cffile action="read" file="#this.approotdirectory##fbFile#"
179                                                variable="fbXML"
180                                                charset="#this.characterEncoding#" />
181                               
182                                <cfcatch type="any">
183                                        <cfthrow type="fusebox.missingFuseboxXML"
184                                                        message="missing fusebox.xml"
185                                                        detail="The file '#fbFile#' could not be found."
186                                                        extendedinfo="#cfcatch.detail#" />
187                                </cfcatch>
188                               
189                        </cftry>
190                       
191                        <cftry>
192                               
193                                <cfset fbCode = xmlParse(fbXML) />
194                               
195                                <!--- see if we need to re-read based on the encoding being different to our default --->
196                                <cfset encodings = xmlSearch(fbCode,"/fusebox/parameters/parameter[@name='characterEncoding']") />
197                                <cfif arrayLen(encodings) eq 1 and structKeyExists(encodings[1].xmlAttributes,"value")>
198                                        <cfif encodings[1].xmlAttributes.value is not this.characterEncoding>
199                                                <cfset this.characterEncoding = encodings[1].xmlAttributes.value />
200                                                <!--- now re-read the file in case anything is changed in that new encoding --->
201                                                <cffile action="read" file="#this.approotdirectory##fbFile#"
202                                                                variable="fbXML"
203                                                                charset="#this.characterEncoding#" />
204                                                <cfset fbCode = xmlParse(fbXML) />
205                                        </cfif>
206                                </cfif>
207                               
208                                <cfcatch type="any">
209                                        <cfthrow type="fusebox.fuseboxXMLError"
210                                                        message="Error reading fusebox.xml"
211                                                        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.)"
212                                                        extendedinfo="#cfcatch.detail#" />
213                                </cfcatch>
214                               
215                        </cftry>
216                       
217                        <cfif fbCode.xmlRoot.xmlName is not "fusebox">
218                                <cfthrow type="fusebox.badGrammar.badFuseboxFile"
219                                                detail="Fusebox file does contain 'fusebox' XML"
220                                                message="Fusebox file #fbFile# does not contain 'fusebox' as the root XML node." />
221                        </cfif>
222
223                        <cfset loadParameters(fbCode) />
224                        <cfset loadLexicons(fbCode) />
225                        <cfset loadClasses(fbCode) />
226                        <cfset loadPlugins(fbCode) />
227                        <cfset loadGlobalPreAndPostProcess(fbCode) />
228                        <!--- save fusebox.xml DOM internally for (re-)loading circuits --->
229                        <cfset variables.fbCode = fbCode />
230                        <cfset variables.fbFile = fbFile />
231                </cfif>
232               
233                <!--- to track circuit loads on this request --->
234                <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />             
235                <cfset loadCircuits(variables.fbCode,arguments.myFusebox) />
236               
237                <!--- FB5: fusebox.loadclean will delete all the parsed files --->
238                <cfif arguments.myFusebox.parameters.clean>
239                        <cfset deleteParsedFiles() />
240                </cfif>
241               
242                <!--- application data available to developers via getApplicationData() method: --->
243                <cfset variables.data = structNew() />
244               
245                <cfset this.isFullyLoaded = true />
246                <cfset this.applicationStarted = false />
247                <cfset this.timestamp = now() />
248                <cfset this.dateLastLoaded = now() />
249               
250                <!---
251                        The following documented parts of application.fusebox are not supported in Fusebox 5:
252                        - application.fusebox.xml
253                        - application.fusebox.globalfuseactions.*
254                        - application.fusebox.circuits.*.xml
255                        - application.fusebox.circuits.preFuseaction.*
256                        - application.fusebox.circuits.postFuseaction.*
257                        - application.fusebox.circuits.*.fuseactions.*.xml
258                --->
259               
260        </cffunction>
261       
262        <cffunction name="getPluginsPath" returntype="string" access="public" output="false"
263                                hint="I am a convenience method to return the location of the plugins.">
264       
265                <cfreturn this.pluginsPath />
266       
267        </cffunction>
268       
269        <cffunction name="getApplicationData" returntype="struct" access="public" output="false"
270                                hint="I return a reference to the application data cache. This is a new concept in Fusebox 5.">
271       
272                <cfreturn variables.data />
273       
274        </cffunction>
275       
276        <cffunction name="getApplicationRoot" returntype="any" access="public" output="false"
277                                hint="I am a convenience method to return the full application root directory path.">
278       
279                <cfreturn this.approotdirectory />
280       
281        </cffunction>
282       
283        <cffunction name="getFuseboxXMLFilename" returntype="string" access="public" output="false"
284                                hint="I return the actual name of the fusebox.xml(.cfm) file.">
285       
286                <cfreturn variables.fbFile />
287       
288        </cffunction>
289       
290        <cffunction name="getCoreToAppRootPath" returntype="any" access="public" output="false"
291                                hint="I am a convenience method to return the relative path from the core files to the application root.">
292       
293                <cfreturn this.coreToAppRootPath />
294       
295        </cffunction>
296       
297        <cffunction name="compileAll" returntype="void" access="public" output="false"
298                                hint="I compile all the public fuseactions in the application.">
299                <cfargument name="myFusebox" type="myFusebox" required="true"
300                                        hint="I am the myFusebox data structure." />
301
302                <cfset var c = 0 />
303                <cfset var a = 0 />
304                <cfset var f = 0 />
305       
306                <cfloop collection="#this.circuits#" item="c">
307                        <cfset a = this.circuits[c].getFuseactions() />
308                        <cfloop collection="#a#" item="f">
309                                <cfif a[f].access is "public">
310                                        <cfset compileRequest(c & "." & f,arguments.myFusebox) />
311                                </cfif>
312                        </cfloop>
313                </cfloop>
314
315        </cffunction>
316       
317        <cffunction name="compileRequest" returntype="struct" access="public" output="false"
318                                hint="I compile a specific (public) fuseaction as an external request.">
319                <cfargument name="circuitFuseaction" type="string" required="true"
320                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
321                <cfargument name="myFusebox" type="myFusebox" required="true"
322                                        hint="I am the myFusebox data structure." />
323
324                <cfset var myVersion = getVersion() />
325                <cfset var circuit = listFirst(arguments.circuitFuseaction,".") />
326                <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
327                <cfset var i = 0 />
328                <cfset var n = 0 />
329                <cfset var needRethrow = true />
330                <cfset var needTryOnFuseaction = false />
331                <cfset var parsedName = "#arguments.circuitFuseaction#.cfm" />
332                <cfset var parsedFile = "#this.getCoreToAppRootPath()##this.parsePath##parsedName#" />
333                <cfset var fullParsedFile = "#this.getApplicationRoot()##this.parsePath##parsedName#" />
334                <cfset var result = structNew() />
335                <cfset var writer = 0 />
336               
337                <!--- validate format of the fuseaction: --->
338                <cfif listLen(arguments.circuitFuseaction,".") neq 2>
339                        <cfthrow type="fusebox.malformedFuseaction"
340                                        message="malformed Fuseaction"
341                                        detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />     
342                </cfif>
343               
344                <!--- to track reloads on this request --->
345                <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />
346                <cfparam name="request.__fusebox.fuseactionsDone" default="#structNew()#" />
347               
348                <!--- set up myFusebox values for this request: --->
349                <cfset arguments.myFusebox.originalCircuit = circuit />
350                <cfset arguments.myFusebox.originalFuseaction = fuseaction />
351                <cfloop collection="#this.plugins#" item="i">
352                        <cfset arguments.myFusebox.plugins[i] = structNew() />
353                </cfloop>
354
355                <!--- note that in Fusebox 5, these are really all the same set of files --->
356                <cfset arguments.myFusebox.version.loader = myVersion />
357                <cfset arguments.myFusebox.version.parser = myVersion />
358                <cfset arguments.myFusebox.version.transformer = myVersion />
359                <!--- legacy test from FB41 although it's a bit pointless --->
360                <cfif myFusebox.version.runtime neq myFusebox.version.loader>
361                        <cfthrow type="fusebox.versionMismatchException"
362                                        message="The loader is not the same version as the runtime" />
363                </cfif>
364               
365                <!--- check access on request - if the circuit/fuseaction doesn't exist we trap it later --->
366                <cfif structKeyExists(this.circuits,circuit) and
367                                structKeyExists(this.circuits[circuit].fuseactions,fuseaction) and
368                                this.circuits[circuit].fuseactions[fuseaction].getAccess() is not "public">
369                        <cfthrow type="fusebox.invalidAccessModifier"
370                                        message="Invalid Access Modifier"
371                                        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.">
372                </cfif>
373               
374                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
375                        <cflock name="#fullParsedFile#" type="exclusive" timeout="30">
376                                <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
377                                        <cfset request.__fusebox.SuppressPlugins = false />
378                                        <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
379                                        <cfset writer.open(parsedName) />
380                                        <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
381                                        <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
382                                        <cfset writer.rawPrintln("<cftry>") />
383                                        <cfset writer.setCircuit(circuit) />
384                                        <cfset writer.setFuseaction(fuseaction) />
385                                        <cfif variables.hasProcess["appinit"]>
386                                                <cfset writer.setPhase("appinit") />
387                                                <cfset writer.println("<cfif myFusebox.applicationStart>") />
388                                                <cfset writer.println(' <cfif not myFusebox.getApplication().applicationStarted>') />
389                                                <cfset writer.println('         <cflock name="##application.ApplicationName##_fusebox_##FUSEBOX_APPLICATION_KEY##_appinit" type="exclusive" timeout="30">') />
390                                                <cfset writer.println('                 <cfif not myFusebox.getApplication().applicationStarted>') />
391                                                <cfset request.__fusebox.SuppressPlugins = true />
392                                                <cfset variables.process["appinit"].compile(writer) />
393                                                <cfset writer.println('                         <cfset myFusebox.getApplication().applicationStarted = true />') />
394                                                <cfset writer.println('                 </cfif>') />
395                                                <cfset writer.println('         </cflock>') />
396                                                <cfset writer.println(' </cfif>') />
397                                                <cfset writer.println("</cfif>") />
398                                        </cfif>
399                                        <cfset request.__fusebox.SuppressPlugins = false />
400                                        <cfif structKeyExists(this.pluginPhases,"preProcess")>
401                                                <cfset n = arrayLen(this.pluginPhases["preProcess"]) />
402                                                <cfloop from="1" to="#n#" index="i">
403                                                        <cfset this.pluginPhases["preProcess"][i].compile(writer) />
404                                                </cfloop>
405                                        </cfif>
406                                        <cfset writer.setPhase("preprocessFuseactions") />
407                                        <cfif variables.hasProcess["preprocess"]>
408                                                <cfset variables.process["preprocess"].compile(writer) />
409                                        </cfif>
410                                        <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
411                                                        arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
412                                                        not request.__fusebox.SuppressPlugins>
413                                                <cfset needTryOnFuseaction = true />
414                                                <cfset writer.rawPrintln("<cftry>") />
415                                        </cfif>
416                                        <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
417                                                <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
418                                                <cfloop from="1" to="#n#" index="i">
419                                                        <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
420                                                </cfloop>
421                                        </cfif>
422                                        <cfset writer.setPhase("requestedFuseaction") />
423                                        <cfset compile(writer,circuit,fuseaction) />
424                                        <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
425                                                <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
426                                                <cfloop from="1" to="#n#" index="i">
427                                                        <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
428                                                </cfloop>
429                                        </cfif>
430                                        <cfif needTryOnFuseaction>
431                                                <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
432                                                <cfloop from="1" to="#n#" index="i">
433                                                        <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
434                                                </cfloop>
435                                                <cfset writer.rawPrintln("</cftry>") />
436                                        </cfif>
437                                        <cfset writer.setPhase("postprocessFuseactions") />
438                                        <cfif variables.hasProcess["postprocess"]>
439                                                <cfset variables.process["postprocess"].compile(writer) />
440                                        </cfif>
441                                        <cfif structKeyExists(this.pluginPhases,"postProcess")>
442                                                <cfset n = arrayLen(this.pluginPhases["postProcess"]) />
443                                                <cfloop from="1" to="#n#" index="i">
444                                                        <cfset this.pluginPhases["postProcess"][i].compile(writer) />
445                                                </cfloop>
446                                        </cfif>
447                                        <cfif structKeyExists(this.pluginPhases,"processError") and
448                                                        not request.__fusebox.SuppressPlugins>
449                                                <cfset n = arrayLen(this.pluginPhases["processError"]) />
450                                                <cfloop from="1" to="#n#" index="i">
451                                                        <cfset needRethrow = false />
452                                                        <cfset this.pluginPhases["processError"][i].compile(writer) />
453                                                </cfloop>
454                                        </cfif>
455                                        <cfif needRethrow>
456                                                <cfset writer.rawPrintln('<' & 'cfcatch><' & 'cfrethrow><' & '/cfcatch>') />
457                                        </cfif>
458                                        <cfset writer.rawPrintln("</cftry>") />
459                                        <cfset writer.close() />
460                                </cfif>
461                        </cflock>
462                </cfif>
463               
464                <cfset result.parsedName = parsedName />
465                <cfset result.parsedFile = parsedFile />
466                <cfset result.lockName = fullParsedFile />
467               
468                <cfreturn result />
469               
470        </cffunction>
471       
472        <cffunction name="compile" returntype="void" access="public" output="false"
473                                hint="I compile a specific fuseaction during a request (such as for a 'do' verb).">
474                <cfargument name="writer" type="any" required="false"
475                                        hint="I am the parsed file writer object. I am required but it's faster to specify that I am not required." />
476                <cfargument name="circuit" type="any" required="false"
477                                        hint="I am the circuit name. I am required but it's faster to specify that I am not required." />
478                <cfargument name="fuseaction" type="any" required="false"
479                                        hint="I am the fuseaction name, within the specified circuit." />
480       
481                <cfset var c = "" />
482
483                <cfif not structKeyExists(this.circuits,arguments.circuit)>
484                        <cfthrow type="fusebox.undefinedCircuit"
485                                        message="undefined Circuit"
486                                        detail="You specified a Circuit of #arguments.circuit# which is not defined." />
487                </cfif>
488                <!--- FB5: development-circuit-load only reloads the requested circuit --->
489                <cfif this.mode is "development-circuit-load">
490                        <!--- FB5: ensure we only reload each circuit once per request --->
491                        <cfif not structKeyExists(request.__fusebox.CircuitsLoaded,arguments.circuit)>
492                                <cfset request.__fusebox.CircuitsLoaded[arguments.circuit] = true />
493                                <cfset this.circuits[arguments.circuit].reload(arguments.writer.getMyFusebox()) />
494                        </cfif>
495                </cfif>
496       
497                <cfset c = arguments.writer.setCircuit(arguments.circuit) />
498                <cfset this.circuits[arguments.circuit]
499                                .compile(arguments.writer,arguments.fuseaction) />
500                <cfset arguments.writer.setCircuit(c) />
501               
502        </cffunction>
503       
504        <cffunction name="handleFuseboxException" returntype="boolean" access="public" output="true"
505                                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.">
506                <cfargument name="cfcatch" type="any" required="true"
507                                        hint="I am the original cfcatch structure from the exception that fusebox5.cfm caught." />
508               
509                <cfset var handled = false />
510                <cfset var type = cfcatch.type />
511                <cfset var ext = "." & this.scriptFileDelimiter />
512                <cfset var errorFile = this.errortemplatesPath & type & ext />
513                <cfset var handlerExists = fileExists(getApplicationRoot() & errorFile) />
514                <cfset var FUSEBOX_APPLICATION_KEY = variables.appKey />
515               
516                <cfloop condition="not handlerExists and len(type) gt 0">
517                        <cfset type = listDeleteAt(type,listLen(type,"."),".") />
518                        <cfset errorFile = this.errortemplatesPath & type & ext />
519                        <cfset handlerExists = fileExists(getApplicationRoot() & errorFile) />
520                </cfloop>
521                <cfif handlerExists>
522                        <cfinclude template="#getCoreToAppRootPath()##errorFile#" />
523                        <cfset handled = true />
524                </cfif>
525               
526                <cfreturn handled />
527               
528        </cffunction>
529       
530        <cffunction name="getFuseactionFactory" returntype="any" access="public" output="false"
531                                hint="I return the factory object that makes fuseaction objects for the framework.">
532               
533                <cfreturn variables.factory />
534
535        </cffunction>
536       
537        <cffunction name="getClassDefinition" returntype="struct" access="public" output="false"
538                                hint="I return the class declaration for a given class. I throw an exception if the class has no declaration.">
539                <cfargument name="className" type="string" required="true"
540                                        hint="I am the name of the class whose declaration should be returned." />
541               
542                <cfreturn this.classes[arguments.className] />
543
544        </cffunction>
545       
546        <cffunction name="getLexiconDefinition" returntype="any" access="public" output="false"
547                                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.">
548                <cfargument name="namespace" type="any" required="false"
549                                        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." />
550               
551                <cfif arguments.namespace is variables.fuseboxLexicon.namespace>
552                        <cfreturn variables.fuseboxLexicon />
553                <cfelse>
554                        <cfreturn variables.fb41Lexicons[arguments.namespace] />
555                </cfif>
556
557        </cffunction>
558       
559        <cffunction name="getVersion" returntype="string" access="public" output="false"
560                                hint="I return the version of this Fusebox 5 object. This is the preferred way to obtain the version in Fusebox 5.">
561       
562                <cfreturn variables.fuseboxVersion />
563               
564        </cffunction>
565       
566        <cffunction name="getAlias" returntype="any" access="public" output="false"
567                                hint="I return the fake circuit alias for the application.">
568       
569                <cfreturn "$fusebox" />
570       
571        </cffunction>
572       
573        <cffunction name="getApplication" returntype="any" access="public" output="false"
574                                hint="I return the fusebox application object.">
575       
576                <cfreturn this />
577       
578        </cffunction>
579       
580        <cffunction name="getCustomAttributes" returntype="struct" access="public" output="false"
581                                hint="I return any custom attributes for the specified namespace prefix.">
582                <cfargument name="ns" type="string" required="true"
583                                        hint="I am the namespace for which to return custom attributes." />
584               
585                <cfif structKeyExists(variables.customAttributes,arguments.ns)>
586                        <!--- we structCopy() this so folks can't poke values back into the metadata! --->
587                        <cfreturn structCopy(variables.customAttributes[arguments.ns]) />
588                <cfelse>
589                        <cfreturn structNew() />
590                </cfif>
591               
592        </cffunction>
593       
594        <cffunction name="deleteParsedFiles" returntype="void" access="private" output="false"
595                                hint="I delete all the script files in the parsed/ directory.">
596       
597                <cfset var fileQuery = 0 />
598                <cfset var parseDir = getApplicationRoot() & this.parsePath />
599               
600                <cftry>
601                        <cfdirectory action="list" directory="#parseDir#"
602                                                filter="*.#this.scriptFileDelimiter#" name="fileQuery" />
603                        <cfloop query="fileQuery">
604                                <cffile action="delete" file="#parseDir##fileQuery.name#" />
605                        </cfloop>
606                <cfcatch />
607                </cftry>
608       
609        </cffunction>
610       
611        <cffunction name="loadCircuits" returntype="void" access="private" output="false"
612                                hint="I (re)load all the circuits in an application.">
613                <cfargument name="fbCode" type="any" required="true"
614                                        hint="I am the parsed XML representation of the fusebox.xml file." />
615                <cfargument name="myFusebox" type="myFusebox" required="true"
616                                        hint="I am the myFusebox data structure." />
617               
618                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/circuits/circuit") />
619                <cfset var i = 0 />
620                <cfset var n = arrayLen(children) />
621                <cfset var previousCircuits = this.circuits />
622                <cfset var alias = "" />
623                <cfset var parent = "" />
624                <cfset var nAttrs = 0 />
625               
626                <cfset this.circuits = structNew() />
627               
628                <!--- pass 1: build the circuits --->
629                <cfloop from="1" to="#n#" index="i">
630                        <cfif not structKeyExists(children[i].xmlAttributes,"alias")>
631                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
632                                                message="Required attribute is missing"
633                                                detail="The attribute 'alias' is required, for a 'circuit' declaration in fusebox.xml." />
634                        </cfif>
635                        <cfif not structKeyExists(children[i].xmlAttributes,"path")>
636                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
637                                                message="Required attribute is missing"
638                                                detail="The attribute 'path' is required, for a 'circuit' declaration in fusebox.xml." />
639                        </cfif>
640                        <cfif structKeyExists(children[i].xmlAttributes,"parent")>
641                                <cfset parent = children[i].xmlAttributes.parent />
642                                <cfset nAttrs = 3 />
643                        <cfelse>
644                                <cfset parent = "" />
645                                <cfset nAttrs = 2 />
646                        </cfif>
647                        <cfif this.strictMode and nAttrs neq structCount(children[i].xmlAttributes)>
648                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
649                                                message="Unexpected attributes"
650                                                detail="Attributes other than 'alias', 'path' and 'parent' were found in the declaration of the '#alias#' circuit in fusebox.xml." />
651                        </cfif>
652                        <cfset alias = children[i].xmlAttributes.alias />
653                        <!--- record each circuit load per request - optimization for development-circuit-load mode --->
654                        <cfset request.__fusebox.CircuitsLoaded[alias] = true />
655                        <cfif structKeyExists(previousCircuits,alias) and
656                                        children[i].xmlAttributes.path is previousCircuits[alias].getOriginalPath() and
657                                        parent is previousCircuits[alias].parent>
658                                <!--- old circuit, we can just reload it --->
659                                <cfset this.circuits[alias] = previousCircuits[alias].reload(arguments.myFusebox) />
660                        <cfelse>
661                                <!--- new circuit, we must create it from scratch --->
662                                <cfset this.circuits[alias] =
663                                                createObject("component","fuseboxCircuit")
664                                                        .init(this,alias,children[i].xmlAttributes.path,parent,arguments.myFusebox) />
665                        </cfif>
666                </cfloop>
667               
668                <!--- pass 2: build the circuit trace for each circuit --->
669                <cfloop collection="#this.circuits#" item="i">
670                        <cfset this.circuits[i].buildCircuitTrace() />
671                </cfloop>
672               
673        </cffunction>
674       
675        <cffunction name="loadLexicons" returntype="void" access="private" output="false"
676                                hint="I load any lexicon declarations (both the Fusebox 4.1 style lexicon declarations and the Fusebox 5 style namespace declarations).">
677                <cfargument name="fbCode" type="any" required="true"
678                                        hint="I am the parsed XML representation of the fusebox.xml file." />
679               
680                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/lexicons/lexicon") />
681                <cfset var i = 0 />
682                <cfset var n = arrayLen(children) />
683                <cfset var aLex = "" />
684                <cfset var attributes = arguments.fbCode.xmlRoot.xmlAttributes />
685                <cfset var attr = "" />
686                <cfset var ns = "" />
687               
688                <cfif n gt 0 and this.strictMode>
689                        <cfthrow type="fusebox.badGrammar.deprecated"
690                                        message="Deprecated feature"
691                                        detail="Using the 'lexicon' declaration in fusebox.xml was deprecated in Fusebox 5." />
692                </cfif>
693
694                <!--- load the legacy FB41 lexicons from the XML --->
695                <cfset variables.fb41Lexicons = structNew() />
696               
697                <cfloop from="1" to="#n#" index="i">
698                        <cfset aLex = structNew() />
699                        <cfset aLex.namespace = children[i].xmlAttributes.namespace />
700                        <cfset aLex.path = replace(children[i].xmlAttributes.path,"\","/","all") />
701                        <cfif right(aLex.path,1) is not "/">
702                                <cfset aLex.path = aLex.path & "/" />
703                        </cfif>
704                        <cfset aLex.path = getCoreToAppRootPath() & "lexicon/" & aLex.path />
705                        <cfset variables.fb41Lexicons[children[i].xmlAttributes.namespace] = aLex />
706                </cfloop>
707               
708                <!--- now load the new FB5 implicit lexicons from the <fusebox> tag --->
709               
710                <!--- pass 1: pull out any namespace declarations --->
711                <cfloop collection="#attributes#" item="attr">
712                        <cfif len(attr) gt 6 and left(attr,6) is "xmlns:">
713                                <!--- found a namespace declaration, pull it out: --->
714                                <cfset aLex = structNew() />
715                                <cfset aLex.namespace = listLast(attr,":") />
716                                <cfif aLex.namespace is variables.fuseboxLexicon.namespace>
717                                        <cfthrow type="fusebox.badGrammar.reservedName"
718                                                        message="Attempt to use reserved namespace"
719                                                        detail="You have attempted to declare a namespace '#aLex.namespace#' (in fusebox.xml) which is reserved by the Fusebox framework." />
720                                </cfif>
721                                <cfset aLex.path = getApplication().getCoreToAppRootPath() & getApplication().lexiconPath & attributes[attr] />
722                                <cfset variables.lexicons[aLex.namespace] = aLex />
723                                <cfset variables.customAttributes[aLex.namespace] = structNew() />
724                        </cfif>
725                </cfloop>
726               
727                <!--- pass 2: pull out any custom attributes --->
728                <cfloop collection="#attributes#" item="attr">
729                        <cfif listLen(attr,":") eq 2>
730                                <!--- looks like a custom attribute: --->
731                                <cfset ns = listFirst(attr,":") />
732                                <cfif ns is "xmlns">
733                                        <!--- special case - need to ignore xmlns:foo="bar" --->
734                                <cfelseif structKeyExists(variables.customAttributes,ns)>
735                                        <cfset variables.customAttributes[ns][listLast(attr,":")] = attributes[attr] />
736                                <cfelse>
737                                        <cfthrow type="fusebox.badGrammar.undeclaredNamespace"
738                                                        message="Undeclared lexicon namespace"
739                                                        detail="The lexicon prefix '#ns#' was found on a custom attribute in the <fusebox> tag but no such lexicon namespace has been declared." />
740                                </cfif>
741                        <cfelseif this.strictMode>
742                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
743                                                message="Unexpected attributes"
744                                                detail="Unexpected attributes were found in the 'fusebox' tag in fusebox.xml." />
745                        </cfif>
746                </cfloop>
747               
748        </cffunction>
749       
750        <cffunction name="loadClasses" returntype="void" access="private" output="false"
751                                hint="I load any class declarations, including custom attributes (based on Fusebox 5 namespace declarations).">
752                <cfargument name="fbCode" type="any" required="true"
753                                        hint="I am the parsed XML representation of the fusebox.xml file." />
754               
755                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/classes/class") />
756                <cfset var i = 0 />
757                <cfset var n = arrayLen(children) />
758                <cfset var attribs = 0 />
759                <cfset var attr = "" />
760                <cfset var ns = "" />
761                <cfset var customAttribs = 0 />
762                <cfset var constructor = "" />
763                <cfset var type = "" />
764                <cfset var nAttrs = 0 />
765               
766                <cfset this.classes = structNew() />
767               
768                <cfloop from="1" to="#n#" index="i">
769                        <cfset attribs = children[i].xmlAttributes />
770
771                        <cfif not structKeyExists(attribs,"alias")>
772                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
773                                                message="Required attribute is missing"
774                                                detail="The attribute 'alias' is required, for a 'class' declaration in fusebox.xml." />
775                        </cfif>
776                        <cfif not structKeyExists(attribs,"classpath")>
777                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
778                                                message="Required attribute is missing"
779                                                detail="The attribute 'classpath' is required, for a 'class' declaration in fusebox.xml." />
780                        </cfif>
781                        <cfif structKeyExists(attribs,"constructor")>
782                                <cfset constructor = attribs.constructor />
783                                <cfset nAttrs = 3 />
784                        <cfelse>
785                                <cfset constructor = "" />
786                                <cfset nAttrs = 2 />
787                        </cfif>
788                        <!--- FB5: allow sensible default for type --->
789                        <cfif structKeyExists(attribs,"type")>
790                                <cfset type = attribs.type />
791                                <cfset nAttrs = nAttrs + 1 />
792                        <cfelse>
793                                <cfset type = "component" />
794                        </cfif>
795
796                        <!--- scan for custom attributes --->
797                        <cfset customAttribs = structNew() />
798                        <cfloop collection="#attribs#" item="attr">
799                                <cfif listLen(attr,":") eq 2>
800                                        <cfset nAttrs = nAttrs + 1 />
801                                        <!--- looks like a custom attribute: --->
802                                        <cfset ns = listFirst(attr,":") />
803                                        <cfif structKeyExists(variables.customAttributes,ns)>
804                                                <cfset customAttribs[ns][listLast(attr,":")] = attribs[attr] />
805                                        <cfelse>
806                                                <cfthrow type="fusebox.badGrammar.undeclaredNamespace"
807                                                                message="Undeclared lexicon namespace"
808                                                                detail="The lexicon prefix '#ns#' was found on a custom attribute in the <class> tag but no such lexicon namespace has been declared." />
809                                        </cfif>
810                                </cfif>
811                        </cfloop>
812                       
813                        <cfif this.strictMode and structCount(attribs) neq nAttrs>
814                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
815                                                message="Unexpected attributes"
816                                                detail="Unexpected attributes were found in the '#attribs.alias#' class declaration in fusebox.xml." />
817                        </cfif>
818
819                        <cfset this.classes[attribs.alias] = createObject("component","fuseboxClassDefinition")
820                                                                .init(type,attribs.classpath,constructor,customAttribs) />
821                       
822                </cfloop>
823               
824        </cffunction>
825       
826        <cffunction name="loadPlugins" returntype="void" access="private" output="false"
827                                hint="I load any plugin declarations.">
828                <cfargument name="fbCode" type="any" required="true"
829                                        hint="I am the parsed XML representation of the fusebox.xml file." />
830               
831                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/plugins/phase") />
832                <cfset var i = 0 />
833                <cfset var n = arrayLen(children) />
834                <cfset var j = 0 />
835                <cfset var nn = 0 />
836                <cfset var phase = "" />
837                <cfset var plugin = 0 />
838               
839                <cfset this.plugins = structNew() />
840                <cfset this.pluginphases = structNew() />
841               
842                <cfloop from="1" to="#n#" index="i">
843                        <cfif not structKeyExists(children[i].xmlAttributes,"name")>
844                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
845                                                message="Required attribute is missing"
846                                                detail="The attribute 'name' is required, for a 'plugin' declaration in fusebox.xml." />
847                        </cfif>
848                        <cfset phase = children[i].xmlAttributes.name />
849                        <cfif this.strictMode and structCount(children[i].xmlAttributes) neq 1>
850                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
851                                                message="Unexpected attributes"
852                                                detail="Unexpected attributes were found in the '#phase#' phase declaration in fusebox.xml." />
853                        </cfif>
854                        <cfset nn = arrayLen(children[i].xmlChildren) />
855                        <cfloop from="1" to="#nn#" index="j">
856                                <cfset plugin = createObject("component","fuseboxPlugin").init(phase,children[i].xmlChildren[j],this) />
857                                <cfset this.plugins[plugin.getName()][phase] = plugin />
858                                <cfif not structKeyExists(this.pluginphases,phase)>
859                                        <cfset this.pluginphases[phase] = arrayNew(1) />
860                                </cfif>
861                                <cfset arrayAppend(this.pluginphases[phase],plugin) />
862                        </cfloop>
863                </cfloop>
864               
865        </cffunction>
866       
867        <cffunction name="loadParameters" returntype="void" access="private" output="false"
868                                hint="I load any parameter declarations (and ensure none of them can overwrite public methods in this object!).">
869                <cfargument name="fbCode" type="any" required="true"
870                                        hint="I am the parsed XML representation of the fusebox.xml file." />
871               
872                <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/parameters/parameter") />
873                <cfset var i = 0 />
874                <cfset var n = arrayLen(children) />
875                <cfset var p = "" />
876               
877                <cfloop from="1" to="#n#" index="i">
878                        <cfif not structKeyExists(children[i].xmlAttributes,"name")>
879                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
880                                                message="Required attribute is missing"
881                                                detail="The attribute 'name' is required, for a 'parameter' declaration in fusebox.xml." />
882                        </cfif>
883                        <cfset p = children[i].xmlAttributes.name />
884                        <cfif not structKeyExists(children[i].xmlAttributes,"value")>
885                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
886                                                message="Required attribute is missing"
887                                                detail="The attribute 'value' is required, for the '#p#' parameter declaration in fusebox.xml." />
888                        </cfif>
889                        <cfif this.strictMode and structCount(children[i].xmlAttributes) neq 2>
890                                <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
891                                                message="Unexpected attributes"
892                                                detail="Unexpected attributes were found in the '#p#' parameter declaration in fusebox.xml." />
893                        </cfif>
894                        <cfif structKeyExists(this,p) and isCustomFunction(this[p])>
895                                <cfthrow type="fusebox.badGrammar.reservedName"
896                                                message="Attempt to use reserved parameter name"
897                                                detail="You have attempted to set a parameter called '#p#' which is reserved by the Fusebox framework." />
898                        <cfelse>
899                                <cfset this[p] = children[i].xmlAttributes.value />
900                        </cfif>
901                </cfloop>
902               
903        </cffunction>
904       
905        <cffunction name="loadGlobalProcess" returntype="void" access="private" output="false"
906                                hint="I load the globalfuseaction for the specified processing phase.">
907                <cfargument name="fbCode" type="any" required="true"
908