Cookies also have some limitations; they can hold only simple values (such as strings, numbers, and Booleans), they can be disabled easily by a user, their size is limited (4KB each), the number of cookies that can be set on a user's machine for a single domain is limited (20), and because they're in a text file on the client, they are not generally very secure.
To further limit the number of cookies you can set, ColdFusion automatically sets some cookies for its own use (CFID and CFTOKEN), reducing the number of available cookies you can set to 18 per host. Both CFID and CFTOKEN are used to track users throughout your site. When a user comes to your site, these two cookies are set and allow ColdFusion to identify that user's session. These two variables are needed for using Session or Client variables. Note that cookies are domain-specific; you cannot read cookies set by other websites, and they cannot access yours either.
There is another cookie, JSESSIONID, that ColdFusion uses in lieu of the CFID/CFTOKEN combination, if J2EE session variables are enabled in the ColdFusion Administrator. The JSESSIONID variable is set when a session is created and can be used to share Session variables between ColdFusion, JSP, and Java servlets. These topics are beyond the scope of this book, but there are some excellent resources at http://www.macromedia.com and http://livedocs.macromedia.com/. It is important to note that when J2EE sessions are enabled in the ColdFusion Administrator, the CFID and CFTOKEN variables are not set, so you are able to set 19 cookies per host instead of 18. One other difference is that J2EE sessions expire as soon as users closes their browser windows, which is not the case with traditional ColdFusion session management.
You can use ColdFusion to set a cookie on a user's machine in one of two ways: either by directly setting a variable in the Cookie scope by using <cfsetCookie.UserID = 14>, or by using the <cfcookie> tag. When using the <cfcookie> tag, you are able to set extra attributes, such as when the cookie expires, whether or not the cookie should be "secure," and the domain and path that the cookie is valid for. Here's an example of the <span style="font-weight:bold;"><cfcookie> tag:</span>
<cfcookie name="UserID" value="14">
The preceding snippet will create a cookie named UserID with a value of 14. Because you haven't defined when this cookie will expire, it will reside in the user's memory until the browser is closed. At this point the cookie will be deleted. This is often referred to as a session cookie because it lasts for only a single user session. If you define when the cookie expires, it will be written to a text file (cookies.txt if you are using Netscape, or individual files if you are using Internet Explorer) on the user's computer and will remain there for the specified time. CFML allows you to define when the cookie expires in the following ways:
An exact date: <cfcookie name="xyz" expires="12/3/2004">
A fixed number of days: <cfcookie name="xyz" expires="100">
NOW: <cfcookie name="xyz" expires="NOW">
NEVER: <cfcookie name="xyz" expires="NEVER">
If you specify an expiration date of NOW, the cookie is deleted from the user's cookies.txt file or individual file on the user's local computer as soon as the <cfcookie> tag executes. Specifying an expiration date of NEVER will set the cookie to expire in 30 years.
In the following simple example, you will see how you can set cookies that persist only as long as the user's browser remains open, and how to create cookies that last for a certain number of days.
1.Use Dreamweaver to create a file called getName.cfm within a new subfolder called Ch7 in your cfbook site, then enter the following code:
2.<cfform action="showName.cfm" method="post">
3. <cfinput type="Text" name="Name" value="" required="yes"
4. message="Please enter your name.">
5. <input type="Submit" value="Submit">
There is nothing interesting about the preceding code. You are simply creating a form to allow users to type in their names.
7.Create another file called showName.cfm within the same folder containing the following code:
8.<cfparam name="Form.Name" type="string">
9.<cfcookie name="Name" value="#Form.Name#">
10.<cfoutput>Your name is: #Cookie.Name#<br /></cfoutput>
11.<a href="anotherPage.cfm">Another page</a>
In this step, you are setting a cookie called Name to the value of the form field that the user filled out. You then display the value of the cookie along with a link to anotherPage.cfm.
12.Create a third file, anotherPage.cfm, and enter the following line into it:
Now test your files. Navigate to the getName.cfm file, enter your name in the text box, and click the Submit button. You name will be stored in a cookie, and you will see the cookie's value output to the window in showName.cfm.
If you then click on the hyperlink at the bottom of showName.cfm, you will go to anotherPage.cfm, which also displays the value of the cookie; you didn't have to explicitly pass the variable to anotherPage.cfm because it was able to retrieve it from the Name.cookie. You can even leave this page, navigate to another site, then return to anotherPage.cfm, and you will still see the name you entered in getName.cfm.
Because you didn't specify an expiration time for this cookie, it will remain in your memory until you close your browser. Therefore, if you close your web browser, open a new one, then navigate back to anotherPage.cfm, you will receive an "Element NAME is undefined in COOKIE" error; the cookie is no longer on your system.
This happens because the cookie expires as soon as the browser is closed—it was never even written to the disk. If you change the code in showName.cfm to the following snippet (showName2.cfm), the cookie will be saved on the user's hard drive and would still be defined after the browser was closed and reopened:
<cfparam name="Form.Name" type="string">
<cfcookie name="Name" value="#Form.Name#" expires="7">
<cfoutput>Your name is: #Cookie.Name#<br /></cfoutput>
<a href="anotherPage.cfm">Another page</a>
By adding the expires attribute to the <cfcookie> tag as shown, you are telling ColdFusion to save this cookie on the user's system for seven days, after which time it will expire. You could also set an actual date for the cookie to expire on.
Cookies can be useful if you want to track users around a site. You might want to create a snippet of code that creates a record of which users were viewing which pages and inserts into a database. You could then paste that snippet at the bottom of each web page and build your own simple stats package. You would be able to query the database and see how many times a certain page was browsed on a particular day, or track to see which pages were viewed by a particular user, and in which order.
There are a few important differences between Session variables and cookies. Cookies are stored on the user's computer, whereas Session variables are stored in the server's memory. Cookies are limited to simple values and lists, but Session variables can hold Recordsets, arrays, objects, or XML documents. Session variables use slightly more resources than cookies, but have fewer limitations.
One very important consideration when using Session variables is that you must be careful to "lock" the variable every time you read or change its value in versions of ColdFusion prior to MX. ColdFusion is able to tell which user the Session variables belong to by using the CFID and CFTOKEN (or JSESSIONID) cookies, so for sessions to work, a user's cookies must be enabled.
In ColdFusion MX and MX 6.1, you do not have to lock access to session variables for memory threading issues, but you do have to lock them anytime there is a possibility of a race condition. (A race condition is anytime one piece of code could change the value of a variable at the same time another piece of code tries to access it, which could result in invalid data being read.) Race conditions can be prevented if locks are used appropriately. This problem holds true with any of the variable scopes that are persisted in server memory: Session, Application, and Server. The main differences among these three scopes are as follows:
Session variables are tied to a specific user.
Application variables are available to all users of a certain application.
Server variables are available to all users in all applications on the server.
Because Session variables are stored within the server's memory, they have a much shorter lifespan than Client variables or Cookies. Session variables by default are stored in the server's memory for only 20 minutes. If a client is idle on your site (no page requests) for more than 20 minutes, the session variables will be deleted from the server's memory and will no longer exist.
As you saw in Chapter 1, Session variable timeouts (along with Application variables) can be set in the ColdFusion Administrator on the Memory Variables page in the Server Settings section. This is where you can disable or enable these two scopes entirely, or else set their default timeout and maximum timeout values. You can also define timeout values by using the <cfapplication> tag in your site, if you want to customize timeout values on a site-by-site basis.
It is also important to note that, if the timeout value within your <cfapplication> tag exceeds the maximum timeout value in the ColdFusion Administrator, the value from the ColdFusion Administrator will be used instead (the shortest of the two values is always the one used).
Using the <cflock> Tag
Let's look at a simple example showing how you use the <cflock> tag when reading or modifying the contents of a Session variable. This example locks access to the entire session scope by using the <cflock> tag's scope attribute. If few variables need to be locked, a better practice is to use the <cflock> tag's name attribute (all locks with the same name "obey" each other).
1.In Dreamweaver, open the showName.cfm file you made earlier in this chapter. Remove any existing code and add the following (see showName3.cfm in the code download):
2.<cfparam name="Form.Name" type="string">
3.<cflock timeout="15" type="exclusive" scope="session">
4. <cfset Session.Name = Form.Name >
6.<cflock timeout="10" type="readonly" scope="session">
You are using <cflock> tags to eliminate the chances of users seeing a different user's session variables. When you set a Session variable, you must set the lock type to "exclusive" so that no other threads are able to read or write to this variable while the current thread is accessing it.
9.Now view the getName.cfm template again, fill out the form, and submit it. You will either see the name displayed on the screen, as in the previous example, or you will get an error similar to the one shown in Figure 7-1. If you receive this error, you need to create an Application.cfm template in the same folder (or parent folder) and enable the session scope by adding the following code to it:
10.<cfapplication name="cfbook" sessionmanagement="yes">
1.Retest getName.cfm, and this time you should see your name being output to the browser. If you still receive an error message, check the ColdFusion Administrator and make sure that the Session scope hasn't been disabled.
Session variables, like Cookies, last a certain amount of time. You saw earlier in this chapter that it's possible to close the browser and navigate back to the anotherPage.cfm template and still view the value of the Cookie. The same holds true with Session variables, assuming they haven't expired. You can get it to persist for a prescribed length of time like so:
Open anotherPage.cfm again and change the code to the following (see anotherpage2.cfm in the code download):
<cflock scope="session" timeout="10" type="readonly">
Now close your browser, reopen it, and navigate back to the anotherPage.cfm template. You will still see your name displayed on the screen (unless the variables have timed out already). Even if the user closes the browser, the user's Session variables still remain in the server's memory until they time out.
But what if you wanted to have users' sessions end once they close their browsers? You need to set the Cookie.CFID and Cookie.CFTOKEN cookies to expire when a user's browser is closed. To achieve this, add the following code into your Application.cfm file below the current line (see Application2.cfm in the code download):
<cfif IsDefined("Cookie.CFID") AND IsDefined("Cookie.CFTOKEN")>
<cfcookie name="CFID" value="#Cookie.CFID#">
<cfcookie name="CFTOKEN" value="#Cookie.CFTOKEN#">
This code checks to see if variables named Cookie.CFID and Cookie.CFTOKEN already exist. We mentioned earlier in this chapter that these two variables are set automatically by ColdFusion and are used to track users throughout their visit to the site. If these two variables exist, you then create two cookies with the exact same names and values.
Although it may not be immediately obvious why you might bother doing this, if you examine the preceding code, you'll notice that you haven't defined an expiration date for these cookies. As we mentioned earlier in this chapter, if you do not define an expiration date, the cookies will not be saved to the users' disks and will expire when the users close their browsers.
So effectively, the users' CFID and CFTOKEN values will now expire when their values are closed. Note that this doesn't actually destroy the users' Session variables; it simply "disconnects" them from the current session. All of their session variables will still remain in server memory (though inaccessible unless the same CFID/CFTOKEN combination are passed) until they time out and are destroyed.
This technique can be very useful for logging out users from an application when their browsers close. If you weren't using the preceding code listing and a user exited the browser without hitting a logout button, the user would still be logged in if the Session variables weren't timed out. If that user (or a different user entirely) were to reopen the browser and navigate back to your site, the new visitor could still use the logged in functionality of the site.
Session variables aren't limited to simple strings. For an e-commerce site, you could create a user's shopping cart and store it in a Session variable instead of storing carts in a database. This has the benefit that, if a user abandons the cart and leaves your site, the Session variable will be deleted when the session times out. If you stored all the items in the user's cart within a database, you would need to run a cleanup routine to delete carts not modified in the past day or two.