root / framework / tags / fusebox51B1 / fuseboxApplication.cfc

Revision 295, 46.5 kB (checked in by scorfield, 2 years ago)

Fixes #170 by creating the 5.1 beta!

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