Accessibility

ColdFusion Article

 

Tips for CFMX-ifying ColdFusion 5 Applications


Table of Contents

The ColdFusion 5 Application

The sample application is a simple press release index. Browse it at http://<your server>/devnet/cf5. When you first browse the application, you will see a list of active press releases. When a user selects a press release title, the application displays a detailed, longer version of the press release. To administer the application, go to http://<your server>/devnet/cf5/admin. Enter admin for the username and admin for the password. The application administrator displays a list of all press releases that you can use to add, delete, or edit a press release. It certainly isn't rocket science, but you get the idea. Now let’s take a look at the code in Listing 1, which contains the Application.cfm file.

Listing 1: Application.cfm

<cfapplication name="devnet_cf5" sessionManagement=true>

<cflock scope="application" type="readOnly" timeout=30>
	<cfif not isDefined("application.init") or isDefined("url.reinit")>
		<cfset variables.needInit = true>
	<cfelse>
		<cfset variables.needInit = false>
	</cfif>
</cflock>

<cfif variables.needInit>
	<cfinclude template="global_vars.cfm">	
</cfif>

The first line defines the application scope and enables session management. What I really want to focus on, however, is the rest of the file. The application has a set of variables, DSN, and so forth, that defines the particular settings for the application. These variables are stored in the application scope. You must lock application, session, and server variables in ColdFusion version 5 for two reasons.

  • To prevent race conditions, where one thread (or request) may access a variable at the same time as another thread.
  • To prevent memory corruption. When this occurs, your application may generate random mysterious errors when the server is handling enough load.

Because of these possible scenarios, I follow a simple rule when developing for ColdFusion 5: “If you type Application, Server, or Session, you darn well better have a lock around it.” The code in the Application.cfm file demonstrates this. Use a readOnly lock to check if application.init exists, which serves as a "flag" that indicates whether application settings have been loaded. If the application variable doesn’t exist (or if you have passed in a URL variable named reinit), then you set a variable that indicates whether you need to load settings. The code in Listing 2 verifies whether the variable is enabled and, if true, includes a file containing the application variables.

Listing 2: /cf5/global_vars.cfm
<cflock scope="application" type="exclusive" timeout=30>
	<cfset application.dsn = "devnet">
	<cfset application.init = true>
</cflock>

Listing 2 uses the cflock tag to set an exclusive lock, sets the variables, and the “flag” I mentioned earlier, application.init. As you can see, it's a lot of work to use one setting. A production application would have more settings of course.

The rest of the application consists of index.cfm (Listing 3) that lists all the press releases and links to a detail page.

Listing 3: /cf5/index.cfm
<cfmodule template="/devnet/tags/layout.cfm" title="Press Releases">

	<cfquery name="getPressReleases" datasource="#application.dsn#">
		select	id, title, teaser, publishDate
		from	tblPressReleases
		where	publishdate < now()
		and		active = 1
		order by	publishDate desc
	</cfquery>
	
	<p>
	Welcome to Acme, Incorporated. Supplying the weapons/trap needs of coyotes everywhere! 
	Here are our latest press releases.
	</p>
	
	<cfoutput query="getPressReleases">
		<a href="press_release.cfm?id=#id#">#title#</a> [#dateFormat(publishDate,"m/d/yy")#]<br>
		#teaser#<br><br>
	</cfoutput>
	
</cfmodule>

I'm not going to go too deep into this file as it is rather simple. This file only does a few things:

  • It queries the database to get all press releases.
  • The where clause in the query restricts the results to active press releases with a publish date before the current date.
  • Displays the results.

Listing 4 displays the detail page for a selected press release.

Listing 4: /cf5/press_release.cfm
<!--- Make sure URL.ID was passed and it is numeric --->
<cfif not isDefined("url.id") or not isNumeric(url.id) or val(url.id) lte 0>
	<cflocation url="index.cfm">
</cfif>

<cfquery name="getPressRelease" datasource="#application.dsn#">
	select	id, title, body, publishdate
	from	tblPressReleases
	where	publishdate < now()
	and		active = 1
	and		id = <cfqueryparam value="#url.id#" cfsqltype="CF_SQL_INTEGER">
</cfquery>

<!--- Was a valid ID passed? --->
<cfif not getPressRelease.recordCount>
	<cflocation url="index.cfm">
</cfif>

<cfmodule template="/devnet/tags/layout.cfm" title="#getPressRelease.title#">

	<cfoutput>
	<p>
	Posted: #dateFormat(getPressRelease.publishDate,"m/d/yy")#
	</p>
	
	#paragraphFormat(getPressRelease.body)#
	
	<p>
	<a href="index.cfm">Return to Press Releases</a>
	</p>
	</cfoutput>
	
</cfmodule>

The beginning of Listing 4 checks to see if the URL variable ID was passed, and that it was numeric and greater than zero. Next it queries the database again. What I want to point out here is that the where clause restricts the returned data much like the where clause in index.cfm. In this case, however, the where clause has an additional restriction: it filters the result to the ID the URL variable requested. The rest of the document verifies that the query returned a press release (it’s possible that a user modified URL.ID variable to an ID not in the database) and displays the result.

Earlier I mentioned the application administrator, where you can add, edit, and delete press releases. This by itself is no big deal, but let’s take a look at the security system. If you remember, the application asked you to enter a username and password. Obviously this is a good thing; you don’t want just anybody using this tool and modifying your website content. Take a look at how the application handles security in Listing 5:

Listing 5: /cf5/admin/Application.cfm
<!--- Load root Application.cfm --->
<cfinclude template="../Application.cfm">

<!--- Handle logon --->
<cfif isDefined("form.logon")>
	<cfif isDefined("form.username") and len(trim(form.username)) and
		  isDefined("form.password") and len(trim(form.password))>
		
		<cfif form.username is "admin" and form.password is "admin">
			<cflock scope="session" type="exclusive" timeout=30>
				<cfset session.authenticated = true>
			</cflock>
		</cfif>
		
	</cfif>
</cfif>

<!--- Handle security. --->
<cflock scope="session" type="readOnly" timeout=30>
	<cfif not isDefined("session.authenticated")>
		<cfinclude template="logon.cfm">
		<cfabort>
	</cfif>
</cflock>

The file begins by including the root application (shown in listing 1). The next portion checks to see if a user has attempted to log on. Let’s skip this for now. The last portion of the file is all you need to handle security. You simply check to see if a particular session variable is set, and if not, you include a logon form and use cfabort to halt the request.

Now go back up to the portion I skipped over. Since the logon form passes in form.username and form.password, you can simply check their values and ensure they are correct. In this example application, I’ve hard coded values of “admin” and “admin.” A normal application would not hard code values. If the values match up, you set the session value that the application checks for later. Notice that Listing 5 uses cflock in all cases in this application where it checks the session variable.

There is little to note in the rest of the administrator. The index file in the administrator is much like the index file of the public side, except that the administrator lists all press releases, whether they are active or not, and regardless of their publish date. The file that edits press releases, press_release_edit.cfm, ensures the administrator fills out the form correctly and either uses an insert or update query, depending on whether you are working with a new or old press release.