Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
Contact Doug!
Help a Brutha Out!
help a brutha out!
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Florida web site design



Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< February, 2010 >>
SMTWTFS
123456
78910111213
14151617181920
21222324252627
28
Search Blog

Recent Comments
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

04 January 2010
Finally Found a Use for CFTHREAD

You know how ColdFusion is so robust that oftentimes there are those tags and functions that exist, yet you've never once had an occasion to use them? Well, today I used a tag for which I've previously not had a need: cfthread. I figured I'd share my use case and implementation in case it piques the curiosity of other CFTHREAD virgins.

The Scenario

Our company has a client who requested that we automate the portion of the process whereby a document originating from them is returned to them by us after we're finished with it. They've built some kind of "folder watching" process on their end, and so asked if we could sFTP the final file to their server. Digging in to the legacy code that performs the final processing, I identified the place where I could surgically place the code to perform this work. But, I didn't want to make the user to encounter any additional delays in page load time.

The Solution
Enter CFTHREAD, a sweet little tag that will allow me to very simply execute a chunk of code asynchronously and unattached from the page it resides within. By simply encompassing the autonomous bit of code within cfthread tags, CF will run it in parallel to the page itself. But, though I did want the upload itself to occur without hindering the execution of the remainder of the page, I DID want to give my user a final message indicating whether or not the upload was successful. In other, more relevant words, i wanted this autonomous process to join back up with the original request at the end, and update a message div on the page appropriately.

Since my standalone process was for sFTP purposes, I'm going to use that snippet of code in my example. Here is the section I needed to run on its own:

<cfset ftpingfile = false />
<!--- if this is a targeted client AND the estimate was approved, FTP it to the target site --->
<cfif thisCarrier IS targCarrier AND (PDFMAccRjt IS "A" OR PDFCAccRjt IS "A")>
 <!--- fetch the ftp settings for client... --->
 <cfquery name="qryGetSettings" datasource="#dsn#">
  select ftpsite,ftpusername,ftppassword,ftpDestFolder
  from clientTable
  where carrier = <cfqueryparam value="#targCarrier#" cfsqltype="cf_sql_varchar" />
 </cfquery>
 <cfif qryGetSettings.recordcount eq 1 AND qryGetSettings.ftpsite IS NOT "">
   <!--- get path to the PDF file --->
   <cfset pathToPDF = "#expandpath("\PDF")#\#InvHolder#.pdf" />
   <cfif fileexists(pathToPDF)>
    <cfset ftpingfile = true /><!--- need this flag for evaluating things at the end of this template --->
    <!--- create new file name... --->
    <cfset newfilename = "#claimnum#-01--#dateformat(now(),"mmddyyyy")##timeformat(now(),"HHmmss")#.pdf" />
    <cfthread action="run" name="FTPit">
     <cfoutput>
      <cfset objFTPProperties = {
       Server = "#qryGetSettings.ftpsite#",
       Username = "#qryGetSettings.ftpusername#",
       Password = "#qryGetSettings.ftppassword#",
       Secure = true
      } />
     </cfoutput>
     <cfftp
      action="open"
      connection="objConnection"
      attributeCollection="#objFTPProperties#"
      />
      <cfftp
      action="putfile"
      connection="objConnection"
      localfile="#pathToPDF#"
      remotefile="#qryGetSettings.ftpDestFolder##newfilename#"
      transfermode="auto"
     />
     <cfftp
      action="close"
      connection="objConnection"
     />
    </cfthread>
   </cfif><!--- if the estimate pdf exists --->
 </cfif><!--- if we found this carrier's ftp info --->
</cfif> 

 

 

 

 

So, while the above code is executing (assuming we met our conditional criteria), the remainder of the page runs. At the end of the template, I use the following code to join the upload request back to the original request:

<!-- div to hold status message for parallel process... -->
<div id="uploading" name="uploading" style="color:red;">
 <cfif ftpingfile> 
  <br><br>Please wait. Uploading file to #targCarrier#... <img src="images/spinner.gif" align="absmiddle" /><br><br>
 <cfelse>
  <br><br>NO PDF UPLOAD ATTEMPTED. EITHER NO PDF WAS FOUND TO UPLOAD, OR THE CLIENT HAS NO FTP SETTINGS IN THE SYSTEM<br><br>
 </cfif>
</div>

<cfflush><!--- output the content of the page thus far to the browser so the user has something to see/do until our process finishes --->

<!--- if we attemptd to ftp a file... --->
<cfif ftpingfile>
 <cfthread action="join" name="FTPit" />
 <cfif cfthread.FTPit.status IS "Completed">
  <script>
   document.getElementById('uploading').innerHTML = "<br><br>Estimate Successfully Uploaded to <cfoutput>#targCarrier#</cfoutput>!<br><br>";
  </script>
 <cfelse>
  <script>
   document.getElementById('uploading').innerHTML = "<br><br><strong>There was a problem uploading the estimate. Here are the details:</strong><br><cfoutput>#JSStringFormat(cfthread.FTPit.error.detail)#</cfoutput><br><br>";
  </script>
  
  <!--- send details of failed attempt to Doug --->
  <cfsavecontent variable="failedFTP">
   <cfdump var="#cfthread#">
  </cfsavecontent>
  <cfmail to="
dboude@adomain.com" from="administrator@adomain.com" type="html" subject="failed FTP attempt for invoice #InvHolder#">
   FTP process failed. Here are the details:
   <br>
   <cfoutput>#failedFTP#</cfoutput>
  </cfmail>
 </cfif>
</cfif>

That's it! So while the user is looking at and potentially interacting with their page as usual, the status message div I placed will be updated appropriately as soon as the upload finishes. Just to clarify, the browser will still show its "loading" status until the threaded ftp process finishes, since we told CF to join it back to the original request. But, because we joined it at the end, and we cfflushed prior to that, the user will see their typical output in the meantime.

Posted by dougboude at 4:15 PM | PRINT THIS POST! | Link | 3 comments



21 December 2009
A SWEET Little MySQL Function: Group_Concat

The Scenario

You're writing code to perform authentication for a web-based app. Your users live in one table (Site_User), your list of site permissions live in another table (systemRoles), and the two are related via a junction table (jctUserRole) that manages the many to many relationship there. Your backend database: MySQL version 5 or better.

So, in an ideal world, you will be able to write a single query that will both authenticate the user AND retrieve a comma delimited list of their roles contained in a single field; if their credentials are good, your result will only have a single record in it. Typically, however, because of the way many to many relationships are normalized, the "one query" you want will be returning multiple rows...one row for each permission the authenticating user is related to. For example, here is the typical way one would write the authenticating query:


  SELECT u.id AS userid, u.firstname, u.lastname, r.rolename
  FROM Site_User u
  INNER JOIN jctUserRole j ON j.userID = u.id
  INNER JOIN systemroles r ON r.id = j.roleID
  WHERE u.username =  'username'
  AND u.password =  'password'

 

This produces a result that looks like this:

standard mysql query for authentication

We would then be required to write additional code in order to more properly "package" up this user's list of permissions for use within the system.

But enter the wonderful world of MySQL 5, and the 'group_concat' function. Group_concat allows you to concatenate values into a list based on a specified grouping, and will produce a list of unique values delimited by whatever character you choose (but defaults to a comma). So transforming the above query just a little bit, we get:

 

  SELECT u.id AS userid, u.firstname, u.lastname, GROUP_CONCAT( r.rolename ) AS roles
  FROM Site_User u
  INNER JOIN jctUserRole j ON j.userID = u.id
  INNER JOIN systemroles r ON r.id = j.roleID
  WHERE r.username =  'username'
  AND r.password =  'password'
  GROUP BY u.id, u.firstname, u.lastname

 

And a result set that now looks like this:

mysql authentication query using group_concat

Man, SO much cleaner and nicer, eh? Now you can simply treat the "roles" value as a list...explode it into an array if you want, or leverage ColdFusion's myriad of List functions to interact with it.

Since discovering group_concat, I've found several other uses for it as well, particularly when producing reports for my internal clients...it saves me a lot of iterating over result sets in order to produce value summaries.

You can read the documentation for this function here

Posted by dougboude at 11:29 AM | PRINT THIS POST! | Link | 5 comments