Created
March 25, 2014 11:17
-
-
Save bennadel/9759690 to your computer and use it in GitHub Desktop.
Monitoring ColdFusion Thread Activity And Status After Redirect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--- Set the status code for the client. ---> | |
<cfheader | |
statuscode="302" | |
statustext="Moved Temporarily" | |
/> | |
<!--- Define the location to redirect to. ---> | |
<cfheader | |
name="location" | |
value="./confirmation.cfm" | |
/> | |
<!--- Flush the headers to the client. ---> | |
<cfflush /> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--- Param the URL variables. ---> | |
<cfparam name="url.launch" type="boolean" default="false" /> | |
<!--- Check to see if the page has been submitted. ---> | |
<cfif url.launch> | |
<!--- | |
We are about to launch some threads that we want to | |
keep track of, as such, let's set a flag in the Application | |
scope. | |
NOTE: We sould not typically store user-specific information | |
in the "Application" scope, but this is just for ease of | |
demonstration purposes. | |
---> | |
<cfset application.isProcessing = true /> | |
<!--- | |
We're going to keep track of the number of threads we will | |
be processing with this request. | |
---> | |
<cfset application.threadCount = 10 /> | |
<!--- | |
We're going to keep track of the number of threads are | |
actively being processed. This will start at the "thread | |
count" and decrement as each thread completes. | |
---> | |
<cfset application.activeThreads = application.threadCount /> | |
<!--- | |
We going to keep track of the outcome of each thread after | |
it has finished executing. | |
---> | |
<cfset application.threads = [] /> | |
<!--- | |
Iterate up to the total thread acount and launch as many new | |
threads as needed. | |
---> | |
<cfloop | |
index="threadIndex" | |
from="1" | |
to="#application.threadCount#" | |
step="1"> | |
<!--- | |
Launch an asynchronous thread. This will execute in | |
parallel with the primary page and will take almost no | |
time do define. | |
---> | |
<cfthread | |
name="thread#threadIndex#" | |
action="run" | |
index="#threadIndex#"> | |
<!--- | |
Randomly sleep the thread for several seconds to | |
demonstrate that some heavy processing might be | |
taking place. | |
---> | |
<cfthread | |
action="sleep" | |
duration="#randRange( 2000, 3000 )#" | |
/> | |
<!--- | |
At this point, the thread has finished performing | |
whatever heavy processing it was gonna be doing. | |
For tracking sake, let's decrement the active thread | |
acount. Because this will create potential race | |
conditions, we'll lock this variable. | |
---> | |
<cflock | |
name="decrementActiveThreads" | |
type="exclusive" | |
timeout="3"> | |
<!--- Decrement thread count. ---> | |
<cfset application.activeThreads-- /> | |
</cflock> | |
</cfthread> | |
</cfloop> | |
<!--- ------------------------------------------------- ---> | |
<!--- ------------------------------------------------- ---> | |
<!--- | |
Now that we have defined our threads, we want to provide | |
immediate feedback to the user. We are going to do this | |
in the form of a re-location. However, we cannot use a | |
traditional CFLocation as this will abort the page request. | |
Rather, we'll use CFHeader to define the CFLocation action | |
without implying the abort. | |
---> | |
<cfheader | |
statuscode="302" | |
statustext="Moved Temporarily" | |
/> | |
<!--- Define the location to redirect to. ---> | |
<cfheader | |
name="location" | |
value="./confirmation.cfm" | |
/> | |
<!--- | |
Tell the browser to reset the content and define the | |
mime-type for the client. | |
---> | |
<cfcontent type="text/html" /> | |
<!--- | |
Ok, now here's the really funky-ass part. We need to | |
"convince" the server to actually flush data to the screen. | |
Even if we call CFFlush, the server won't push content to | |
the client unless it sees that it has enough. As such, we | |
have to create enough white-space to convice the browser that | |
it is prudent to flush the headers. | |
NOTE: This was not an issue with earlier versions of | |
ColdFusion, but seems to be popping up in CF8. | |
---> | |
<cfoutput>#repeatString( " ", 73729 )#</cfoutput> | |
<!--- | |
Now that have set the headers and provided enough "contente," | |
we have to comit the response to the client. This will cause | |
the headers to be passed back. | |
---> | |
<cfflush /> | |
<!--- ------------------------------------------------- ---> | |
<!--- ------------------------------------------------- ---> | |
<!--- | |
At this point, the User should have been redirected to the | |
confirmation page; as such, the rest of the processing will | |
not affect the user's experience. Let's now join all of the | |
outstanding threads back to the page so we can use them. | |
NOTE: We could have used the Name attribute to join a single | |
thread; but, without the name attribute, this will cause all | |
threads defined on this page to be joined. | |
---> | |
<cfthread action="join" /> | |
<!--- | |
Now that the threads have been joined, loop over the threads | |
and add them to the array of processed threads. | |
---> | |
<cfloop | |
item="threadName" | |
collection="#cfthread#"> | |
<!--- Add this thread to the array. ---> | |
<cfset arrayAppend( | |
application.threads, | |
cfthread[ threadName ] | |
) /> | |
</cfloop> | |
<!--- Flag that the threads are no longer being processed. ---> | |
<cfset application.isProcessing = false /> | |
<!--- | |
At this point, we need to abort the page. Because we did a | |
"soft" relocation, that page did not abort as it normally | |
would have with a CFLocation. Now that we are done with all | |
of our thread logic, however, we must manually abort. | |
---> | |
<cfabort /> | |
</cfif> | |
<!DOCTYPE HTML> | |
<html> | |
<head> | |
<title>ColdFusion CFThread Post-Redirect Monitoring</title> | |
</head> | |
<body> | |
<h1> | |
ColdFusion CFThread Post-Redirect Monitoring | |
</h1> | |
<p> | |
<a href="./test.cfm?launch=1">Launch some threads</a> | |
already! | |
</p> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html> | |
<head> | |
<title>ColdFusion CFThread Post-Redirect Monitoring</title> | |
<script type="text/javascript" src="jquery-1.4.2.min.js"></script> | |
<script type="text/javascript"> | |
// When the DOM is ready, initialize. | |
jQuery(function( $ ){ | |
// Get the updates element. | |
var updates = $( "#updates" ); | |
// Define a function to get the updates. | |
getUpdates = function(){ | |
// Get the thread-processing updates from the server. | |
$.ajax({ | |
type: "get", | |
url: "updates.cfm", | |
dataType: "json", | |
success: function( results ){ | |
// Output the updates. | |
updates.html( results.HTML ); | |
// Check to see if the updates are still | |
// processing. | |
if (results.ISPROCESSING){ | |
// Make another request to get updates. | |
// Give the server a tiny rest. | |
setTimeout( | |
getUpdates, | |
500 | |
); | |
} | |
} | |
}); | |
}; | |
// Get the updates. | |
getUpdates(); | |
}); | |
</script> | |
</head> | |
<body> | |
<h1> | |
Your Threads Are Being Proccessed. | |
</h1> | |
<p id="updates"> | |
<!--- Processing updates will go here. ---> | |
</p> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--- Create the return struct. ---> | |
<cfset response = { | |
isProcessing = application.isProcessing, | |
html = "" | |
} /> | |
<!--- | |
Check to see if the if the threads are still processing so we | |
can see what kind of output to build. | |
---> | |
<cfif application.isProcessing> | |
<!--- Save the processing output. ---> | |
<cfsavecontent variable="response.html"> | |
<cfoutput> | |
#application.activeThreads# thread(s) still need to | |
be processed. | |
</cfoutput> | |
</cfsavecontent> | |
<cfelse> | |
<!--- Save the results output. ---> | |
<cfsavecontent variable="response.html"> | |
<cfoutput> | |
<!--- Loop over the threads to output data. ---> | |
<cfloop | |
index="thread" | |
array="#application.threads#"> | |
#thread.name#: | |
#thread.status# | |
(#numberFormat( thread.elapsedTime )# milliseconds)<br /> | |
</cfloop> | |
</cfoutput> | |
</cfsavecontent> | |
</cfif> | |
<!--- Convert the response to a binary JSON response. ---> | |
<cfset binaryResponse = toBinary( | |
toBase64( | |
serializeJSON( response ) | |
) | |
) /> | |
<!--- Stream the response back to the client. ---> | |
<cfcontent | |
type="text/json" | |
variable="#binaryResponse#" | |
/> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment