Ticket #380: EventContext.cfc

File EventContext.cfc, 40.0 kB (added by andrea@…, 16 years ago)
Line 
1<cfcomponent output="false" extends="ModelGlue.Core.Event" hint="I represent an event request and its context.  I am what you see as arguments.event.">
2
3<cffunction name="init" access="public" returnType="any" output="false" hint="I build a new EventContext.">
4        <cfargument name="eventHandlers" default="#structNew()#" hint="Available event handlers." />
5        <cfargument name="messageListeners" default="#structNew()#" hint="Message subscribers." />
6        <cfargument name="requestPhases" hint="Request phases." />
7        <cfargument name="modelglue" required="false" hint="The framework itself." />
8        <cfargument name="statePersister" required="false" default="#createObject("component", "ModelGlue.gesture.eventrequest.statepersistence.SessionBasedStatePersister").init()#" hint="StatePersister to use during stateful redirects." />
9        <cfargument name="viewRenderer" required="false" default="#createObject("component", "ModelGlue.gesture.view.ViewRenderer").init()#" hint="ViewRenderer to use to render included views to HTML." />
10        <cfargument name="beanPopulator" required="false" default="#createObject("component", "ModelGlue.gesture.externaladapters.beanpopulation.CollectionBeanPopulator").init()#" hint="Populator used by makeEventBean()." />
11        <cfargument name="logWriter" required="false" default="#createObject("component", "ModelGlue.gesture.eventrequest.log.LogWriter").init()#" hint="LogWriter used by addTraceStatement()." />
12        <cfargument name="values" required="false" default="#arrayNew(1)#" hint="A single structure or array of structures to merge into this collection." />
13        <cfargument name="helpers" required="false" hint="Helpers available as part of the event context." default="#structNew()#" />
14       
15        <cfset variables._state = createObject("component", "ModelGlue.gesture.collections.MapCollection").init(values) />
16        <cfset variables._viewCollection = createObject("component", "ModelGlue.gesture.collections.ViewCollection").init() />
17        <cfset variables._readyForExecution = false />
18        <cfset variables._initialEvent = "" />
19        <cfset variables._currentEventHandler = "" />
20        <cfset variables._currentMessage = "" />
21        <cfset this.log = arrayNew(1) />
22        <cfset this.created = getTickCount() />
23        <cfset variables._helpers = arguments.helpers />
24        <cfset variables._logWriter = arguments.logWriter />
25       
26        <cfif structKeyExists(arguments, "modelglue")>
27                <!--- External maps of listeners and handlers --->
28                <cfset variables._listeners = arguments.modelglue.messageListeners />
29                <cfset variables._eventHandlers = arguments.modelglue.eventHandlers />
30       
31                <!--- Default to MG's list of phases if none are passed. --->
32                <cfif not structKeyExists(arguments, "requestPhases")>
33                        <cfset variables._requestPhases = arguments.modelglue.phases />
34                <cfelse>
35                        <cfset variables._requestPhases = arguments.requestPhases />
36                </cfif>
37                       
38                <cfset variables._modelGlue = arguments.modelglue />
39        <cfelse>
40                <!--- External maps of listeners and handlers --->
41                <cfset variables._listeners = arguments.messageListeners />
42                <cfset variables._eventHandlers = arguments.eventHandlers />
43       
44                <!--- External list of event phases --->
45                <cfif not structKeyExists(arguments, "requestPhases")>
46                        <cfset variables._requestPhases = arrayNew(1) />
47                <cfelse>
48                        <cfset variables._requestPhases = arguments.requestPhases />
49                </cfif>
50        </cfif>
51       
52        <!--- Event Handler and View queues are implemented as linked lists --->
53        <cfset variables._nextEventHandler = "" />
54        <cfset variables._lastEventHandler = "" />
55        <cfset variables._nextView = "" />
56        <cfset variables._lastView = "" />
57       
58        <cfset setStatePersister(arguments.statePersister) />
59        <cfset setViewRenderer(arguments.viewRenderer) />
60        <cfset setBeanPopulator(arguments.beanPopulator) />
61
62        <cfset resetResults() />
63
64        <cfset addTraceStatement("Creation", "Event Context Created") />
65         
66        <cfreturn this />
67</cffunction>
68
69<cffunction name="setListenerMap" output="false" hint="I set the map of message listeners to be used for message broadcasting.">
70        <cfargument name="listenerMap" type="struct" hint="The listener map." />
71        <cfset variables._listeners = arguments.listenerMap /> 
72</cffunction>
73
74<cffunction name="setEventHandlerMap" output="false" hint="I set the map of event handlers registered with Model-Glue.">
75        <cfargument name="eventHandlerMap" type="struct" hint="The event handlers map." />
76        <cfset variables._eventHandlers = arguments.eventHandlerMap /> 
77</cffunction>
78
79<cffunction name="setViewRenderer" output="false" hint="I set the instance of the view renderer to use when a request is made to render a view.">
80        <cfargument name="viewRenderer" hint="The view renderer to use." />
81        <cfset variables._viewRenderer = arguments.viewRenderer />     
82</cffunction>
83
84<cffunction name="setRequestPhases" output="false" hint="I set the instance of the view renderer to use when a request is made to render a view.">
85        <cfargument name="requestPhases" hint="The view renderer to use." />
86        <cfset variables._requestPhases = arguments.requestPhases />   
87</cffunction>
88
89<cffunction name="setStatePersister" output="false" hint="I set the mechanism by which this event context should persist and recall its state across requests.">
90        <cfargument name="statePersister" />
91       
92        <cfset variables._statePersister = arguments.statePersister />
93</cffunction>
94
95<cffunction name="setBeanPopulator" output="false" hint="I set Populator used by makeEventBean.">
96        <cfargument name="beanPopulator" />
97       
98        <cfset variables._beanPopulator = arguments.beanPopulator />
99</cffunction>
100
101<cffunction name="getModelGlue" output="false" hint="Gets the instance of ModelGlue associated with this context.">
102        <cfreturn variables._modelGlue />
103</cffunction>
104                                               
105<!--- EVENT HANDLER QUEUE MANAGEMENT --->
106<cffunction name="addEventHandler" output="false" hint="Queues an event handler to be executed as part of this request.">
107        <cfargument name="eventHandler" hint="An EventHandler instance to queue.  Duck-typed for speed." />
108       
109        <cfset var link = structNew() />
110
111        <cfset link.eventHandler = arguments.eventHandler />
112        <cfset link.next = "" />
113
114        <cfset addTraceStatement("Event Queue", "Queueing event handler: #arguments.eventHandler.name#") />
115       
116        <cfif variables._readyForExecution and not isObject(variables._initialEvent)>
117                <cfset variables._initialEvent = arguments.eventHandler />
118        </cfif>
119
120        <cfif isSimpleValue(variables._nextEventHandler)>
121                <cfset variables._nextEventHandler = link />
122                <cfset variables._lastEventHandler = link />
123        <cfelse>
124                <cfset variables._lastEventHandler.next = link />
125                <cfset variables._lastEventHandler = link />
126        </cfif>
127</cffunction>
128
129<cffunction name="getNextEventHandler" access="public" output="false" hint="Returns the next event handler in the queue.">
130        <cfset var eh = variables._nextEventHandler.eventHandler />
131        <cfset variables._nextEventHandler = variables._nextEventHandler.next />
132       
133        <cfreturn eh />
134</cffunction>
135
136<cffunction name="hasNextEventHandler" output="false" hint="Returns if there's another event handler in the queue.">
137        <cfreturn not isSimpleValue(variables._nextEventHandler) />
138</cffunction>
139
140<cffunction name="prepareForInvocation" access="public" output="false" hint="Invoked when all ""under the hood"" events (onRequestStart, etc.) are complete.">
141        <cfset variables._readyForExecution = true />
142</cffunction>
143
144<!--- REQUEST EXECUTION --->
145<cffunction name="execute" output="false" hint="Executes the event request.  Duck typed for speed:  returns the request itself.">
146        <cfset var i = "" />
147        <cfset var exceptionEventHandler = "" />
148       
149        <!--- Try to execute the event phases or nonphased request --->
150        <cftry>
151                <cfif isArray(variables._requestPhases) and arrayLen(variables._requestPhases)>
152                        <cfloop from="1" to="#arrayLen(variables._requestPhases)#" index="i">
153                                <cfset this.addTraceStatement(variables._requestPhases[i].name, "Beginning request phase.") />
154                               
155                                <cfset variables._requestPhases[i].execute(this) />
156                               
157                                <cfset this.addTraceStatement(variables._requestPhases[i].name, "Request phase complete.") />
158                        </cfloop>
159                <cfelse>
160                        <cfset executeEventQueue() />
161                </cfif>
162                <cfcatch>
163                        <!--- Bus the exception --->
164                        <cfset setValue("exception", cfcatch) />
165                       
166                        <!--- Trace the exception.  Maybe people will like this ;) --->
167                        <cfset addTraceStatement("Exception", cfcatch) />
168                       
169                        <cfif structKeyExists(variables, "_modelGlue")>
170                                <!--- If we're not running the exception handler, queue the exception handler. --->
171                                <cfset exceptionEventHandler = variables._modelGlue.getConfigSetting("defaultExceptionHandler") />
172                       
173                                <cfif isObject(getCurrentEventHandler()) and getCurrentEventHandler().name neq exceptionEventHandler and variables._modelGlue.hasEventHandler(exceptionEventHandler)>
174                                        <cfset addEventHandler(variables._modelGlue.getEventHandler(exceptionEventHandler)) />
175                                        <cfset executeEventQueue() />
176                                <cfelse>
177                                        <cfrethrow />
178                                </cfif>
179                        <cfelse>
180                                <cfrethrow />
181                        </cfif>
182                </cfcatch>
183        </cftry>
184       
185        <cfreturn this />
186</cffunction>
187
188<cffunction name="executeEventQueue" access="public" output="false" hint="Executes all event handlers currently in the event queue and renders queued views.">
189        <cfargument name="signalCompletion" />
190       
191        <cfset var initialEh = variables._initialEvent />
192        <cfset var eh = 0 />
193        <cfset var cacheKey = 0 />
194        <cfset var cacheReq = "" />
195        <cfset var i = 0 />
196        <cfset var requestFormat = getValue(variables._modelGlue.getConfigSetting("requestFormatValue")) />
197        <cfset var view = "" />
198       
199        <cfif not isStruct(variables._nextEventHandler)>
200                <!--- Nothing to do! --->
201                <cfreturn />
202        </cfif>
203
204        <cfif isObject(variables._initialEvent) and initialEh.cache>
205                <cfset cacheKey = initialEh.cacheKey />
206                <cfloop list="#initialEh.cacheKeyValues#" index="i">
207                        <cfset cacheKey = "#requestFormat#." & cacheKey & ".#getValue(i)#" />
208                </cfloop>
209               
210                <cfset cacheReq = variables._modelglue.cacheAdapter.get(cacheKey) />
211
212                <cfif cacheReq.success>
213                        <cfset this.addTraceStatement("Event Handler Cache", "Cached initial event handler used.", "Key: #cacheKey#") />
214                        <cfset this.addView(cacheReq.content.key, cacheReq.content.output) />
215               
216                        <!--- Reset the queue --->
217                        <cfset variables._nextEventHandler = "" />
218                        <cfreturn />
219                </cfif>
220        </cfif>
221         
222        <!---
223                Andrea Campolonghi : see if the event has been cached and add the cached views if exists 
224                If event is cached then is not executed at all.
225        --->
226        <cfloop condition="not isSimpleValue(variables._nextEventHandler)">
227
228                <cfset eh = getNextEventHandler() />
229
230                <cfif isObject(eh) and eh.cache>
231                       
232                        <cfloop array="#eh.views.CFNULLKEYWORKAROUND#" index="view">
233                       
234                                <cfset cacheKey = this.generateCacheKey(view.cacheKey,eh.cacheKeyValues) />
235                               
236                                <cfset cacheReq = variables._modelglue.cacheAdapter.get(cacheKey) />
237               
238                                <cfif cacheReq.success>
239                                       
240                                        <cfset this.addTraceStatement("Event Handler Cache", "Rendering cached event handler.", "Key: #cacheKey#") />
241                                        <cfset this.addView(cacheReq.content.name, cacheReq.content.output) />
242                               
243                                <cfelse>
244               
245                                        <cfset executeEventHandler(eh) />
246       
247                                        <cfset cacheReq = structNew() />
248                                        <cfset cacheReq.key = cacheKey />
249                                        <cfset cacheReq.name = view.name />                             
250                                        <cfset cacheReq.output = renderView(view,false) />
251                                       
252                                        <cfif eh.cacheTimeout>
253                                                <cfset variables._modelglue.cacheAdapter.put(cacheKey, cacheReq, eh.cacheTimeout) />
254                                        <cfelse>
255                                                <cfset variables._modelglue.cacheAdapter.put(cacheKey, cacheReq) />
256                                        </cfif>
257       
258                                        <cfset this.addTraceStatement("Event Handler Cache", "Caching initial event handler", "Key: #cacheKey#") />
259               
260                                </cfif>
261                       
262                        </cfloop>
263
264                <cfelse>
265
266                        <cfset executeEventHandler(eh) />
267
268                </cfif>
269                       
270
271        </cfloop>
272
273        <!--- If we need to signal completion, do so. --->
274        <cfif structKeyExists(arguments, "signalCompletion")
275                                and structKeyExists(variables._eventHandlers, "modelGlue.onQueueComplete")             
276        >
277                <cfset executeEventHandler(variables._eventHandlers["modelglue.onQueueComplete"]) />
278        </cfif>
279       
280        <!--- Render all views queued - moved inline after tooling heavy load situations.
281        <cfif not isSimpleValue(variables._nextView)>
282                <cfset renderViewQueue() />
283        </cfif>
284         --->
285
286        <!--- Edit by Cristian Costantini for managing view cache --->
287        <cfloop condition="not isSimpleValue(variables._nextView)">
288               
289                <cfset view = getNextView() />
290               
291                <cfif view.cache>
292               
293                        <cfset cachekey = this.generateCacheKey(view.cacheKey,view.cacheKeyValues) />
294                                       
295                        <cfif not view.cacheTimeout>
296                                <cfset view.cacheTimeout = variables._modelGlue.configuration.defaultcachetimeout />
297                        </cfif>
298
299                        <cfset cacheReq =  variables._modelglue.cacheAdapter.get( cachekey ) />
300                       
301                        <cfif cacheReq.success eq false >
302                       
303                                <cfset cacheReq.key = cachekey />
304                               
305                                <cfset this.addTraceStatement("Views", "Rendering view ""#view.name#"" (#view.template#)", "<include name=""#view.name#"" template=""#view.template#"" />") />
306                               
307                                <cfset cacheReq.output = renderView(view,false) />
308                               
309                                <cfset this.addTraceStatement("Views Cache", "Caching views", "Name:#view.name# (#view.template#) Key: #cacheKey#") />
310                               
311                                <cfif view.cacheTimeout>
312                                        <cfset variables._modelglue.cacheAdapter.put(cachekey, cacheReq, view.cacheTimeout) />
313                                <cfelse>
314                                        <cfset variables._modelglue.cacheAdapter.put(cachekey, cacheReq) />
315                                </cfif>
316                               
317                        <cfelse>
318                       
319                                <cfset addView( view.name, cacheReq.content.output, view.append) />
320                       
321                        </cfif>
322                       
323                <cfelse>
324               
325                        <cfset this.addTraceStatement("Views", "Rendering view ""#view.name#"" (#view.template#)", "<include name=""#view.name#"" template=""#view.template#"" />") />
326                        <cfset renderView(view) />
327               
328                </cfif>
329               
330        </cfloop>
331
332        <cfif isObject(initialEh) and initialEh.cache>
333                <cfset cacheReq = structNew() />
334                <cfset cacheReq.key = variables._viewCollection.getFinalViewKey() />
335                <cfset cacheReq.output = variables._viewCollection.getFinalView() />
336               
337                <cfif initialEh.cacheTimeout>
338                        <cfset variables._modelglue.cacheAdapter.put(cacheKey, cacheReq, initialEh.cacheTimeout) />
339                <cfelse>
340                        <cfset variables._modelglue.cacheAdapter.put(cacheKey, cacheReq) />
341                </cfif>
342
343                <cfset this.addTraceStatement("Event Handler Cache", "Caching initial event handler", "Key: #cacheKey#") />
344        </cfif>
345</cffunction>
346
347<cffunction name="executeEventHandler" output="false" hint="Executes an event handler:  invokes listener functions, handles results, and queues views for rendering">
348        <cfargument name="eventHandler" hint="The event handler to execute.  Duck-typed for speed." />
349       
350        <cfset var message = "" />
351        <cfset var results = "" />
352        <cfset var result = "" />
353       
354        <cfset var i = "" />
355        <cfset var j = "" />
356        <cfset var requestFormat = getValue("requestFormat", variables._modelGlue.getConfigSetting("requestFormatValue")) />
357        <cfset var cacheKey = "" />
358               
359        <cfset variables._currentEventHandler = arguments.eventHandler />
360       
361        <cfset this.addTraceStatement("Event Handler", "Execute ""#arguments.eventHandler.name#""", "<event-handler name=""#arguments.eventHAndler.name#"">") />
362       
363        <!---
364                Invoke "" message broadcasts.  Code repeated for format, if necessary, to
365                avoid string parsing - this is a per-request invocation!
366        --->
367        <cfloop from="1" to="#arrayLen(arguments.eventHandler.messages.cfNullKeyWorkaround)#" index="i">
368                <cfset message = arguments.eventHandler.messages.cfNullKeyWorkaround[i] />
369               
370                <cfset variables._currentMessage = message />
371
372                <cfset this.addTraceStatement("Message Broadcast", "Broadcasting ""#message.name#""", "<message name=""#message.name#"">") />
373
374                <cfif structKeyExists(variables._listeners, message.name)>
375                        <cfloop from="1" to="#arrayLen(variables._listeners[message.name])#" index="j">
376                                <cfset this.addTraceStatement("Message Listener", "Invoking #variables._listeners[message.name][j].listenerFunction# in #getMetadata(variables._listeners[message.name][j].target).name#", "<message-listener message=""#message.name#"" function=""#variables._listeners[message.name][j].listenerFunction#"" />") />
377                                <!---
378                                <cfset variables._listeners[message.name][j].invokeListener(this) />
379                                --->
380                                <cfinvoke component="#variables._listeners[message.name][j].target#" method="#variables._listeners[message.name][j].listenerFunction#">
381                                        <cfinvokeargument name="event" value="#this#" />
382                                </cfinvoke>
383                        </cfloop>
384                </cfif>
385        </cfloop>
386        <cfif len(requestFormat) and structKeyExists(arguments.eventHandler.messages, requestFormat)>
387                <cfloop from="1" to="#arrayLen(arguments.eventHandler.messages[requestFormat])#" index="i">
388                        <cfset message = arguments.eventHandler.messages[requestFormat][i] />
389                       
390                        <cfset variables._currentMessage = message />
391       
392                        <cfset this.addTraceStatement("Message Broadcast", "Broadcasting ""#message.name#""", "<message name=""#message.name#"">") />
393       
394                        <cfif structKeyExists(variables._listeners, message.name)>
395                                <cfloop from="1" to="#arrayLen(variables._listeners[message.name])#" index="j">
396                                        <cfset this.addTraceStatement("Message Listener", "Invoking #variables._listeners[message.name][j].listenerFunction# in #getMetadata(variables._listeners[message.name][j].target).name#", "<message-listener message=""#message.name#"" function=""#variables._listeners[message.name][j].listenerFunction#"" />") />
397                                        <cfset variables._listeners[message.name][j].invokeListener(this) />
398                                </cfloop>
399                        </cfif>
400                </cfloop>
401        </cfif>
402               
403        <!--- Get, queue, and reset results. --->
404        <cfset results = getResults() />
405       
406        <!--- Queue explicit results: repetitive on purpose. --->
407        <cfloop from="1" to="#arrayLen(results)#" index="i">
408                <cfif len(results[i]) and arguments.eventHandler.hasResult(results[i]) and isArray(arguments.eventHandler.results.cfNullKeyWorkaround[results[i]])>
409                        <cfloop from="1" to="#arrayLen(arguments.eventHandler.results.cfNullKeyWorkaround[results[i]])#" index="j">
410                                <cfset result = arguments.eventHandler.results.cfNullKeyWorkaround[results[i]][j] />
411                               
412                                <cfif result.redirect>
413                                        <cfset this.addTraceStatement("Result", "Explicit result ""#result.name#"" added, redirecting to event event ""#result.event#""", "<result name=""#result.name#"" do=""#result.event#"" redirect=""#true#"" />") />
414                                        <cfset forward(eventName:result.event, preserveState:result.preserveState, addToken:false, append:result.append, anchor:result.anchor) />
415                                <cfelse>
416                                        <cfset this.addTraceStatement("Result", "Explicit result ""#result.name#"" added, queueing event event ""#result.event#""", "<result name=""#result.name#"" do=""#result.event#"" />") />
417                                        <cfset addEventHandler(variables._eventHandlers[arguments.eventHandler.results.cfNullKeyWorkaround[results[i]][j].event]) />
418                                </cfif>
419                        </cfloop>
420                </cfif>
421        </cfloop>
422        <cfif len(requestFormat)>
423               
424                <cfloop from="1" to="#arrayLen(results)#" index="i">
425                        <cfif len(results[i]) and arguments.eventHandler.hasResult(results[i], requestFormat) and isArray(arguments.eventHandler.results[requestFormat][results[i]])>
426                                <cfloop from="1" to="#arrayLen(arguments.eventHandler.results[requestFormat][results[i]])#" index="j">
427                                        <cfset result = arguments.eventHandler.results[requestFormat][results[i]][j] />
428                                       
429                                        <cfif result.redirect>
430                                                <cfset this.addTraceStatement("Result", "Explicit result ""#result.name#"" added, redirecting to event event ""#result.event#""", "<result name=""#result.name#"" do=""#result.event#"" redirect=""#true#"" />") />
431                                                <cfset forward(eventName:result.event, preserveState:result.preserveState, addToken:false, append:result.append, anchor:result.anchor) />
432                                        <cfelse>
433                                                <cfset this.addTraceStatement("Result", "Explicit result ""#result.name#"" added, queing event ""#result.event#""", "<result name=""#result.name#"" do=""#result.event#"" />") />
434                                                <cfset addEventHandler(variables._eventHandlers[arguments.eventHandler.results[requestFormat][results[i]][j].event]) />
435                                        </cfif>
436                                </cfloop>
437                        </cfif>
438                </cfloop>
439        </cfif>
440       
441        <!--- Queue implicit results --->
442        <cfif structKeyExists(arguments.eventHandler.results.cfNullKeyWorkaround, "cfNullKeyWorkaround") and isArray(arguments.eventHandler.results.cfNullKeyWorkaround.cfNullKeyWorkaround)>
443                <cfset results = arguments.eventHandler.results.cfNullKeyWorkaround.cfNullKeyWorkaround />
444               
445                <cfloop from="1" to="#arrayLen(results)#" index="i">
446                                <cfset result = results[i] />
447
448                                <cfif result.redirect>
449                                        <cfset this.addTraceStatement("Result", "Implicit result redirecting to event ""#result.event#""", "<result do=""#result.event#"" redirect=""true"" />") />
450                                        <cfset forward(eventName:result.event, preserveState:result.preserveState, addToken:false, append:result.append, anchor:result.anchor) />
451                                <cfelse>
452                                        <cfset this.addTraceStatement("Result", "Implicit result queing event ""#result.event#""", "<result do=""#result.event#"" />") />
453                                        <cfset addEventHandler(variables._eventHandlers[results[i].event]) />
454                                </cfif>
455                </cfloop>
456        </cfif>
457        <cfif len(requestFormat) and structKeyExists(arguments.eventHandler.results, requestFormat)>
458                <cfif structKeyExists(arguments.eventHandler.results[requestFormat], "cfNullKeyWorkaround")
459                                        and isArray(arguments.eventHandler.results[requestFormat].cfNullKeyWorkaround)>
460                        <cfset results = arguments.eventHandler.results[requestFormat].cfNullKeyWorkaround />
461                       
462                        <cfloop from="1" to="#arrayLen(results)#" index="i">
463                                        <cfset result = results[i] />
464       
465                                        <cfif result.redirect>
466                                                <cfset this.addTraceStatement("Result", "Implicit result redirecting to event ""#result.event#""", "<result do=""#result.event#"" redirect=""true"" />") />
467                                                <cfset forward(eventName:result.event, preserveState:result.preserveState, addToken:false, append:result.append, anchor:result.anchor) />
468                                        <cfelse>
469                                                <cfset this.addTraceStatement("Result", "Implicit result queing event ""#result.event#""", "<result do=""#result.event#"" />") />
470                                                <cfset addEventHandler(variables._eventHandlers[results[i].event]) />
471                                        </cfif>
472                        </cfloop>
473                </cfif>
474        </cfif>
475               
476        <!--- Reset results --->
477        <cfset resetResults() />
478       
479        <!--- Queue views.  Repetitive on purpose - speed over elegance here. --->
480        <cfloop from="1" to="#arrayLen(arguments.eventHandler.views.cfNullKeyWorkaround)#" index="i">
481                <cfset queueView(arguments.eventHandler.views.cfNullKeyWorkaround[i]) />
482        </cfloop>
483        <cfif len(requestFormat) and structKeyExists(arguments.eventHandler.views, requestFormat)>
484                <cfloop from="1" to="#arrayLen(arguments.eventHandler.views[requestFormat])#" index="i">
485                        <cfset queueView(arguments.eventHandler.views[requestFormat][i]) />
486                </cfloop>
487        </cfif>
488</cffunction>
489
490<!--- CACHING --->
491<cffunction name="generateCacheKey" access="private" returntype="String" hint="Generate uniformal cache keys">
492        <cfargument name="cacheKey" required="true" type="string">
493        <cfargument name="cacheKeyvalues" required="true" type="string">
494       
495        <cfset var requestFormat = getValue(variables._modelGlue.getConfigSetting("requestFormatValue")) />
496        <cfset var key = "" />
497       
498        <cfif len(requestFormat)>
499                <cfset key = "#cgi.http_host#.#requestFormat#." & cacheKey />
500        <cfelse>
501                <cfset key = "#cgi.http_host#." & cacheKey />                                                                   
502        </cfif>
503       
504        <cfloop list="#cacheKeyValues#" index="i">
505                <cfset key = key & ".#i#.#getValue(i)#" />
506        </cfloop>
507
508        <cfreturn key />
509</cffunction>
510
511<!--- EVENT KNOWLEDGE --->
512<cffunction name="getCurrentEventHandler" access="public" hint="Returns the current event handler.  Modifying the instance returned alters the behavior of the event handler for all users of the application!">
513        <cfreturn variables._currentEventHandler />
514</cffunction>
515
516<cffunction name="getCurrentEventHandlerName" access="public" hint="Returns the name of the currently executing event handler.">
517        <cfreturn getCurrentEventHandler().name />
518</cffunction>
519
520<cffunction name="getInitialEventHandler" access="public" output="false" hint="Returns the initial event in this request.  Modifying the instance returned alters the behavior of the event handler for all users of the application!">
521        <cfreturn variables._initialEvent />
522</cffunction>
523
524<cffunction name="getInitialEventHandlerName" access="public" hint="Returns the name of the user-requested event handler.">
525        <cfreturn getInitialEventHandler().name />
526</cffunction>
527
528<cffunction name="getEventHandlerName" access="public" hint="Returns the name of the user-requested event handler. Here for backwards compatibility.">
529        <cfreturn getInitialEventHandlerName() />
530</cffunction>
531
532<cffunction name="getMessage" access="public" hint="Returns the name of the currently broadcast message.">
533        <cfreturn variables._currentMessage />
534</cffunction>
535
536<cffunction name="getArgument" access="public" output="false" hint="Gets a value of an argument from the currently broadcast message.">
537        <cfargument name="name" type="string" required="true" />
538        <cfargument name="default" type="string" required="false" />
539       
540        <cfif structKeyExists(arguments, "default")>
541                <cfreturn variables._currentMessage.arguments.getValue(arguments.name, arguments.default) />
542        <cfelse>
543                <cfreturn variables._currentMessage.arguments.getValue(arguments.name) />
544        </cfif>
545</cffunction>
546
547<cffunction name="argumentExists" access="public" output="false" hint="Does an argument of the given name exist in the currently broadcast message?">
548        <cfargument name="argumentName" type="string" />
549       
550        <cfreturn variables._currentMessage.arguments.exists(arguments.argumentName) />
551</cffunction>
552
553<cffunction name="getAllArguments" access="public" returnType="struct" output="false" hint="I get all arguments by reference.">
554  <cfreturn variables._currentMessage.arguments.getAll() />
555</cffunction>
556
557<!--- RESULT MANAGEMENT --->
558<cffunction name="resetResults" access="public" hint="Resets results to an empty array.">
559        <cfset variables._results = arrayNew(1) />
560</cffunction>
561
562<cffunction name="getResults" access="public" hint="Gets the result names added by a listener function.">
563        <cfreturn variables._results />
564</cffunction>
565
566<cffunction name="addResult" access="public" hint="Adds a result, by name, to the result queue.">
567        <cfargument name="resultName" type="string" hint="The name of the result (e.g., ""formInvalid"" or the like) to add." />
568       
569        <cfset var results = getResults() />
570        <cfset var format = "" />
571        <cfset var formatList = listappend(getValue("requestFormat", ""),  "cfNullKeyWorkaround") />
572        <cfset var i = "" />
573        <cfset var eh = getCurrentEventHandler() />
574        <cfset var result = "" />
575       
576       
577        <cfset addTraceStatement("Message Listener", "A named result ""#arguments.resultName#"" has been added.") />
578       
579        <cfloop list="#formatList#" index="format">
580                <cfif structkeyexists(eh.results, format) AND structKeyExists(eh.results[format], arguments.resultName)>       
581                        <cfloop from="1" to="#arrayLen(eh.results[format][arguments.resultName])#" index="i">
582                                <cfset result = eh.results[format][arguments.resultName][i] />
583                               
584                                <cfif result.redirect>
585                                        <cfset this.addTraceStatement("Result", "Explicit result redirecting to event ""#result.event#""", "<result do=""#result.event#"" redirect=""true"" />") />
586                                        <cfset forward(eventName:result.event, preserveState:result.preserveState, addToken:false, append:result.append, anchor:result.anchor) />
587                                </cfif>
588                        </cfloop>
589                </cfif>
590        </cfloop>
591       
592        <cfset arrayAppend(variables._results, arguments.resultName) />
593</cffunction>
594
595<!--- LOCATION MANAGEMENT --->
596<cffunction name="formatUrlParameter" output="false" hint="Formats a key/value pair for the URL.">
597        <cfargument name="key" />
598        <cfargument name="value" />
599
600        <cfset var urlManager = variables._modelglue.getInternalBean("modelglue.urlManager") />
601
602        <cfreturn urlManager.formatUrlParameter(argumentCollection=arguments) />
603</cffunction>
604
605<cffunction name="linkTo" access="public" hint="Creates URLs using the configured URL manager." output="false">
606        <cfargument name="eventName" type="string" hint="Name of the event to forward to." />
607        <cfargument name="append" default="" hint="The list of values to append." />
608        <cfargument name="anchor" default="" hint="The anchor literal for the resultant URL." />
609
610        <cfset var urlManager = variables._modelglue.getInternalBean("modelglue.urlManager") />
611
612        <cfreturn urlManager.linkTo(arguments.eventName, arguments.append, arguments.anchor, this) />
613</cffunction>
614
615<cffunction name="forwardToUrl" access="public" hint="Forwards to a given URL, optionally storing state across the redirect.">
616        <cfargument name="url" type="string" hint="The URL to redirect to using <cflocation />">
617        <cfargument name="preserveState" type="boolean" required="false" default="false" hint="Preserve state across the redirect?" />
618        <cfargument name="addToken" type="boolean" default="false" hint="Should session tokens be added to the url?">
619       
620        <cfif arguments.preserveState>
621                <cfset saveState() />
622        </cfif>
623       
624        <cflocation url="#arguments.url#" addToken="#arguments.addToken#" />
625</cffunction>
626
627<cffunction name="forward" access="public" hint="Forwards the request to a given event name, optionally storing state across the redirect.">
628        <cfargument name="eventName" type="string" hint="Name of the event to forward to." />
629        <cfargument name="append" default="" hint="The list of values to append." />
630        <cfargument name="preserveState" type="boolean" required="false" default="false" hint="Preserve state across the redirect?" />
631        <cfargument name="anchor" default="" hint="The anchor literal for the resultant URL." />
632        <cfargument name="addToken" type="boolean" default="false" hint="Should session tokens be added to the url?">
633
634        <!--- Was:
635        <cfargument name="eventName" type="string" hint="Name of the event to forward to." />
636        <cfargument name="preserveState" type="boolean" required="false" default="false" hint="Preserve state across the redirect?" />
637        <cfargument name="addToken" type="boolean" default="false" hint="Should session tokens be added to the url?">
638        <cfargument name="append" default="" hint="The list of values to append." />
639        <cfargument name="anchor" default="" hint="The anchor literal for the resultant URL." />
640        --->
641               
642        <cfset var urlManager = variables._modelglue.getInternalBean("modelglue.urlManager") />
643       
644        <cfset var targeturl  = urlManager.linkTo(arguments.eventName, arguments.append, arguments.anchor, this) />
645       
646        <cfset forwardToUrl(targeturl, arguments.preserveState, arguments.addToken) />
647</cffunction>
648
649<!--- STATE (DATA BUS) MANAGEMENT --->
650<cffunction name="getAllValues" access="public" returnType="struct" output="false" hint="DEPRECATED:  Use getAll().  Still supported for reverse compatibility.  I return the entire view's current state.">
651  <cfreturn getAll() />
652</cffunction>
653
654<cffunction name="getAll" access="public" returnType="struct" output="false" hint="I get all values by reference.">
655  <cfreturn variables._state.getAll() />
656</cffunction>
657
658<cffunction name="setValue" access="public" returnType="void" output="false" hint="I set a value in the collection.">
659        <cfargument name="name" type="string" required="true" hint="I am the name of the value.">
660        <cfargument name="value" type="any" required="true" hint="I am the value.">
661
662        <cfreturn variables._state.setValue(argumentCollection=arguments) />
663</cffunction>
664
665<cffunction name="getValue" access="public" returnType="any" output="false" hint="I get a value from the collection.">
666  <cfargument name="name" hint="I am the name of the value.">
667  <cfargument name="default" required="false" type="any" hint="I am a default value to set and return if the value does not exist." />
668
669        <cfreturn variables._state.getValue(argumentCollection=arguments) />
670</cffunction>
671
672<cffunction name="removeValue" access="public" returnType="void" output="false" hint="I remove a value from the collection.">
673  <cfargument name="name" type="string" required="true" hint="I am the name of the value.">
674       
675        <cfreturn variables._state.removeValue(argumentCollection=arguments) />
676</cffunction>
677
678<cffunction name="valueExists" access="public" returnType="boolean" output="false" hint="DEPRECATED:  Use exists().  Still supported for reverse compatibility.  I state if a value exists.">
679  <cfargument name="name" type="string" required="true" hint="I am the name of the value.">
680
681        <cfreturn exists(argumentCollection=arguments) />
682</cffunction>
683
684<cffunction name="exists" access="public" returnType="boolean" output="false" hint="I state if a value exists.">
685  <cfargument name="name" type="string" required="true" hint="I am the name of the value.">
686
687        <cfreturn variables._state.exists(argumentCollection=arguments) />
688</cffunction>
689
690<cffunction name="merge" access="public" returnType="void" output="false" hint="I merge an entire struct into the collection.">
691  <cfargument name="struct" type="struct" required="true" hint="I am the struct to merge." />
692
693        <cfreturn variables._state.merge(argumentCollection=arguments) />
694</cffunction>
695
696<cffunction name="saveState" access="public" returntype="void" output="false" hint="I save all state to session">
697        <cfset variables._statePersister.save(this) />
698</cffunction>
699
700<cffunction name="loadState" access="public" returntype="void" output="false" hint="I load state from session.">
701        <cftry>
702                <cfset variables._statePersister.load(this) />
703                <cfcatch></cfcatch>
704        </cftry>
705</cffunction>
706
707<cffunction name="copyToScope" output="false" access="public" returntype="void" hint="I copy values from the event into the desired scope">
708        <cfargument name="scope" type="struct" required="true"/>
709        <cfargument name="ListOfEventKeys" type="string" default="true"/>
710        <cfargument name="ArrayOfDefaults" type="any" default="#arrayNew(1)#"/>
711        <cfset var EventKeyArray =  listToArray( arguments.ListOfEventKeys ) />
712        <cfset var thisEventKeyArray = "" />
713        <cfset var ScopeContext = "" />
714        <cfset var i = "" />
715        <cfset var j = "" />
716        <cfif isSimpleValue( arguments.ArrayOfDefaults ) IS true>
717                <cfset arguments.ArrayOfDefaults = listToArray( arguments.ArrayOfDefaults ) />
718        </cfif>
719        <cfloop from="1" to="#arrayLen( EventKeyArray )#" index="i">
720                <cfset thisEventKeyArray = listToArray( EventKeyArray[ i ], ".") />
721                <!--- make sure the scope context is set so we can dot-walk up the variable --->
722                <cfset ScopeContext = arguments.Scope />
723               
724                <cfloop from="1" to="#arrayLen( thisEventKeyArray)#" index="j">
725                        <cfif structKeyExists( ScopeContext, thisEventKeyArray[j]) IS false OR isStruct( ScopeContext[ thisEventKeyArray[j] ]  ) IS false >
726                                <!--- so we don't have something we can attach keys to, lets make something--->
727                                <cfset ScopeContext[ thisEventKeyArray[j] ] = structNew() />
728                        </cfif>
729                        <cfif j IS arrayLen( thisEventKeyArray ) AND i LTE arrayLen( arguments.ArrayOfDefaults )>
730                                <!--- if we are done dot-walking, and have a default, lets use it. We should be done in the inner loop after this---->
731                                <cfset ScopeContext[ thisEventKeyArray[j] ] = variables._state.getValue( EventKeyArray[i], arguments.ArrayOfDefaults[i] ) />
732                        <cfelseif j IS arrayLen( thisEventKeyArray )>
733                                <!--- ok, done dot-walking, grab something from the event. We should be done in the inner loop after this--->
734                                <cfset ScopeContext[ thisEventKeyArray[j] ] = variables._state.getValue( EventKeyArray[i] ) />
735                        <cfelse>
736                                <!--- walk down the dot path another level and go around the merry go round again --->
737                                <cfset ScopeContext = ScopeContext[ thisEventKeyArray[j] ] />
738                        </cfif>
739                </cfloop>       
740        </cfloop>
741</cffunction>
742
743<!--- VIEW MANAGEMENT --->
744<cffunction name="getViewCollection" access="public" output="false" hint="I return the view collection for the event request.">
745        <cfreturn variables._viewCollection />
746</cffunction>
747
748<cffunction name="addView" access="public" returnType="void" output="false" hint="I add a rendered view to the view collection.">
749  <cfargument name="key" type="string" required="true" hint="I am the name of the view to add.">
750  <cfargument name="content" type="string" required="true" hint="I am the HTML of the view.">
751  <cfargument name="append" type="boolean" required="false" default="false" hint="Should the HTML be appended on to an existing view of the same name?">
752       
753        <cfset variables._viewCollection.addRenderedView(argumentCollection=arguments) />
754</cffunction>
755
756<cffunction name="getView" access="public" output="false" hint="I get a rendered view by name.">
757  <cfargument name="name" required="true" hint="I am the name of the view to get.">
758       
759        <cfreturn variables._viewCollection.getView(argumentCollection=arguments) />
760</cffunction>
761
762<!--- Edit by Cristian Costantini for managing view cache - return content render --->
763<cffunction name="renderView" access="public" output="false" hint="I render a view into the view collection.">
764        <cfargument name="view" type="any" hint="I am the view to render.">
765        <cfargument name="addToEh" type="boolean" default="true" hint="If view is cached no need to add this here">
766
767        <cfset var content = variables._viewRenderer.renderView(this, arguments.view, variables._helpers) />
768       
769        <cfif addToEh>
770                <cfset addView(arguments.view.name, content, arguments.view.append) />
771        </cfif>
772       
773        <cfreturn content />
774</cffunction>
775
776<cffunction name="queueView" access="private" returnType="void" output="false" hint="I add a view to the queue of views to render.">
777  <cfargument name="view" type="any" hint="I am the view to queue.">
778
779        <cfset var link = structNew() />
780
781        <cfset link.view = arguments.view />
782        <cfset link.next = "" />
783
784        <cfset addTraceStatement("View Queue", "View queued: #view.template#") />
785       
786        <cfif not isStruct(variables._nextView)>
787                <cfset variables._nextView = link />
788                <cfset variables._lastView = link />
789        <cfelse>
790                <cfset variables._lastView.next = link />
791                <cfset variables._lastView = link />
792        </cfif>
793       
794</cffunction>
795
796<cffunction name="getNextView" access="private" output="false" hint="Returns the next view in the queue.">
797        <cfset var view = variables._nextView.view />
798        <cfset variables._nextView = variables._nextView.next />
799
800        <cfreturn view />
801</cffunction>
802
803<cffunction name="renderViewQueue" access="private" returnType="void" output="false" hint="I render all views currently in the queue.">
804        <cfset var view = "" />
805
806        <cfloop condition="isStruct(variables._nextView)">
807                <cfset view = getNextView() />
808
809                <cfset this.addTraceStatement("Views", "Rendering view ""#view.name#"" (#view.template#)", "<include name=""#view.name#"" template=""#view.template#"" />") />
810
811                <cfset renderView(view) />
812        </cfloop>
813</cffunction>
814
815<cffunction name="getLastRendereredView" access="public" returntype="string" output="false" hint="Gets the last renderered view content.">
816        <cfreturn variables._viewCollection.getFinalView() />
817</cffunction>
818
819<!--- TRACE LOG --->
820<cffunction name="getTrace" access="public" returntype="array" output="false" hint="Gets the trace log for the event context.">
821        <cfreturn this.log />
822</cffunction>
823
824<cffunction name="getCreated" access="public" returntype="numeric" output="false" hint="Gets the tick count for when this event context was initialized.">
825        <cfreturn this.created />
826</cffunction>
827
828<cffunction name="addTraceStatement" access="public" returnType="Void" output="false" hint="I add a message to the trace log.">
829  <cfargument name="type" type="string" />
830  <cfargument name="message" type="any" />
831  <cfargument name="tag" type="string" default="" />
832  <cfargument name="traceType" type="string" default="OK" />
833
834        <cfset arguments.time = getTickCount() />
835       
836        <cfif not isSimpleValue(arguments.message)>
837                <cfsavecontent variable="arguments.message"><cfdump var="#arguments.message#" /></cfsavecontent>
838        </cfif>
839       
840        <cfset variables._logWriter.write(this, arguments) />
841</cffunction>
842
843
844<!--- BEAN POPULATION --->
845<cffunction name="makeEventBean" access="public" returnType="any" output="false" hint="Populates a CFC instance's properties with like-named event values (or the list of values passed in the VALUES argument).">
846        <cfargument name="target" type="any" hint="An instance of a CFC or the name of a CFC to instantiate and populate." />
847        <cfargument name="fields" type="string" default="" hint="(Optional) List of properties to populate." />
848       
849        <cfset var source = "" />
850        <cfset var i = "" />
851       
852        <cfreturn variables._beanPopulator.populate(arguments.target, variables._state, fields) />
853</cffunction>
854
855</cfcomponent>