Site-Wide Error Handler Config Files

Ray Camden recently gave another friendly reminder to use a site-wide error handler for your ColdFusion site if you're not using <cferror> or onError in your Application.cfc. I've been doing a lot of fine-tuning on my intranet apps at work lately, and realized I never implemented any across-the-board error protection like this.

So, using TechNote 19198 as a starting point, I began writing my error handler.

Being that all my apps go into Subversion now, I am obsessive about putting specific settings of an app into config files, so that my light OCD doesn't flare up when those settings need to change (heaven forbid I bump a revision number just for a configuration change!).

This presents a light challenge for a couple reasons. First, the error handler is executed within the application scope of the application that threw the error. It does not automatically run the application.cfm or application.cfc in its own directory. Second, the working path is also the path of the template that threw the error.

These facts threw a wrench in standard operation for me. I store my config file settings in the application scope; that wasn't going to work for the error handler. And I'd like my config file to be in the same directory as my error handler, so I need to be able to FIND the config file.

Finding the config file is easy. Although ExpandPath() will operate from the working path of the erring template, GetCurrentTemplatePath() will still return the error handler's path. Use the GetDirectoryFromPath() function and append the filename of your config file.

<cfset ConfigPath=GetDirectoryFromPath(GetCurrentTemplatePath()) & "Config.xml">

Great, now we have a path to look at.

I use a custom tag (hey, it was written before CFMX) named <cf_AppConfig> that receives a config file path, retrieves the application settings from the file, and stores them in the Application scope. In my case, I needed to modify this custom tag to allow me to specify the scope the settings would be written in. Then, I simply write the settings in the Variables scope instead of the Application scope, to avoid accidentally overwriting any values of the existing application (or throwing an error if the Application scope isn't available!).

I can now cleanly retrieve settings from a config file for my error handler.

What kind of settings am I placing in this error handler's config file? Well, the error handler accomplishes three tasks: (a) Display a friendly error page, instead of the ugly ColdFusion default error handling. (b) Log the error with <cflog>. (c) Send details of the error via email.

That said, I put the email sender, recipient, and subject line in the config file. I also could put the name of the log file that <cflog> should write to.

I also have a config setting for the "Mode" of the server. In my case, I have a <cf_SiteConfig> custom tag that retrieves various site-wide settings from a global config file stored outside the web root. The "Mode" is one of those settings; it is set to "Production" for our live server and "Development" for my local PC and my QA server.

By looking at the "Mode" of your server, you can have your site-wide error handler "disable" itself on your development boxes. After all, I don't want to be getting email notifications all day long from my development server as I'm debugging my apps, and I don't want to hide the error information. To do that is simple. Manually throwing an error in your error handler will cause ColdFusion to abort the error handler and resort back to its default error management.

<cfif Mode is "Development"><cfthrow></cfif>

You could accomplish something similar by just looking to see if you're on localhost, or 127.0.0.1.

<cfif CGI.REMOTE_ADDR is "127.0.0.1"><cfthrow></cfif>

That way, if the browser is on the same machine as the server (i.e. while you are developing locally), the error handler would abort. The CGI.REMOTE_ADDR approach would work for you while developing and browsing on a local machine, but would not work for a QA server. For that, store the "Mode" in a config file.

Remotely Start a VMware Server

There have been a few times now that I needed to start a VMware virtual machine on my server but I wasn't on a PC that had the VMware console installed. Besides, unless I'm on my own network, starting up a server with the VMware console can be a pain due to slower upstream speeds.

Well, I finally took the time to find out how to do this from the command line. It's simple.

Get the current state of the virtual machine. Is it running or not?

$ vmware-cmd /path/to/vm/machine.vmx getstate

And start it.

$ vmware-cmd /path/to/vm/machine.vmx start

There are many other things you can do as well. Just man vmware-cmd.

How to Make ColdFusion Sleep

From time to time, you need to make your web app pause for a number of milliseconds or seconds. The need isn't often, but it does come up. There is no ColdFusion function or tag that accomplishes this currently, but you can do it with the following code snippet:

<cfscript>
    thread = CreateObject("java", "java.lang.Thread");
    thread.sleep(4000);
<cfscript>

This example will sleep for 4000 milliseconds, or 4 seconds.

This isn't news. There are many blogs out there that mention this extremely useful tidbit. Again, I don't mean to add to the echo chamber; I just use my blog as my initial reference point for little technical tidbits, so I had to throw it in there.

This is useful when ColdFusion needs to wait for a task to complete. For instance, I'm working with Active Directory today, and if I change an account in the directory with a DSMod command and then immediately make an LDAP call to load that account's updated information, the directory doesn't have time to react to the change and sends the old information in the LDAP query. If ColdFusion pauses about 2 seconds before querying LDAP, however, the updated information will come through, because the directory has time to catch up with the DSMod command. This is just one example of the usefulness of "putting ColdFusion to sleep".

Configuring Subversion for HTTP Access Behind Proxy

For the life of me, I couldn't figure out why I was unable to checkout from my Subversion repository while I was at work. It worked while I was at home (where my Subversion server is) just a few days ago, and I can view the repository using Subversion's built-in web page functionality, but if I tried to access the repository from the command-line client or TortoiseSVN, I would get an error message.

C:>svn co http://MyServer/Path/To/Proj/ MyProj
svn: REPORT request failed on '/Proj/!svn/vcc/default'
svn: REPORT of '/Proj/!svn/vcc/default': 400 Bad Request (http://MyServer)

Yeah, that's not cryptic. Fortunately, the solution is simple. Sander Striker explained in a thread REPORT request failed ... 400 Bad Request that if you're behind a proxy at work, and the proxy isn't configured to support the necessary Subversion calls REPORT, your request will fail.

Like he says in his message, you could request that the proxy be configured to allow the necessary requests, but you could just as well configure your server to work on a different port. Now, I like the fact that my Subversion calls can work on port 80, and I don't want to change that. So, I configured Apache to have my Subversion sites work on an additional port.

In the following example, let's use port 81 as the additional port. So my example URL http://MyServer/Path/To/Proj/ would become http://MyServer:81/Path/To/Proj/.

At the following spots in your httpd.conf file, make the one-time additions as marked in bold italics:

Listen 80
Listen 81

And...

NameVirtualHost *:80
NameVirtualHost *:81

And for every virtual host entry you have for a Subversion site (I host a couple different Subversion sites), add *:81 in the VirtualHost header:

< VirtualHost *:80 *:81 >

After restarting Apache, you will now be able to continue to use the URLs you normally use, but anytime you need to checkout from the repository while at work behind a proxy, you can use port 81 to do so successfully.

  Theme Brought to you by Directory Journal and Elegant Directory.