|
|
YOUR FEEDBACK
SOA World Conference
Virtualization Conference $200 Savings Expire May 16, 2008... – Register Today! Did you read today's front page stories & breaking news?
SYS-CON.TV SYS-CON.TV WEBCASTS |
TOP COLDFUSION LINKS Journeyman CF
Toward Better Error Handling - Part 4
By: Charlie Arehart
Digg This!
If you've programmed in a modern programming language (Java, C, JavaScript, or even stored procedure languages), you've probably heard of exception handling via try/catch statements. ColdFusion supports the same capability. If you're new to using them, I'll explain how they're used, and even experienced programmers may learn a thing or two about CF's particular use of try/catch exception handling. This is the fourth in a series on error handling (see CFDJ, Vol. 2, issues 10 and 12, and Vol. 3, issue 2). In the three previous installments, we've focused on handling errors at the page level. Something goes wrong, and you want to do something other than have the user see the typical CF error message. That rather cryptic, black-on-white message is great for developers but it's not useful to end users, and we discussed several ways to improve on that. In this article, we'll move a level further down the "error-handling hierarchy" introduced in Part 2 (see Figure 1). With try/catch handling, or specifically the CFTRY/CFCATCH tags, we're generally designing a way to detect and handle an error that we suspect may happen at runtime, but that we can't know will always happen.
Why a CFTRY?
In these or any circumstance like them, if you know an error may happen, you can anticipate and handle it. Of course, if you've implemented a CFERROR tag for your application (as discussed in the last issue), then it could handle the error, but that will be on a more global scale for the entire page or, indeed, application. But if some particular tag (or function) or set of them may cause a problem that you can at least contemplate, then the CFTRY/CFCATCH pair will give you much finer control in handling the error. Indeed, handling the error may mean simply ignoring it, as we'll see later.
A Simple Example
<CFTRY>A specific example might be: <CFTRY>There are a few very important things to note about what goes on within this process.
What Goes on Within the CFTRY/CFCATCH Process
If No Error Occurs
That's the really cool thing about TRY/CATCH processing: it's as if we now have return codes to test for CF tags. Of course, some processes (like CFQUERY) do in fact return a code (a database error code, that is), but we've never had a way to trap that error before Release 4. More on that in a moment. But just keep in mind that if an error does not occur, then processing simply skips the CFCATCH and continues after the /CFTRY.
If An Error Occurs
CFCATCH Error Variables
There are a few other variables available when specific kinds of errors are being handled, including ErrNumber, LockName, LockOperation, MissingFileName, Native-ErrorCode, SQLState, ErrorCode, and ExtendedInfo. The CF documentation (the CFML Language Reference) contains more detail on each of these. Keep in mind, too, that you have full access to all the other CF variables including CGI.HTTP_ USER_AGENT (in place of ERROR.BROWSER), so the loss of the "ERROR." variables is easily supplemented by the full range of CF variables providing the same information (indeed, the only reason the "ERROR." variables were created was because the original CFERROR handler couldn't process CF tags or variables, as was discussed in the last two articles). Before leaving the subject of CFCATCH variables, note something about how CF will catch an exception thrown by a Java object (called via CFOBJECT). From the 4.5 New Features document: "ColdFusion checks if the exception thrown is the method exception and stores the classname of the exception in the CFCATCH.MESSAGE variable."
Anticipating Multiple Exceptions
<CFCATCH TYPE="Database">or <CFCATCH TYPE="Lock">More types are provided in Table 2. The last item - "custom_type" - literally means any phrase at all. As we'll learn later, your code can "throw" an exception within a CFTRY to be caught by CFCATCH. In such a case, you can choose to create your own "type" for the error-handling mechanism to look for. Finally, be aware that there's also a possible type of "unknown" for certain exceptions caught by TYPE="ANY". And there are a whole host of types whose names start with "COM.Allaire.ColdFusion" that may be returned under certain conditions. Again, you don't need to worry about catching a specific type if you're simply trying to catch any error that occurs. Leave the type off. Remember, you can view the type in the available CFCATCH.TYPE variable.
A Change in Processing Multiple CFCATCHes in 4.51
CFCATCH selection logic in ColdFusion 4.5.1 differs slightly from ColdFusion 4.0.x. In Cold-Fusion 4.0.x, the first matching CFCATCH block encountered would be selected to handle an exception. ColdFusion 4.5 scans a CFTRY tag's entire list of CFCATCH blocks to find the closest match. For example, if a CFTRY tag has a CFCATCH TYPE= TEMPLATE block, followed by a CFCATCH TYPE=MISSINGINCLUDE block, ColdFusion 4.0.x will select the TEMPLATE block to handle a MISSINGINCLUDE exception, while ColdFusion 4.5.1 will select the MISSINGINCLUDE block. ColdFusion 4.5.1 can be reset to handle a template using ColdFusion 4.0.x rules by setting the compatibility setting, <cfsetting catchexceptionsbypattern=no>.
What You Can and Can't Catch
Don't Wrap Entire Program in a Try!
Can't Catch Syntax Errors
That stresses the point that CFTRY is for catching "runtime" or execution errors. It can't catch compilation or syntax errors. (Again, CFERROR can help us here, but see the end of the article for more on when it may or may not catch syntax errors.)
Use It to Ignore Array Existence Errors
Use It to Catch Unavoidable Database Errors
Be Careful Using It to Catch Avoidable Database Errors
It may seem that it's saving you from having to do a test to see if the record already exists (in which case an update is the action to perform), but consider that the error causes the database connection to be lost. The time involved to re-create that connection may outweigh the performance gain from avoiding the quick check for the existence of a given primary key value. Just do a "Select keyname from tablename where keyname=value" (as opposed to a more wasteful "select *"), which should execute very quickly. (Often you can program your interface so that your insert/update page is passed information that indicates whether the process is to be an insert or update, with an update being indicated if the primary key is passed from a hidden form field.) The same consideration about not overusing CFTRY for avoidable database errors applies when doing an insert or update to a record having a column with a uniqueness constraint. Consider a userID column. There should be only one. Whether it's the primary key or has a "unique index" on the column, there can never be more than one record with a given userID. So what can you do as you contemplate inserting or updating that column with a given value, since you don't want to proceed if there's already a record with that value? The simple solution is to just do a select on that value to see if it already exists and report an error if it does. Some clever programmers wrap the insert or update in a try, and catch and report the uniqueness constraint error if the value already exists (which would violate the uniqueness constraint and cause the insert/update to fail). Their thinking is that database will do the same check for whether the value already exists on the insert/update, so why bother suffering the redundancy of doing it first themselves? But the cost in the lost DB connection likely outweighs the savings of just checking if the value already exists. Your mileage may vary, but consider that uniqueness is enforced via an index, so the check you do first will be very fast. Plus, while the insert or update will proceed to do another check for that value's existence, consider that the check you've done first will have caused that result to be cached in the database engine by the time the second check is done, making the "redundancy" even less painful. (Thanks to Daryl Banttari, senior consultant for Macromedia Consulting, for offering that insight.)
Some Quirks in Studio
You may also notice that if you look for CFCATCH in the CFML Language Reference's alphabetical listing of tags, you won't find it. It's listed as CFTRY CFCATCH. That may make sense since it's really a subtag to CFTRY, but it's confusing at first. Worse still, pressing F1 on CFCATCH won't produce the expected help for the same reason.
More to Come
And then there are quirks in the way a sitewide error handler will override a CFERROR TYPE="Request", as well as how a sitewide missing template handler won't catch a missing file in a CFINCLUDE or CFMODULE (you have to code your own CFERROR TYPE="EXCEPTION"EXCEPTION="MISSINGINCLUDE"). There are also quirks when a CFERROR TYPE="Exception" will catch a syntax error. Still another quirk exists in the aforementioned "CATCHEXCEPTIONSBYPATTERN" process, which doesn't quite work as expected in some situations. When throwing a type of "x.foo", the docs say we should be able to catch it with type x, but it doesn't work. We'll get to all that eventually. As always, forewarned is forearmed. We'll also eventually talk about how to get errors logged to the logs even when being handled by CFERROR or CFTRY (which will be even easier with the new CFLOG tag in CF 5), as well as how to detect and handle a long-running request (perhaps in a different way than other errors), and generally how you might detect and handle different "types" of errors in the CFERROR handlers, now that you know about the differences among such types. I know some have wondered how I even got four articles out of the subject of error handling. From feedback so far, you agree that there is indeed more to it than many had imagined. CFDJ LATEST STORIES . . .
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||