Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:17
Show Gist options
  • Save bennadel/9759690 to your computer and use it in GitHub Desktop.
Save bennadel/9759690 to your computer and use it in GitHub Desktop.
Monitoring ColdFusion Thread Activity And Status After Redirect
<!--- 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 />
<!--- 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>
<!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>
<!--- 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