You are on page 1of 406

ColdFusion MX

D E V E L O P E R S

C O O K B O O K

ColdFusion MX

D E V E L O P E R S

C O O K B O O K

Pete Freitag Brad Leupen Chris Reeves

DEVELOPERS LIBRARY

Sams Publishing, 201 West 103rd Street, Indianapolis, Indiana 46290

ColdFusion MX Developers Cookbook


Copyright 2003 by Sams Publishing All rights reserved. No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical, photocopying, recording, or otherwise, without written permission from the publisher. No patent liability is assumed with respect to the use of the information contained herein. Although every precaution has been taken in the preparation of this book, the publisher and author assume no responsibility for errors or omissions. Nor is any liability assumed for damages resulting from the use of the information contained herein. International Standard Book Number: 0-672-32462-8 Library of Congress Catalog Card Number: 2002110482 Printed in the United States of America First Printing: April 2003 04 03 02 01 4 3 2 1

Acquisitions Editor Shelley Johnston Development Editor Damon Jordan Managing Editor Charlotte Clapp Project Editor Andy Beaster Copy Editor Kezia Endsley Indexer Erika Millen Proofreader Suzanne Thomas Technical Editors Peter Reese Chris Hiester Team Coordinator Vanessa Evans Media Developer Dan Scherf Designer Gary Adair

Trademarks
All terms mentioned in this book that are known to be trademarks or service marks have been appropriately capitalized. Sams Publishing cannot attest to the accuracy of this information. Use of a term in this book should not be regarded as affecting the validity of any trademark or service mark.

Warning and Disclaimer


Every effort has been made to make this book as complete and as accurate as possible, but no warranty or fitness is implied.The information provided is on an as is basis.The author and the publisher shall have neither liability nor responsibility to any person or entity with respect to any loss or damages arising from the information contained in this book or from the use of the CD or programs accompanying it.

To my family, friends, and teachers:Thanks for everything. Pete Freitag To my family and loved ones:Thank you so much for your on-going support. Brad Leupen To my family and friends:Thanks for your support. Chris Reeves

Contents at a Glance
Introduction 1 I Datatypes and Containers 1 String Manipulation 7 2 Working with Lists 23 3 Arrays 37 4 Structures 51 5 Queries 67 6 Dates and Times 79 II Controlling Application Flow 7 Application Flow 91 8 Exception Handling 101 III Using Utility Tags 9 Charting Data 113 10 Files and Directories 125 11 Using Internet Protocols 137 12 Using Verity 151 13 User Authentication 159

IV Database Integration 14 Structured Query Language (SQL) 181 15 Stored Procedures 203 V Language Extensibility Features 16 User Defined Functions 217 17 ColdFusion Custom Tags 229 18 CFX API Custom Tags 239 19 Components 259 20 Objects 271 21 WDDX 281 22 XML 295 23 Web Services 305 24 J2EE Interoperability 317 VI Appendixes A Online ColdFusion Resources 329 B Security 333 C Migrating from ColdFusion 5 to CFMX 339 D Converting Between Tags and Scripting 341 Index 349

viii

Contents

Table of Contents Table of Contents


Introduction 1
How This Book Is Organized 1 How to Use This Book 1 Chapter Overviews 2

I Datatypes and Containers 1 String Manipulation 7


1.0. 1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. 1.8. 1.9. 1.10. 1.11. 1.12. 1.13. 1.14. 1.15. 1.16. 1.17. Introduction 7 Concatenating Strings 7 Delimiting Strings 8 Using ASCII Character Codes 9 Forming Substring 10 Encrypting Strings 12 Hashing Strings 13 Iterating Through a String 14 Reading a File Line by Line 15 Searching a String 16 Replacing Substrings within a String 17 Using REFind() to Perform a Literal String Search 18 Using Qualifiers to Indicate Multiplicity 19 Using Wildcards 20 Using Brackets 20 Performing OR Operations 21 Escaping Special Characters 21 Using Character Classes 22

Contents

ix

2 Working with Lists 23


2.0. 2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. 2.8. 2.9. 2.10. 2.11. 2.12. Introduction 23 Creating Lists 23 Changing Delimiters 24 Extracting Elements 25 Handling Empty Values 26 Changing Values in a List 28 Searching a List 29 Sorting a List 30 Adding Qualifiers to a List 30 Looping Through a List 31 Looping Over a Structures Key List 32 Looping Until a List Is Empty 33 Parsing Input Forms 34

3 Arrays 37
3.0. 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. 3.9. 3.10. 3.11. Introduction 37 Creating an Array 37 Adding an Element to an Array 38 Displaying a Value in an Array 39 Looping over an Array 40 Manipulating Array Values 41 Sorting an Array 41 Multidimensional Arrays 44 Creating Arrays with More Than Three Dimensions 45 Array Aggregate Functions 46 Array Utility Functions 47 Array Information Functions 48

4 Structures 51
4.0. 4.1. 4.2. 4.3. Introduction 51 Creating a Structure 52 Using Structure Information Functions 52 Using Structure Management Functions 53

Contents

4.4. 4.5. 4.6. 4.7. 4.8. 4.9. 4.10. 4.11.

Inserting Values into a Structure 55 Updating Values in a Structure 56 Displaying the Value of an Element in a Structure Using the Key Name 57 Using Dot Notation 58 Looping Over a Structure 59 Using Structure Utility Functions 60 Copying Structures 61 Sorting Structures 63

5 Queries 67
5.0. 5.1. 5.2. 5.3. 5.4. 5.5. 5.6. 5.7. Introduction 67 Querying a Database 67 Programmatically Creating Queries 69 Displaying Query Results 70 Using Query Variables 71 Creating Queries of Queries 73 Converting a Query Column to a List 75 Caching a Query 76

6 Dates and Times 79


6.0. 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. 6.8. Introduction 79 Creating a Date,Time, or DateTime Object 79 Formatting Dates 80 Comparing Dates 82 Determining Date Differences 83 Formatting Times 84 Defining Time Spans 85 Using Date Information Functions 86 Managing Dates and Times in Different Locales 88

Contents

xi

II Controlling Application Flow 7 Application Flow


7.0. 7.1. 7.2. 7.3. 7.4. 7.5. 7.6. 7.7.

91

Introduction 91 Creating a ColdFusion Application 91 Using the Application Variable Scope 94 Implementing Session Management 95 Implementing Client Management 96 Storing Client State Using the Registry 97 Storing Client State Using Cookies 98 Storing Client State Using a Database 99

8 Exception Handling 101


8.0. 8.1. 8.2. 8.3. 8.4. 8.5. Introduction 101 Catching Exceptions 103 Catching Multiple Exceptions 104 Examining an Exception 105 Throwing Exceptions 107 Re-Throwing Exceptions 108

III Using Utility Tags 9 Charting Data 113


9.0. 9.1. 9.2. 9.3. 9.4. 9.5. 9.6. 9.7. Introduction 113 Creating a Simple Chart 113 Managing the Chart Container 114 Managing Chart Series 117 Populating a Chart Series with Explicit Values 121 Populating Charts with Query Values 121 Implementing Click-Through Behaviors in Charts 122 Administering Charts Through the ColdFusion Administrator 123

xii

Contents

10 Files and Directories 125


10.0. 10.1. 10.2. 10.3. 10.4. 10.5. 10.6. 10.7. Introduction 125 Reading Files 125 Writing Files 126 Appending to Files 127 Uploading Files 129 Manipulating Files 132 Listing Directories 134 Manipulating Directories 136

11 Using Internet Protocols 137


11.0. 11.1. 11.2. 11.3. 11.4. 11.5. 11.6. 11.7. 11.8. 11.9. 11.10. 11.11. Introduction 137 HTTP Requests 137 Sending Email 140 Receiving Email 140 Querying an LDAP Directory 141 Adding an LDAP Entry 143 Updating an LDAP Entry 144 Adding and Deleting an LDAP Entrys Attributes 145 Renaming an Entry 146 Deleting an LDAP Entry 147 Sending a File via FTP 147 Getting a File via FTP 148

12 Using Verity
12.0. 12.1. 12.2. 12.3. 12.4.

151

Introduction 151 Creating a Collection 151 Adding Information to a Verity Collection 152 Searching a Verity Collection with Simple Search 155 Searching a Verity Collection with Explicit Search 157

Contents

xiii

13 User Authentication and Authorization 159


13.0. 13.1. 13.2. 13.3. 13.4. 13.5. 13.6. 13.7. Introduction 159 Logging in a User 160 Logging out a User 160 Authenticating a User 161 Obtaining the Logged-in User ID 162 Authorizing a Block of Code 162 Authorizing a Function 164 Authentication Example 165

IV Database Integration 14 Structured Query Language (SQL) 181


14.0. 14.1 14.2. 14.3. 14.4. 14.5. 14.6 14.7. 14.8 14.9. 14.10. 14.11. 14.12. 14.13. 14.14. 14.15. 14.16. 14.17. 14.18. 14.19. Introduction 181 Inserting Data 183 Updating Data 183 Deleting Data 184 Selecting Data 185 Limiting Rows Returned 185 Specifying Multiple Conditions 186 Sorting Results 187 Using Aliases 187 Selecting from Multiple Tables 188 Joining Two Tables 189 Joining Three Tables 189 Grouping Data with CFOUTPUT 190 Performing Outer Joins 191 Selecting Unique Values 192 Using Aggregate Functions 192 Grouping Aggregate Data 193 Limiting Aggregate Results 194 Using the IN Keyword 194 Using IN with Web Forms 195

xiv

Contents

14.20. 14.21. 14.22. 14.23. 14.24. 14.25. 14.26.

Matching a Wildcard Pattern 196 Matching a Single Character 197 Matching a Group of Characters 198 Matching a Range of Characters 198 Excluding a Group of Characters 199 Excluding a Range of Characters 199 Using Prepared Statements 200

15 Stored Procedures 203


15.0. 15.1. 15.2. 15.3. 15.4. 15.5. 15.6. 15.7. 15.8. Introduction 203 Writing a Simple Stored Procedure in Microsoft SQL Server 204 Writing a Simple Stored Procedure in Oracle 205 Invoking a Simple Stored Procedure 206 Writing a Stored Procedure with Parameters in Microsoft SQL Server 207 Writing a Stored Procedure with Parameters in Oracle 208 Invoking a Stored Procedure with Parameters 209 Using Output Parameters 211 Working with Multiple Result Sets 213

V Language Extensibility Features 16 User-Defined Functions


16.0. 16.1. 16.2. 16.3. 16.4. 16.5. 16.6.

217

Introduction 217 Declaring a Function 218 Function Arguments 220 Returning Values 222 Suppressing Output 222 Recursive Functions 223 Working with Function References 225

Contents

xv

17 ColdFusion Custom Tags 229


17.0. 17.1. 17.2. 17.3. 17.4. 17.5. 17.6. 17.7. 17.8. 17.9. Introduction 229 Creating a Simple Custom Tag 229 Accessing a Custom Tag 230 Using Attributes 230 Returning a Result 231 Adding Start and End Tags 232 Building Nested Tags 234 Encoding Custom Tags 235 Using <cfmodule> 236 Using <cfimport> 237

18 CFX API Custom Tags 239


18.0. 18.1. 18.2. 18.3. 18.4. 18.5. 18.6. 18.7. 18.8. 18.9. 18.10. Introduction 239 Creating a Simple Java CFX Tag 240 Using Tag Attributes 241 Setting ColdFusion Variables 244 Using Queries in Java CFX Tags 245 A Simple C++ CFX Tag 247 Using Tag Attributes 250 Setting ColdFusion Variables 251 Using Queries in C++ CFX Tags 253 Installing a Java CFX Tag 256 Installing a C++ CFX Tag 257

19 Components 259
19.0. 19.1. 19.2. 19.3. 19.4. 19.5. 19.6. Introduction 259 Defining a Component 259 Instantiating a Component 262 Invoking a Component Method 263 Component Inheritance 265 Accessing a Component 267 Using Component Meta Data 268

xvi

Contents

20 Objects 271
20.0. 20.1. 20.2. 20.3. 20.4. 20.5. 20.6. Introduction 271 Creating a Java Object Instance 271 Invoking Methods on an Object 273 Using Properties on an Object 275 Working with Java Objects 275 Working with COM Objects 276 Working with CORBA Objects 277

21 WDDX
21.0. 21.1. 21.2. 21.3. 21.4. 21.5. 21.6. 21.7.

281

Introduction 281 CFML to WDDX 281 CFML to JavaScript 283 WDDX to CFML 285 WDDX to JavaScript 286 Syndicating Content with WDDX 287 Converting a Java Object into WDDX 290 Deserializing WDDX in Java 292

22 XML 295
22.0. 22.1. 22.2. 22.3. 22.4. 22.5. 22.6. 22.7. Introduction 295 Creating an XML Object with the <cfxml> Tag 295 Creating an XML Object with the XmlNew() Function 296 Creating an XML Object from Another XML Object 298 Parsing an XML Document 299 Accessing Attributes 299 Accessing Children 300 Accessing Inner Text 300 Technique 300 Comments 301 Searching XML with XPath 301 XSL Transformations 302

22.8. 22.9.

Contents

xvii

23 Web Services 305


23.0. 23.1. 23.2. 23.3. 23.4. 23.5. 23.6. 23.7. Introduction 305 Invoking a Web Service with CFINVOKE 306 Creating an Instance of a Web Service 307 Writing a Web Service 308 Using Properties 309 Deploying a Web Service 311 Invoking a Web Service with .NET 312 Invoking a Web Service with Java 315

24 J2EE Interoperability
24.0. 24.1. 24.2. 24.3. 24.4. 24.5.

317

Introduction 317 Redirecting to a Servlet or JSP 318 Including a Servlet or JSP 318 Accessing ColdFusion Pages from Servlets and JSPs 319 Sharing Data Between ColdFusion and Servlets/JSPs 320 Accessing Enterprise Java Beans 322

VI Appendixes A Online ColdFusion Resources


ColdFusion Web Sites 329 Java Web Sites 331 XML Web Sites 331 Other Useful Web Sites 332

329

B Security

333

Session Hijacking 333 URL Injection Hacking 334 File Uploading 335 Denial of Service Attacks 335 Cross Site Scripting 336

xviii

Contents

Sandbox Security 337 Security Resources 337

C Migrating from ColdFusion 5 to ColdFusion MX 339


ColdFusion Code Compatibility Analyzer 339 ColdFusion Settings Migration Wizard 339 Changes to CFML Tags and Functions 340 Database Operations 340 Localization 340 References 340

D Converting Between Tags and Scripting 341


The <cfscript> Tag 341 If-Then Statements 343 Loops 343 Switch Statements 345 Exception Handling 346

Index 347

About the Authors


Pete Freitag is the co-founder and Chief Technology Officer of CFDev, a company that builds Web components for ColdFusion and other languages. He has several years of experience developing database-driven Web applications, integrating with legacy systems, and product development. He contributes to the ColdFusion,Web development, and software development communities regularly, and holds a BS in Software Engineering from Clarkson University. Brad Leupen is the Chief Technology Officer for the software company Noverant. He holds an AB in Computer Science from Harvard University, where he focused on operating systems and distributed systems research. He won the Derek Bok Award for Excellence in Teaching as a Teaching Fellow for several university undergraduate classes. He has closely followed the development of key Web technologies and standards, including J2EE, .NET, and XML. Chris Reeves is a software developer for Religent, Inc. in Raleigh, NC, where he assists in development for the companys remote systems management software suite. He graduated with a BA in English from Appalachian State University.

Acknowledgments
I would first like to thank my co-authorsBrad and Chrisfor sharing in the joys and pains involved in putting this book together.They both exerted a great deal of energy and sacrificed lots of time for this project. I also owe thanks to Sams Publishing for providing me with several great editors to work with. In addition Id like to thank Bob Kern and TIPS Technical Publishing for their hard work. Id like to thank my co-workers at CFDev and ActivSoftware for giving me the flexibility I needed while working on this project. I must also thank my brother Steve for introducing me to the industry and providing me with knowledge and resources. Additionally, Dr. Douglas MacIntosh shared much of his knowledge and wisdom with me. Finally, Id like to thank everyone involved in the ColdFusion community for providing a synergistic environment for learning about ColdFusion. Pete Freitag I would like to thank the folks at Sams Publishing for their hard work in this project. I would also like to thank Chris and Pete. It was a joy to work with you. Finally, I would like to thank Bob Kern and the employees of TIPS Technical Publishing. None of this could have happened without you. Brad Leupen Thanks to my co-authors Brad and Pete for their outstanding work.Thanks also to everyone involved with this project at Sams Publishing as well as Bob Kern and the good folks at TIPS Technical Publishing. Chris Reeves

We Want to Hear from You!


As the reader of this book, you are our most important critic and commentator.We value your opinion and want to know what were doing right, what we could do better, what areas youd like to see us publish in, and any other words of wisdom youre willing to pass our way. You can email or write me directly to let me know what you did or didnt like about this bookas well as what we can do to make our books stronger. Please note that I cannot help you with technical problems related to the topic of this book, and that due to the high volume of mail I receive, I might not be able to reply to every message. When you write, please be sure to include this books title and author as well as your name and phone or email address. I will carefully review your comments and share them with the author and editors who worked on the book. Email: webdev@samspublishing.com Mail: Mark Taber Associate Publisher Sams Publishing 201 West 103rd Street Indianapolis, IN 46290 USA

Reader Services
For more information about this book or others from Sams Publishing, visit our Web site at www.samspublishing.com.Type the ISBN (excluding hyphens) or the title of the book in the Search box to find the book youre looking for.

Introduction

he ability to produce applications with relative ease, power, and speed spawned the popularity of ColdFusion, and is the reason many people use the technology.This book attempts to further strengthen these assets by opening your eyes to the power of ColdFusion, and at the same time accelerating the ease and speed at which you develop ColdFusion applications. This book is not meant to introduce you to ColdFusion, rather it is meant to enrich your development experience with solutions to everyday problems.We wrote this book for developers already familiar with the basics of ColdFusion and programming. Several books available today do a fine job of explaining what a conditional is, as well as how to set a variable.We felt no need to reinvent the wheel with this book; it seemed better to complement your current ColdFusion library with an arsenal of solutions to everyday problems.

How This Book Is Organized


This book is organized in such a way that it should be useful to experienced ColdFusion developers as well as beginners. Each chapter is broken into problem-oriented sections. The sections are then broken down further into two parts: Technique and Comments. The technique portion of each section asserts a one- or two-sentence solution to the problem presented in the section, followed by a short code sample.The technique section aims to quickly provide a solution to the problem with as few words as possible. This approach is especially friendly to experienced developers looking to find a quick solution to a particular problem. Novice developers may also find it helpful to see a solution to the problem upfront. The comments part of each section serves to describe the problem and its solution in detail. In the comments sections, you will find an explanation of the code presented in the technique, along with additional information pertaining to the tags or functions used to solve the problem.Within the comments, developers will find tips, tricks, and pointers to additional information.

How to Use This Book


This book is not intended to be read sequentially or cover to cover. Rather, when a problem arises, you should use the Table of Contents or Index to find the appropriate recipe and apply it.

Introduction

Although you wouldnt start using a traditional cookbook if you didnt first know how to use your oven, this cookbook also is not meant to be an introduction to core principles. Several books are currently on the market that can help you get started with ColdFusion. In addition to this book and other books, the ColdFusion documentation is also a good source of reference and explanations. If you chose to install it, the ColdFusion documentation can be found in the /cfdocs directory of your Web root. ColdFusion documentation can also be found at livedocs.macromedia.com, which augments the documentation with comments posted by developers.The comments are often helpful to clarify the use of a particular function, or to verify documentation bugs.

Chapter Overviews
Chapter 1, String Manipulation, includes several basic problems such as string concatenation and substring formation.The chapter also covers more complex problems such as using regular expressions. Chapter 2, Working with Lists, explains what lists are, how to create them, and how to work with them. Some of the problems solved include looping through a list, searching a list, and changing the list delimiter. Chapter 3, Arrays, handles the creation, manipulation, and iteration of the array data type in ColdFusion. Chapter 4, Structures, covers the structure data type.The topics covered include notation, sorting, populating, and copying structures. Chapter 5, Queries, shows you how to output the contents of a query object, how to create a query programmatically, and how to run queries of queries. Chapter 6, Dates and Times, covers date and time formatting, date math, and locales. Chapter 7, Application Flow, covers the <cfapplication> tag. It explains state management with sessions, and discusses client variables. Chapter 8, Exception Handling, shows how to catch, throw, and re-throw exceptions. Chapter 9, Charting Data, explains how to create charts with the <cfchart> tag. Chapter 10, Files and Directories, shows how to create, manipulate, and read files and directories. Chapter 11, Using Internet Protocols, covers the HTTP, LDAP, FTP, and email (SMTP, POP) protocols, and how to use them with ColdFusion. Chapter 12, Using Verity, shows you how to create and search a verity collection. Chapter 13, User Authentication and Authorization, takes you through the problems and solutions encountered when managing users in an application. Chapter 14, Structured Query Language (SQL), explains how to use many of the features in the SQL language to query databases. Chapter 15, Stored Procedures, shows you how to create stored procedures and invoke them in ColdFusion. Chapter 16, User-Defined Functions, details the procedures necessary to create your own functions in ColdFusion.

Introduction

Chapter 17, ColdFusion Custom Tags, explains how to create your own tags in ColdFusion. Chapter 18, CFX API Custom Tags, explains how to create your own tags in ColdFusion using Java and C++. Chapter 19, Components, shows you how to create, instantiate, invoke, and access ColdFusion components or CFCs. Chapter 20, Objects, explains how to use Java, COM, and CORBA objects in your ColdFusion applications. Chapter 21, WDDX, describes how to use WDDX to serialize data in XML and pass it between applications. Chapter 22, XML, shows you how to extract data from XML, and how to transform XML documents with XSL. Chapter 23, Web Services, explains how to invoke and create Web Services. Chapter 24, J2EE Interoperability, explains how to work with J2EE technologies such as servlets, JSP, and EJB. Appendix A, Online ColdFusion Resources, lists and describes resources for ColdFusion developers available online. Appendix B, Security, outlines some common security vulnerabilities, as well as possible solutions. Appendix C, Migrating from ColdFusion 5 to CFMX, describes procedures you can take to migrate your code to work optimally on CFMX. Appendix D, Converting Between Tags and Scripting, shows you how to write logic with <cfscript> blocks instead of with tags.

I
Datatypes and Containers
1 2 3 4 5 6 String Manipulation Working with Lists Arrays Structures Queries Dates and Times

1
String Manipulation
1.0. Introduction
No programming concept in the history of modern computing has appeared as overcomplicated as string manipulation. How many of you C programmers have secretly wondered why strings are so tough to use? malloc()? free()? strcat()? What? I just want to combine two words! you might say to yourself, although not loud enough for other programmers to overhear. Strings in C are represented as discreet arrays of characters, or bytes. If the length of your string exceeds that of the array, watch out! CGI programs, in particular, bring out the worst in Cs low-level string API. Debugging memory leaks in your string-handling code can be particularly frustrating when that is all your program does. Fortunately, the ColdFusion Markup Language (CFML) makes working with strings a lot easier. Strings are the default data type in CFML and the language offers a rich library of functions and operators specifically designed to play with strings. This chapter will explore many of these APIs as it describes how to solve many typical programming problems.The chapter will start slow but the examples will quickly move into some more advanced techniques, such as regular expressions.

1.1.

Concatenating Strings

You want to combine two strings.

Technique
Use the & operator to combine two strings into one.
<cfset s1 = Hello > <cfset s2 = World!> <cfset s = s1 & s2> <cfoutput>#s#</cfoutput>

Chapter 1

String Manipulation

Comments
The & operator combines the two strings, Hello and World!, to create a new string, Hello World!.The new string is then output to the resulting Web page by using the <cfoutput></cfoutput> tags. Notice that you dont have to worry about the length of the final string.The ColdFusion application environment handles allocating the memory for you.

1.2. Delimiting Strings


You want to represent many text fields as a single string.

Technique
You can create a simple comma-delimited list in ColdFusion by combining the different variables and delimiters.
<cfset x = a> <cfset y = b> <cfset z = #x#,#y#>

Comments
In this example, you have created a comma-delimited list by joining the x and y variables.The comma-delimited string is stored in the variable z. If you have ever written a program to import data into a database, you know the joys of working with delimited strings. As long as Microsoft Excel is around, CSV files may well remain the data interchange format of choice. In fact, delimited strings hold a place near to ColdFusions heart as well.The CFML language includes an entire list processing library geared toward splicing and dicing delimited strings.Youll learn about support for list processing library in great detail in Chapter 2, Working with Lists. However, lets take a moment to see just how to create a delimited string, or list. The key to a delimited list is (surprise!) the delimiter.The list uses a common delimiter (usually a comma or a tab character) to segregate a string into a series of substrings. Quite often, data files will include multiline delimited strings, where each line represents an individual data record and each delimited substring represents a unique field value.

1.3.

Using ASCII Character Codes

1.3. Using ASCII Character Codes


You want to specify ASCII characters by their numeric value.

Technique
Use the Chr() function to display a characters by their ASCII character code.
<cfset string = Copyright &Chr(169)& 2002> <cfoutput>#string#</cfoutput>

The Chr() function returns the character that corresponds to a given ASCII character code.The following example inserts the copyright symbol ([cr]) into a line of text by referencing its ASCII character code. The resulting Web page will faithfully render the string Copyright [cr] 2002 just as the code implies. This is particularly useful for embedding special characters (such as carriage returns and line feeds) into strings. ColdFusion also provides the Asc() function, which returns a characters numeric value. To display the alphabet, use the Asc() function to find the beginning of alphabet, and then loop to the end, displaying each character with the Chr() function.
<cfset start = Asc(A)> <cfset end = start + 25> <cfloop from=#start# to=#end# index=i> <cfset charUpper = Chr(i)> <cfset charLower = Chr(i+32)> <cfoutput>#charUpper# --> #charLower#<br></cfoutput> </cfloop>

Comments
Rather than type 52 letters into the source file, you can do this by taking advantage of the fact that alphabetic characters are represented by a contiguous numeric sequence in the ASCII character set. (The observant reader may note that this algorithm requires 227 characters.) The first step is to obtain the beginning ASCII number by using the Asc() function to find the ASCII equivalent of the uppercase letter A. Adding 25 to this value will then result in the number of the letter Z.You then iterate over this range by using the <cfloop> tag. For each number in the range, you find the corresponding character by using the Chr() function.You can discover the numeric code of the lowercase version of the letter by adding an offset of 32.The fruits of your labor look something like this:
A B . Z --> a --> b . . --> z

10

Chapter 1

String Manipulation

1.4. Forming Substring


You want to divide a string into smaller substrings.

Technique
Use the Right() and Left() functions to form substrings.
<cfparam name=str default=The quick brown fox jumped over the lazy dog.> <cfparam name=pivot default=1> <html> <body> <cfoutput> <cfif isdefined(submitted)> <cfset s1 = Left(str,pivot)> <cfif Len(str) gt pivot> <cfset s2 = Right(str,Len(str)-pivot)> <cfelse> <cfset s2 = > </cfif> <cfset str = s2&s1> </cfif> #str# <form action=substrings.cfm method=post> <select name=pivot> <cfloop from=1 to=#Len(str)# index=i> <cfif i eq pivot> <option value=#i# selected=yes>#i#</option> <cfelse> <option value=#i#>#i#</option> </cfif> </cfloop> </select> <input type=hidden name=str value=#str#> <input type=submit name=submitted value=Pivot String> </form> </cfoutput> </body> </html>

Comments
This example starts with the string The quick brown fox jumped over the lazy dog. It then asks the users to select a number between 1 and 45, which will be used as the pivot point in the string. Once the number is submitted, the ColdFusion

1.4.

Forming Substring

11

template creates two new strings.The first string is the result of performing a Left() function on the string to take the first n characters up to, and including, the pivot point. The second is the result of a Right() function, which takes all of the characters to the right of the pivot point. Finally, the page swaps the two substrings and displays the result to the users.The new, rotated string is used as the starting point the next time the users submit the page. ColdFusion MX includes a number of utility functions for dissecting, rearranging, and assembling strings.These functions are listed in Table 1.1.
Table 1.1 CFML Substring Functions
Find(substring, string [, start])

FindNoCase(substring, string ) [,start] FindOneOf(set, string [, start])

Left(string, count) Len(string) LTrim(string) Mid(string, start, count) RemoveChars(string, start, count) Replace(string, substring1, ) substring2 [, scope] ReplaceNoCase(string, substring1, substring2 [, scope]) Right(string, count) RTrim(string)

Finds the first occurrence of a substring within a string, beginning at an optional start position.The search is case-sensitive. Returns the position of the substring within the string, or 0 if the substring is not found. Same as Find() except that the search is not case-sensitive. Finds the first occurrence of any one of a list of characters within a string. Returns the position of the first character found in the string, or 0 if none of the characters appears in the string. Returns a substring of specified length, starting at the beginning of the string. Returns the length, in characters, of the specified string. Returns the specified string stripped of the leading spaces. Returns a substring of length count beginning at position start in the specified string. Returns a copy of the string with count characters removed, beginning at position start. Replaces one or more occurrences of substring1 with substring2 in string.The search is case-sensitive. Replaces one or more occurrences of substring1 with substring2 in string.The search is not case-sensitive. Returns count characters from the end of the string. Returns the string with all trailing spaces removed.

12

Chapter 1

String Manipulation

Table 1.1 Continued


StripCR(string) Trim(string)

Returns the string with all return characters removed. Returns the string with all leading and trailing spaces removed.

1.5. Encrypting Strings


You want to encrypt a string.

Technique
Use the Encrypt() and Decrypt() functions to encrypt or decrypt strings.
<cfset theString = ColdFusion> <!--- I also use 1234 for my luggage combination ---> <cfset key = key> <cfset encryptedString = Encrypt(theString,key)> <cfoutput>Encrypted String: #encryptedString#</cfoutput><br> <cfset decryptedString = Decrypt(encryptedString,key)> <cfoutput>Decrypted String: #decryptedString#</cfoutput>

Comments
You might find it necessary to encrypt and decrypt strings for storage or for transmission across a network.The CFML language includes two functions to aid in this effort: Encrypt() and Decrypt().The encryption algorithm used by the functions is based on a private key and the algorithms security is tied to the security of the key. Note that this approach differs in this way from the public-key encryption techniques used in the popular RSA encryption algorithm. The syntax for both of these functions is straightforward. Encrypt() takes two strings as arguments: the string to be encrypted and a key to be used in the encryption process.The function returns an encrypted string, which can be (and usually is) much longer than the original.The Decrypt() function also takes two strings as arguments: the encrypted string to be decrypted and the key that was used to encrypt the original string.The function reverses the encryption algorithm and returns the original string as a result. Note the importance of using the same key when calling the Encrypt() and Decrypt() functions. Be sure not to expose this key to outsiders, as malicious users can then thwart your security efforts! The result of executing this page looks like this:

1.6.

Hashing Strings

13

Encrypted String: *>H1W%R&X0-QUP Decrypted String: ColdFusion

Although the Encrypt() and Decrypt() functions are technically secure in and of themselves, limitations in private key security can make them undesirable for use in ecommerce applications.The sender and receiver must agree on a common key before a message can be encrypted.The sharing of this key is often insecure, thereby compromising the security of the entire transaction. However, ColdFusion string encryption is perfectly fine for protecting simple data elements. It is more than suitable for disguising, say, system variables in a pages URL.Use the Encrypt() and Decrypt() functions to encrypt or decrypt strings.

1.6. Hashing Strings


You want to hash, or forward-encrypt, a string for comparison purposes.

Technique
Use the Hash() function to encode a string.
<html> <body> <cfif isdefined(submitted)> <cfset orig_password = password> <cfset hashedPw = Hash(orig_password)> <cfset hashedAttempt = Hash(password)> <cfif hashedPw is hashedAttempt> You entered the correct password. <cfelse> Password attempt failed. Please try again. </cfif> </cfif> <form action=hash.cfm method=post> Enter your password: <input type=password name=password><br> <input type=hidden name=submitted value=true> <input type=submit> </form> </body> </html>

Comments
In addition to the Encrypt() and Decrypt() functions, ColdFusion MX provides a forward-encryption function called Hash(). Hash() uses the MD5 algorithm to encrypt a string.The Hash() function is useful for tasks such as storing a password

14

Chapter 1

String Manipulation

securely and comparing user-supplied passwords against the stored copy.The one-sided nature of the Hash() function means that the original password cannot be directly reproduced with a copy of the hashed password.The only way somebody can crack it is to test the Hash() function on every possible password string. The previous example illustrates the use of the Hash() function to forward-encrypt and compare a user-supplied password with the original.This program returns success only if the user enters the string password.

1.7. Iterating Through a String


You want to loop over each substring in a single string.

Technique
Use the <cfloop> tag and the Mid() function to iterate over a string.
<html> <body> <cfif isdefined(submitted)> <cfset newstring = > <cfloop from=1 to=#Len(text)# index=i> <cfset chr = Mid(text,i,1)> <cfset newstring = chr&newstring> </cfloop> <cfoutput>#newstring#</cfoutput><br> </cfif> <form action=reverse.cfm method=post> Enter text to be reversed:<br> <textarea name=text></textarea> <input type=hidden name=submitted value=true> <input type=submit> </form> </body> </html>

Comments
CFML allows you to inspect individual characters within a string. For example, lets say that your boss has asked you to write a Web page that reverses a user-defined string.The first question you need to ask yourself is: Why am I working here?The second question should be: How can I use CFML to do this? The reversal algorithm is simple: you loop from 1 to the strings length and use the Mid() function (which you learned about earlier) to extract the character at each position in the string.The character is prepended to a resulting string, which you show to the users (and your boss) when you are finished.

1.8.

Reading a File Line by Line

15

Lazy programmers would take the easy way out and use the CFML function Reverse() to do this.

1.8. Reading a File Line by Line


You want to parse an input file into separate lines of data.

Technique
Use <cffile> and <cfloop> to iterate over a file, line by line.
<cfset filename = GetDirectoryFromPath(GetCurrentTemplatePath())&data.txt> <cffile action=read file=#filename# variable=data> <cfloop list=#data# index=line delimiters=#chr(10)##chr(13)#> <cfoutput>#line#<p></cfoutput> </cfloop>

Comments
This example reads a small text file and parses it into multiple lines of data.The trick to this is to treat the entire file as a delimited text string and use the <cfloop> tag to iterate over a list of lines.You can use the carriage-return and line-feed characters as the delimiters in your string. The first two lines of the template read a file called data.txt (see Chapter 10, Files and Directories, for more information on file I/O). <cffile> expects the filename argument to be an absolute path name, so you might want to use the GetCurrentTemplatePath() function to handle relative paths. The last three lines form the heart of this little program.These lines call the <cfloop> tag, specifying the list attribute.This type of loop will iterate over a delimited string. In this case, the string contains the data file.This example uses the string #chr(10)##chr(13)# (line feed and carriage return) as the delimiter (UNIX users may just require #chr(10)#).This technique will cause the loop to iterate over each line in the file, assigning the contents of the line to the variable line in the program. It then prints the line and continues. One of the reasons that ColdFusion has been so popular for so long is its vast library of I/O tags and functions. ColdFusion is an excellent platform for moving data from one data repository to another. It is incredibly easy to access information in file systems, databases, FTP/HTTP sites, LDAP directories, mail servers, and so on.

16

Chapter 1

String Manipulation

1.9. Searching a String


You want to search a string for occurrences of a specific substring.

Technique
Use the Find() and FindNoCase() functions to search a string.
<cfparam name=searchstring default=> <html> <body> <cfif isdefined(submitted)> <!--- read the file ---> <cfset filename = GetDirectoryFromPath( GetCurrentTemplatePath())&TMch04.txt> <cffile action=read file=#filename# variable=data> <cfset cnt = 0> <cfset pos = 0> <cfset search = true> <cfloop condition = search is true> <cfif isdefined(casesensitive) and casesensitive is 1> <cfset pos = Find(searchstring,data,pos+1)> <cfelse> <cfset pos = FindNoCase(searchstring,data,pos+1)> </cfif> <cfif pos gt 0> <cfset cnt = cnt + 1> <cfelse> <cfset search = false> </cfif> </cfloop> <cfoutput>The string <b>#searchstring#</b> appeared #cnt# time(s).<p></cfoutput> </cfif> <form action=search.cfm method=post> <cfoutput>Enter a string to search for: <input type=text name=searchstring value=#searchstring#> <br></cfoutput> Case sensitive? <cfif isdefined(casesensitive) and casesensitive is 1> <input type=checkbox name=casesensitive value=1 checked=yes> <cfelse> <input type=checkbox name=casesensitive value=1> </cfif>

1.10.

Replacing Substrings within a String

17

<input type=hidden name=submitted value=true> <input type=submit> </form> </body> </html>

Comments
ColdFusion includes a nice string-searching utility that allows you to test for certain substrings within a larger string.These searches can be case-sensitive or case-insensitive, depending on your mood.You should familiarize yourself with two functions: Find() and FindNoCase().They will come in handy at some time or another, so you might as well learn them now. In the following example, you create a small application that enables you to search for strings in Chapter 4 of Alexandre Dumas The Three Musketeers, in which the hero, dArtagnan, meets the Royal MusketeersAthos, Porthos, and Aramisfor the first time. This example prompts the users to enter a search string. It will then load the chapters text into memory and count the number of times the search string appears. In order to count each occurrence of the string, you must make use of the optional start parameter for the Find() and FindNoCase() functions.The idea is to call the Find() function repeatedly, passing the result of the preceding function call (the starting position of the substring) as the new search starting position. In other words, make Find() resume the search where it stopped before. Be sure to avoid an infinite loop by adding 1 to the start position each time through!

1.10. Replacing Substrings within a String


You want to replace instances of a substring with another substring.

Technique
Use the Replace() and ReplaceNoCase() functions to replace a substring with another string.
<cfparam name=str default=Peter Piper picked a peck of pickled peppers. A peck of pickled peppers Peter Piper picked. If Peter Piper picked a peck of pickled peppers, wheres the peck of pickled peppers Peter Piper picked?> <cfparam name=substring1 default=> <cfparam name=substring2 default=> <html>

18

Chapter 1

String Manipulation

<body> <cfoutput> <cfif isdefined(submitted)> <cfset str = ReplaceNoCase(str,substring1,substring2,all)> </cfif> #str# <form action=replace.cfm method=post> Replace this string: <input type=text name=substring1 value=#substring1#><br /> With this string: <input type=text name=substring2 value=#substring2#><br /> <input type=hidden name=str value=#str#> <input type=submit name=submitted> </form> </cfoutput> </body> </html>

Comments
The Replace() and ReplaceNoCase() functions take as arguments a string to be searched, the substring to be replaced, the substring to do the replacing, and an optional scope parameter. The scope parameter dictates whether the function should replace all occurrences of substring1 with substring2 (all) or just the first occurrence (one). Be carefulthe default behavior of Replace() and ReplaceNoCase() is to replace just the first occurrence. Be sure to use the all scope option if you intend to perform a global search and replace. The previous example attempts to make a popular tongue-twister more manageable by replacing peppers with some other vegetable. Use the Replace() and ReplaceNoCase() functions liberally to child-proof your Web bulletin board!

1.11. Using REFind() to Perform a Literal String Search


You want to search for a substring within another string.

1.12.

Using Qualifiers to Indicate Multiplicity

19

Technique
Use the REFind() function to perform a literal string search.
<cfset pos = REFind(Sun,The Sun Also Rises)>

Comments
This will return the value 5, indicating the start position of the first occurrence of the substring Sun in the string The Sun Also Rises. ColdFusions regular expression functionality lies within the functions REFind, REReplace, REReplaceNoCase, and REFindNoCase. In the case of literal text searches, the functions operate just like their sans-RE counterparts.

1.12. Using Qualifiers to Indicate Multiplicity


You want to use the *, ?, and + symbols to specify a characters multiplicity.

Technique
Use the *, ?, and + quantifiers to indicate the multiplicity of the preceding character.
REReplace(Please pahhk the cahhh in the Hahvahd Yahhhd,ah+, ar, all)

Comments
There are three common symbols used as quantifiers here: *, ?, and +.They are appended to a character (or a group of characters) to indicate how many times that character should appear in the search string.The * quantifier represents zero or more.The ? quantifier represents zero or one. Finally, the + character represents one or more. Lets look at a few examples, the first of which I like to call the Bostonian Translator. The previous example returns the correct spelling of the phrase Park the car in the Harvard Yard. How does this work? Is it because Allaire used to be headquartered in Cambridge? Nope.This works because you tell it to.The example tells ColdFusion to look for an a followed by one or more hs and to replace them all with the string ar. What would happen if you used the * quantifier instead of +?
REReplace(Please pahhk the cahhh in the Hahvahd Yahhhd,ah*, ar, all)

This is no good. Instead of English, you get the phrase Plearse park the car in the Harvard Yard. Plearse? The problem is that the regular expression parser did what you told it to do. It looked for an a followed by zero or more hs. It found the single a in Please and replaced it.

20

Chapter 1

String Manipulation

1.13. Using Wildcards


You want to use the ., ^, and $ characters to represent other characters.

Technique
Use the ., ^, and $ characters to represent other characters.
REReplace(a man, a plan, a canal, panama!,^a, The, all)

Comments
The special characters ^ and $ represent the beginning and the end of the string, respectively. The example will return the string The man, a plan, a canal, panama!. Only the a at the very beginning of the string changed. The . (dot) character is used to represent any possible character, including whitespace and newlines. For example, the pattern .ort can match the words fort, port, tort, but not ort. However, the pattern .*ort will match fort, report, ort, retort, and so on.

1.14. Using Brackets


You want to use the bracket characters ([]) to represent a subset of characters.

Technique
Use brackets [] to represent a subset of characters.
<cfset pos = REFind( [A-Z]l,The Sun Also Rises)>

Comments
This expression looks for a space, followed by any uppercase letter, followed by the lowercase letter l.The bracketed expression [A-Z] serves as a wildcard to the regular expression parser to indicate: any uppercase letter in the range of A to Z.There is an implicit or among strings of letters in brackets.The pattern [dl]og will match dog and log but not dlog. Adding a ^ (caret) to the beginning of the bracketed clause will impose a negation on the characters contained in the brackets. For example, [^dl]og will match any sequence ending in og that is not preceded with a d or an l. The brackets also allow you to specify a range of characters using the (dash) character. Heres an example using the character:
[0-9]?[0-9]/[0-9]?[0-9]/[0-9][0-9]

1.16.

Escaping Special Characters

21

Can you figure out what it is matching? It matches dates. It can match 1/1/01 or 12/12/02 using the ? quantifier because the first digit of the month and day is optional.

1.15. Performing OR Operations


You want to use the pipe (|) character to perform a logical OR operation and to group characters.

Technique
Use the | (pipe) character and parentheses to perform a logical OR operation, as well as to group characters.

Comments
The | (pipe) special character is used as a logical OR. Each character in the pattern is intrinsically joined with a logical AND, unless explicitly specified with the | character. If you wanted a regular expression to match car or bar, you could use the pattern c|bar. You might have noticed with that last example that you could have also matched car or bar using brackets [cb]ar. It turns out that the | character has other uses; it can be used to match sequences of characters as well.This is done with the parentheses ( ) characters. Parentheses are used to group characters. Heres an example using parentheses and the | character:
(Mon)|(Tues)day

This example matches Monday or Tuesday.

1.16. Escaping Special Characters


You want to use the backslash (\) character to escape special characters.

Technique
Use the backslash (\) character to escape special characters.
<cfif NOT ReFind(.+@.+\..+, form.emailAddress)> You entered an invalid email address. <cfabort> </cfif>

Comments
The \ character is used for escaping any quantifiers or special characters. Lets look at a practical example, which validates an email address.To build this pattern, you deconstruct

22

Chapter 1

String Manipulation

the email address. Email addresses start with a username .+. Next comes the @ sign, .+@, and then a domain name that contains at least one dot (you need to escape it). Therefore, .+@.+\..+ is the correct pattern. Heres a code snippet that you can use: The \ character is also used for a very handy feature called the back reference. It allows you to use your groups (patterns in parentheses) again in your pattern or in your replacement.The back reference \1 represents the first group, \2 represents the second, and so on. Heres an example that eliminates repeated words:
REReplace(Echo Echo,(.+) +\1,\1,ALL)

1.17. Using Character Classes


You want to use character classes to represent a predefined subset of characters.

Technique
Use character classes to represent a predefined subset of characters.

Comments
The last group of special characters are known as character classes. Character classes are keywords that represent a predefined class of characters. One example is [[:alnum:]], which is the same as [a-zA-Z0-9].They are listed in Table 1.2.
Table 1.2 Regular Expression Character Classes Character Class
:alpha: :upper: :lower: :digit: :alnum: :xdigit: :blank: :space: :print: :punct: :graph: :cntrl:

Matches Any letter Any uppercase letter Any lowercase letter Any digit (09) Any alphanumeric character Any hexadecimal digit A space or tab. A space, tab, newline, carriage return, or line feed Any printable character. Any punctuation character All printable characters except those in the space character class All characters not in the upper, lower, alpha, digit, punct, graph, print, or xdigit classes

2
Working with Lists
2.0. Introduction
The last chapter discussed how ColdFusion gives special treatment to delimited strings. These strings are even given a special nickname: lists.The following string is an example of a ColdFusion list:
apple,orange,grape,banana,peach

Lists, such as this list of fruit, are wonderful data structures in ColdFusion.They are plain text, so they are easy to assemble.They are also common. Data import files often contain lists. Better yet, HTML check boxes submit their results as comma-delimited lists. Finally, ColdFusion provides a complete function library for manipulating lists. Once familiar with them, you will find yourself using ColdFusion lists throughout your CFML templates.They are simple yet powerful and are another reason why the CFML language is so popular among non-programmers. This chapter looks at some common programming tasks involving lists. In doing so, it explains key list concepts and includes some practical examples.

2.1. Creating Lists


You want to create a list.

Technique
Use delimited strings to create lists. Lists are very easy to create.You can create a list of fruit in one line of code:
<cfset fruitList = apple,orange,grape,banana,peach>

24

Chapter 2

Working with Lists

Comments
You can create the same list iteratively by tacking on values one at a time.
<cfset <cfset <cfset <cfset <cfset fruitList fruitList fruitList fruitList fruitList = = = = = apple> fruitList&,orange> fruitList&,grape> fruitList&,banana> fruitList&,peach>

You can also use the ListPrepend() and ListAppend() functions to add elements to the beginning and end of a list, respectively. Both functions take as arguments the list, the value to be added, and an optional delimiter (again, the comma is used as the default). Both functions also return a copy of the list with the new item.Thus, you can substitute this line:
<cfset fruitList = fruitList&,orange>

with:
<cfset fruitList = ListAppend(fruitList,orange)>

Note
The ColdFusion documentation actually discourages the use of ListAppend() and ListPrepend() in favor of manually adding items to a list. Unfortunately, it does not give the reason why.

Like Peter Parkers spider or Pamela Andersons surgeon, the delimiter is the source of a lists power.You can choose any character you like as a delimiter, although the comma is the default in ColdFusion. Be sure to choose one that you like because it is important that you maintain a consistent delimiter in your list.

2.2. Changing Delimiters


You want to change the delimiter that a list uses.

Technique
Use the ListChangeDelims() function to change a lists delimiter.
<cfset list1 = elem1,elem2,elem3,elem4,elem5> <cfset list2 = ListChangeDelims(list1,|)> <cfoutput> List 1: #list1#<br> List 2: #list2#<br> </cfoutput>

2.3.

Extracting Elements

25

Comments
It was love at first sight.Your delimiter seemed to be perfect for your list when you first started going out. Alas, you caught your delimiter cheating with certain list elements and it is now time to move on.What is to be done? Fortunately, ColdFusion makes it easy to extract yourself from this potentially awkward situation.The ListChangeDelims() function will clean up your mess. Simply pass it the list you want to change, the new delimiter, and (optionally) the old delimiter and you are back in business.Who said breaking up is hard to do? This function is used to change the delimiter from a comma (,) to a pipe (|).This can be useful when you anticipate using the delimiter as a part of a data element in the list.

2.3. Extracting Elements


You want to extract specific elements within a list.

Technique
Use the ListGetAt() and ListToArray() functions to extract elements in a list.
<cfset fruitlist <cfset thirdfruit <cfset fruitarray <cfset fourthfruit = apple,orange,grape,banana,peach> = ListGetAt(fruitlist,3)> = ListToArray(fruitlist)> = fruitarray[4]>

<cfoutput> fruit 3: #thirdfruit#<br> fruit 4: #fourthfruit#<br> </cfoutput>

Comments
There are two primary ways to extract elements from a ColdFusion list.The most popular way is to use the ListGetAt() function. ListGetAt() takes a list, a position, and an optional delimiter as its arguments. It returns the value that is found at the specified position in the list. You use this technique previously to extract the third fruit element in the list:
<cfset thirdfruit = ListGetAt(fruitlist,3)>

Note
ColdFusion lists use cardinal (or one-based) numbering. Therefore, you should use the index 1, not 0, to reference the first element in a list.

26

Chapter 2

Working with Lists

The second way to inspect the contents of a list is to convert the list into an array and use array indexing to retrieve the values.The ListToArray() function performs the conversion for you. Pass the function a list and an optional delimiter and it will return an indexed array.You can use this approach in the current example to get the fourth fruit:
<cfset fruitarray = ListToArray(fruitlist)> <cfset fourthfruit = fruitarray[4]>

The use of arrays is covered in much more detail in the next chapter. However, you can see that the expression fruitarray[4] is much easier on the wrists than ListGetAt(fruitlist,4). Converting your list into an array is a nice technique when you will be extracting many elements from a single list.There is a potential performance boost to this as well, because indexed lookups take less time than do linear searches in a list. However, make sure that you are done modifying the list before you convert it into an array. ColdFusion makes a copy of the list when it converts it. If you make changes to the list following the conversion, you must re-convert the list or else make the same changes to the array. ColdFusion also supplies the following three functions to access the contents of a listListFirst(), ListRest(), and ListLast(). ListFirst() returns the first element of a list. ListLast() returns the last element of a list. ListRest() returns a list without its first element. LISP programmers might recognize ListFirst() and ListRest() as the car and cdr functions of the recursive LISP programming language.
Note
Use the Trim() function to remove extra spaces surrounding delimiters in your list.

2.4. Handling Empty Values


You want to handle empty values in your list.

Technique
Use a custom function to fix null values in a list.
<cfscript> function ListFixNulls(list,delim) { ar = ArrayNew(1); theval = ; thechar = ; pos = 1; for (i=1; i lte Len(list); i = i + 1) { thechar = mid(list,i,1);

2.4.

Handling Empty Values

27

if (thechar is delim) { ar[pos] = theval; pos = pos + 1; theval = ; } else { if (thechar is not chr(0)) { theval = theval & thechar; } } } if (not(arrayisempty(ar)) or theval is not ) { ar[pos] = theval; } return ar; } </cfscript> <cfinclude template = ListFixNulls.cfm> <cfset emplist = 10002,Jane,,Smith> <cfoutput> With ListFixNulls()...<br> <cfset emparray = ListFixNulls(emplist,,)> <cfset <cfset <cfset <cfset id = emparray[1]> firstname = emparray[2]> middle = emparray[3]> lastname = emparray[4]>

ID: #id#<br/> First Name: #firstname#<br/> Middle Initial: #middle#<br/> Last Name: #lastname#<br/> Without ListFixNulls()...<br> <cfset id = ListGetAt(emplist,1)> <cfset firstname = ListGetAt(emplist,2)> <cfset middle = ListGetAt(emplist,3)> <cfset lastname = ListGetAt(emplist,4)> ID: #id#<br/> First Name: #firstname#<br/> Middle Initial: #middle#<br/> Last Name: #lastname#<br/> </cfoutput>

28

Chapter 2

Working with Lists

Comments
Most historians attribute the ancient Indian mathematician Brahmagupta (598670 AD) with the introduction of the number zero to modern numbering systems. He forever changed the way mathematicians think by recognizing the importance of acknowledging the null value in any mathematical system. Unfortunately, the works of Brahmagupta were not consulted during the design phase of the ColdFusion lists implementation. CFML list-processing functions completely disregard the null value, a fact which has long irked this ColdFusion developer. For example, consider the following table of employee names and IDs:
Employee ID,First Name,Middle Initial,Last Name 10001,John,H,Smith 10002,Jane,,Smith 10003,George,O,Smith

Although John and George both have middle initials, Jane does not. ColdFusion will throw an Invalid List Index exception when you try to access the fourth element of Jane Smiths list.The ListGetAt() function essentially removes all null values from the list (for example, 1,2,3 is equivalent to 1,,,,2,,,,,,,,3).Thus, the template returns the string Smith as the third element of the list instead of returning an empty string, as one might expect. This phenomenon becomes a nightmare when dealing with associated lists that may contain null values (like most data imports). Fortunately, there is a work-around.The previous technique shows a custom function (see Chapter 16, User-Defined Functions, for more about user-defined functions) that converts a list into array while preserving the lists null values.This function takes as arguments a list and a delimiter character and returns the new array. The function must not rely on the ColdFusion List API because those functions will ignore null values. Instead, it must examine each character in the list and build the array values manually. Note that the function will not work with multi-character delimiters. I leave this up to you to implement.

2.5. Changing Values in a List


You want to change the values of specific elements within a list.

Technique
Use the ListInsertAt(), ListSetAt(), and ListDeleteAt() functions to modify the contents of a list.
<cfset <cfset <cfset <cfset list list1 list2 list3 = = = = elt1,elt2> ListInsertAt(list,1,elt0)> ListSetAt(list,1,elt0)> ListDeleteAt(list,1)>

2.6.

Searching a List

29

<cfoutput> Originial List: #list#<br> With Element Inserted: #list1#<br> With Element Set: #list2#<br> With ElementDeleted: #list3#<br> </cfoutput>

Comments
Updating lists in ColdFusion is even easier than creating them. Use the
ListInsertAt() function to insert a new value into your list.The function takes the

list you are updating, the position you want to change, the value you want to insert, and an optional delimiter as arguments.The function confirms that you have supplied a valid list index and inserts the new value at the specified position, moving the original element and all subsequent elements over one spot in the list. The ListSetAt() function uses the same syntax as ListInsertAt() but its behavior is slightly different. ListSetAt()replaces the old element at the specified position with the new element. The ListDeleteAt() function is the third list-modification function.The arguments for the ListDeleteAt() function are the list to be modified, the position that should be removed from the list, and an optional delimiter. ListDeleteAt() will return a copy of the list sans the element at the specified position. The previous technique uses these functions to modify a single list in three ways.

2.6. Searching a List


You want to search a list for specific values.

Technique
Use the ListFind() and ListFindNoCase() functions to search the elements of a list.
<cfset fruitList = apple,orange,grape,banana,peach> <cfset pos = ListFind(fruitList,grape)> <cfoutput> The element was found at position #pos# in the list. </cfoutput>

Comments
How can you determine whether a given list contains a specific element? Once again, CFML comes to the rescue by giving you the ListFind() and ListFindNoCase() functions.The arguments to both functions are the same: a list to search, a value to search for, and an optional delimiter to use when searching.These functions return the index of the first element that matches the specified value. ListFind()performs a case-sensitive search, whereas ListFindNoCase() does not.

30

Chapter 2

Working with Lists

The template in the previous example searches a list for the element grape.The ListFind() function returns the number 3 to indicate that the element in question resides in the lists third position.

2.7. Sorting a List


You want to sort the elements in a list.

Technique
Use the ListSort() function to sort a list.
<cfset fruitList = apple,orange,grape,banana,peach> <cfset sortedList = ListSort(fruitList,text,asc)> <cfoutput>#sortedlist#</cfoutput>

Comments
CFML also gives you the capability to sort your lists.The ListSort() function allows you to sort a list of elements in different ways.The arguments to ListSort() are the list to sort, the sort type (that is, numeric, text, and textnocase), the sort order (such as asc[ending] or desc[ending]), and an optional delimiter. ListSort() returns a copy of the sorted list. The previous technique shows a template that performs an ascending text sort on a list.The result of the function is this list: apple,banana,grape,orange,peach.
Note
Macromedia introduced a subtle change to the ListSort() function in the new MX server. When using the textnocase and desc options, MX returns elements that differ only in case in the opposite order as when the textnocase and asc options are used. Older versions of ColdFusion did not change the ordering of elements that differed only in case.

2.8. Adding Qualifiers to a List


You want to add qualifiers to the elements within a list.

Technique
Use the ListQualify() function to qualify the elements of a list.
<cfset fruitList = apple,orange,grape,banana,peach> <cfset qualifiedList = ListQualify(fruitList,,,,all)> <cfoutput>#qualifiedList#</cfoutput>

2.9.

Looping Through a List

31

Comments
Delimiters are a necessary evil in any list. A poorly chosen delimiter can render a list useless. Imagine using the letter a as a delimiter in a list of names.That probably would not work so well.The perfect delimiter is one that will never appear in the lists data. Choosing this delimiter usually requires a prior knowledge of the characters that will appear in the list. What should you do, then, if the element data in your list requires that the delimiter appear in the data fields as well? One technique you can use is to qualify the elements in your list. Qualifying a list means using additional characters, other than the delimiter, to isolate the different elements. Single and double quotes are common list qualifiers. Consider the following list of addresses:
8317 Morgans Way,1128 Blenheim Dr,804 W. Morgan St, Apt X2C

The string Apt X2C is clearly a continuation of the 804 W. Morgan St address and not an address on its own.You can eliminate this ambiguity by qualifying the elements in the list:
8317 Morgans Way,1128 Blenheim Dr,804 W. Morgan St, Apt X2C

ColdFusion includes a function, ListQualify(), that will surround a lists elements with a qualifier of your choosing.The function accepts a list, a character or string qualifier, an optional delimiter, and an optional elements argument.The all elements option will put qualifiers around all elements in the list; the char option will put qualifiers around alphabetic strings (numeric fields usually do not require qualifiers). The example shown in this technique adds qualifiers to a list to produce the following output:
apple,orange,grape,banana,peach

ListQualify() is a nice function to use when you are preparing a large list of data for

import into a database. Many import routines actually expect data to be in a qualified (quote-comma-delimited) format.

2.9. Looping Through a List


You want to iterate over each element in a list.

Technique
Use the <cfloop> tag with the list attribute to iterate over the elements in a list.
<cfset fruitList = apple,orange,grape,banana,peach> <cfloop index=fruit list=#fruitList# delimiters=,> <cfoutput>#fruit#</cfoutput><br/> </cfloop>

32

Chapter 2

Working with Lists

Comments
My favorite list-related feature in ColdFusion is the built-in support for looping over a list of elements.The simplist way to do this is to use the <cfloop> tag and to specify its list attribute. The list attribute is, perhaps, the <cfloop> tags finest asset. In this mode, <cfloop> expects the following attributes: index (requiredthe variable name to which each list element will be assigned), list (requiredthe list that is to be looped), and delimiters (optionalone or more delimiters to use in iterating over the list).The tag will execute its body n-times, where n equals the number of elements in the list. Upon each iteration, the loop will assign the next element in the list to the variable named by the index parameter. The <cfloop> tag will iterate over each fruit in the list, assigning a new value to the variable fruit each time.The page output generated by the templates looks like this:
Apple orange grape banana peach

2.10. Looping Over a Structures Key List


You want to iterate over the keys contained in a structure.

Technique
Use the <cfloop> tag to iterate over a structures keylist.
<cfset <cfset <cfset <cfset <cfset <cfset <cfset p = StructNew()> p.id = 10001> p.firstName = Ryan> p.middleName = Allen> p.lastName = Hackney> p.city = Red Stick> p.state = LA>

<cfloop index=key list=#StructKeyList(p)#> <cfset value = StructFind(p,key)> <cfoutput>#key# = #value#</cfoutput><br/> </cfloop>

Comments
The <cfloop> tag can be used to examine the contents of a structure. Structures, which are covered in better detail in Chapter 4, Structures, are hash tables that contain an

2.11.

Looping Until a List Is Empty

33

unlimited number of key/value pairs.You can use the StructKeyList() function to acquire a list of entry keys in a structure.You can then use the <cfloop> tag to iterate over these keys and evaluate the entry in the structure.The code shown in this technique creates a simple structure representing an employee and examines its contents by using the <cfloop> tag.The template produces the following output:
STATE = LA LASTNAME = Hackney ID = 10001 MIDDLENAME = Allen CITY = Red Stick FIRSTNAME = Ryan

2.11. Looping Until a List Is Empty


You want to perform a conditional loop on a list that might change.

Technique
Use the <cfloop> tag and its condition attribute to loop over the contents of a dynamic list.
<cfset fruitList = apple,orange,grape,banana,peach> <cfloop condition=fruitList is not > <cfset fruit = ListFirst(fruitList)> <cfset fruitList = ListRest(fruitList)> <cfoutput>#fruit#</cfoutput><br/> </cfloop>

Comments
Another way to loop over a list is to use the <cfloop> tag with its condition attribute in concert with the ListFirst() and ListRest() functions.This is useful when you will be modifying the contents of the list inside the body of the loop.The algorithm is to pop off the first element of the list continuously until the list is empty.The example in this section implements this strategy. In the example, fruitList is constantly reduced in size by assigning it the result of the ListRest() function.You therefore have greater control over the contents of the list and the behavior of the loop.The loop will continue until the list is empty.You can control how and when that condition arises.This level of control is often unnecessary, making the <cfloop list=...> approach the looping construct of choice nine times out of ten. However, keep this technique in your bag of tricks in case you need to change the contents of your list in midstream.

34

Chapter 2

Working with Lists

2.12. Parsing Input Forms


You want to use lists to help parse the values in an input form.

Technique
Use the <cfloop> tag to iterate over a collection of values contained in a single input form field.
<html> <body> <cfif isdefined(submitted)> <cfif isDefined(fruitlist) and ListLen(fruitList) gt 0> These are the fruits you like:<br/> <cfloop index=fruit list=#fruitList#> <cfoutput>#fruit#</cfoutput><br/> </cfloop> <cfelse> You hate fruit! </cfif> <cfelse> <form action=Checkboxes.cfm method=post> <cfoutput> Which fruits do you like?<br> <cfset fruitOptions = Apple,Banana,Grape,Orange,Peach> <cfloop index=option list=#fruitOptions#> <input type=checkbox name=fruitList value=#option#/>#option#<br/> </cfloop> <input type=submit name=submitted value=Submit> </cfoutput> </form> </cfif> </body> </html>

Comments
ColdFusion creates a list out of a form variable that has been defined multiple times on an input form. Consider, for example, the following URL submitted by a Web browser:
http://localhost/colors_form.cfm?color=red&color=blue&color=green

Notice that the variable color has three values. Obviously, ColdFusion cannot create three unique variables with the same name, so it compensates by creating a single variable that is a list of three values. In other words, the value of the variable color is the string red,blue,green.

2.12.

Parsing Input Forms

35

The example shown in this technique demonstrates this concept by creating a small form that contains five check boxes with the same name.The template uses the looping technique to examine the contents of the submitted form. Notice that this example loops over two lists. It loops over the types of fruit that the user submitted in lines 68. However, the example also loops over a list of options (lines 1619) to populate the input form.This is a nice way to save time typing while making the form more extensible.You can easily change the order and values of the options on the form by modifying the fruitOptions list.

3
Arrays
3.0. Introduction
An array is a data type containing an ordered collection of variables. Items in an array are referred to as array elements and are referenced by their index (or numerical position) in the array. You use arrays when you have a collection of variables and need to store them in a defined order. Like lists (discussed in Chapter 2, Working with Lists), arrays support variable orderyou can explicitly reference the value stored in a given position of the array. Although lists only store simple variables like strings and numbers, arrays can store complex data types, such as structures, queries, and even other arrays. If you need to store complex data types in a collection but do not need them in a defined order, you may want to look into using the ColdFusion data type structure, discussed in Chapter 4, Structures. This chapter shows you how to iterate over array element values, manipulate element values, and sort an array. It also explains how and when to use multidimensional arrays and how and when to use array aggregate functions.

3.1. Creating an Array


You want to create a new array.

Technique
Use the ArrayNew() function to create a new ColdFusion Array object.
<cfset aPetSounds = ArrayNew(1)>

38

Chapter 3

Arrays

Comments
ColdFusion arrays are different than arrays in many other programming languages in that ColdFusion arrays are sized dynamically. A dynamically sized array is one that allows for expansion as elements are addedthe programmer does not need know the size of the array when it is declared. Instead, ColdFusion sizes the array as the number of elements exceeds the size that ColdFusion initially appropriated. This dynamic sizing provides flexibility in programming but can also cause a performance hitespecially for large arrays, because ColdFusion essentially has to guess how large the array will eventually be. So if you know the maximum number of elements your array will contain, ColdFusion provides the ArrayResize() function to force a minimum size. ArrayResize() accepts two parameters, the array to resize and the minimum size to set that array. ColdFusion documentation suggests using the ArrayResize() function immediately following an array declaration that will contain more than 500 elements. Multidimensional arrays are covered later in this chapteruntil then youll use only single dimensional arrays, created by passing a value of 1 to the ArrayNew() function.
Note
As a best practice, all arrays in this chapter begin with the letter a, as in aArrayName.

3.2. Adding an Element to an Array


You want to add an element to an array.

Technique
You add elements to an array by statically referencing the index of the element you want to insert, by using the ArrayAppend() function, or by using the ArrayPrepend() function.
<!---add element by index---> <cfset aPetSounds[1] = You Still Believe In Me> <cfset aPetSounds[2] = Thats Not Me> <!---add element using ArrayAppend---> <cfset ArrayAppend(aPetSounds, Dont Talk (Put Your Head On My Shoulder))> <!---add element using ArrayPrepend---> <cfset ArrayPrepend(aPetSounds, Wouldnt It Be Nice)>

3.3.

Displaying a Value in an Array

39

Comments
After youve created an array, the natural next step is to populate its elements. (To create an array, see section 3.1, Creating an Array.) One way to set the index value of a given element in an array is to reference the element you want to populate by index and set its value explicitly.This code is an example of creating a new array named aPetSounds and setting the first and second element to the strings You Still Believe In Me and Thats Not Me, respectively, by reference. (Pet Sounds, of course, is an album by the Beach Boysoften considered their masterpiece.) Notice that ColdFusion uses 1-based numbering arraysthe first array element is in position 1. Many programming languages use 0-based numbering arrays, which place the first element in position 0. Another way to add an element to an array is to use either the ArrayAppend() or ArrayPrepend() function.This method of adding elements ensures you dont place a value in an unintended index of the array. ArrayPrepend() will prepend your value to the array, making it the first element and pushing all other elements down an index.This is a useful function if you need to add an element to the beginning of an array, but arent worried about the other elements index values. For example, in the previous technique, Wouldnt It Be Nice gains an index of 1, whereas You Still Believe In Me and Thats Not Me now have index values of 2 and 3, respectively (even though their index values were given explicitly). ArrayAppend() adds your element to the end of an array, making it the last element in the array, and leaving the other elements where they are. Use the ArrayAppend() function when you need to maintain the integrity of the arrays current index. Notice that Dont Talk (Put Your Head On My Shoulder) is now a part of the petSounds array, but it has simply been tacked on, and has an index value of 4.This leaves the first three song titles index values untouched.

3.3. Displaying a Value in an Array


You want to retrieve and display a value stored in an array.

Technique
Use <cfoutput> tags and reference the desired value by index to retrieve and display the value stored in a specific array element.
<cfoutput> Song 1: #aPetSounds[1]#<br> Song 2: #aPetSounds[2]#<br>

40

Chapter 3

Arrays

Song 3: #aPetSounds[3]#<br> Song 4: #aPetSounds[4]# </cfoutput>

Comments
Executing this code produces the following output:
Song Song Song Song 1: 2: 3: 4: Wouldnt It Be Nice You Still Believe In Me Thats Not Me Dont Talk (Put Your Head On My Shoulder)

If you attempt to reference an element that does not exist in your array, ColdFusion throws an ArrayBoundException exception. Refer to Chapter 8, Exception Handling, for more information on exceptions in CFMX.

3.4. Looping over an Array


You want to iterate over each element in an array.

Technique
Use <cfloop> to iterate over each element in an array.
Pet Sounds Track Listing <hr> <cfoutput> <!---loop over array and display each element value---> <cfloop from=1 to=#ArrayLen(aPetSounds)# index=i> Song #i#: #aPetSounds[i]#<br> </cfloop> </cfoutput>

Comments
This example revisits the aPetSounds array and loops from 1 to the length of the array to extract and display the value of each element. Looping over an array to display its values ensures that you wont reference an index that doesnt exist.This code also shows that you can use ColdFusion expressions and variables to reference indexes within an arrayin this case, you use the current index of the loop to retrieve the associated index in the array.
Note
This code introduces the ArrayLen() function. ArrayLen() accepts a single argument: the array in question. Similar to the Len() function, which counts the number of characters in a string, ArrayLen() returns the number of elements in an array.

3.6.

Sorting an Array

41

3.5. Manipulating Array Values


You want to access or modify specific elements within an array.

Technique
Manipulate elements in a populated array by iterating over the array and performing the desired function during each iteration of the loop.
<!---Manipulate each element to append song number---> <cfloop from=1 to=#ArrayLen(aPetSounds)# index=i> <cfset aPetSounds[i] = Song & i & : & aPetSounds[i]> </cfloop>

Comments
Arrays are used in Web applications to store ordered collections of related data. Naturally, as a user progresses through an application, data will change.Therefore, you need a way to manipulate the values of an array in order to perform functions on element values. Say you need to add the track number to each value in the aPetSounds array.You can do so by looping over the array values and manipulating the string to include the loop number, resulting in the desired value. Notice in the first cfloop statement that the value of the current element is referenced and a new value is set in the same line.This inline variable manipulation is acceptable in CFML.You also use the index variable i to insert the song numbera simple example of benefiting from the order functionality provided by the array data type. To display the new values with the track number prepended to the value, execute the following code:
Pet Sounds Track Listing <hr> <cfoutput> <!---loop over array and display each element value---> <cfloop from=1 to=#ArrayLen(aPetSounds)# index=i> #aPetSounds[i]#<br> </cfloop> </cfoutput>

3.6. Sorting an Array


You want to sort the elements of an array.

Technique
Use the ArraySort() function to sort the elements of an array.
<cfset abcList = c,B,b,A,C,a>

42

Chapter 3

Arrays

<cfset aAbcArray = ArrayNew(1)> <cfloop from=1 to=#ListLen(abcList)# index=i> <cfset aAbcArray[i] = listGetAt(abcList,i)> </cfloop> aAbcArray:<br/> <cfoutput> <cfloop from=1 to=#ArrayLen(aAbcArray)# index=i> element #i#: # aAbcArray[i]#<br/> </cfloop> </cfoutput> <p/> aAbcArray sorted with text attribute:<br/> <cfset ArraySort(aAbcArray,text)> <cfoutput> <cfloop from=1 to=#ArrayLen(aAbcArray)# index=i> element #i#: # aAbcArray[i]#<br/> </cfloop> </cfoutput> <p/> aAbcArray sorted with text and desc attributes:<br/> <cfset ArraySort(aAbcArray,text,desc)> <cfoutput> <cfloop from=1 to=#ArrayLen(aAbcArray)# index=i> element #i#: # aAbcArray[i]#<br/> </cfloop> </cfoutput> <p/> aAbcArray sorted with textnocase attribute:<br/> <cfset ArraySort(aAbcArray,textnocase)> <cfoutput> <cfloop from=1 to=#ArrayLen(aAbcArray)# index=i> element #i#: # aAbcArray[i]#<br/> </cfloop> </cfoutput> <p/> aAbcArray sorted with textnocase and desc attributes:<br/> <cfset ArraySort(aAbcArray,textnocase,desc)> <cfoutput> <cfloop from=1 to=#ArrayLen(aAbcArray)# index=i> element #i#: # aAbcArray[i]#<br/> </cfloop> </cfoutput>

3.6.

Sorting an Array

43

Comments
The ArraySort() function sorts arrays numerically or alphabetically. It accepts two required arguments, the array to be sorted and the sort type, and one optional argument, sort order (which, if not provided, defaults to ascending). Using ArraySort(), the sort type argument can be numeric, text, or textnocase. If sort type is numeric, the array elements are sorted numerically in ascending or descending order based on the sort order argument. If no sort order is provided, ArraySort() uses ascending, the default. If sort type is text, the array elements are sorted alphabetically taking case into account. In text sorts, sort order ascending starts with uppercase A and ends with lowercase z, descending starts with lowercase z and ends with uppercase A. If sort type is textnocase, the array elements are sorted alphabetically without taking case into account.That is, if sort order is ascending, A/a precedes a B/b. If sort order is descending, a Z/z precedes a Y/y.Take a look at what happens when you execute the code in this technique:
aAbcArray: element 1: element 2: element 3: element 4: element 5: element 6: c B b A C a

aAbcArray sorted with text attribute: element 1: A element 2: B element 3: C element 4: a element 5: b element 6: c aAbcArray sorted with text and desc attributes: element 1: c element 2: b element 3: a element 4: C element 5: B element 6: A aAbcArray sorted with textnocase attribute: element 1: a element 2: A element 3: b

44

Chapter 3

Arrays

element 4: B element 5: c element 6: C aAbcArray sorted with textnocase and desc attributes: element 1: C element 2: c element 3: B element 4: b element 5: A element 6: a

3.7. Multidimensional Arrays


You want to create an array with more than one dimension.

Technique
Use ArrayNew(), passing in as a parameter the number of dimensions you require, to create a multidimensional array.
<!---Create a two dimensional aBeachBoysAlbums array---> <cfset aBeachBoysAlbums = ArrayNew(2)> <!---Build first album array, <cfset aBeachBoysAlbums[1][1] <cfset aBeachBoysAlbums[1][2] <cfset aBeachBoysAlbums[1][3] Surfin Safari---> = Surfin Safari> = County Fair> = Ten Little Indians>

<!---Build second album array, Surfin USA---> <cfset aBeachBoysAlbums[2][1] = Surfin USA> <cfset aBeachBoysAlbums[2][2] = Farmers Daughter> <cfset aBeachBoysAlbums[2][3] = Miserlou> <!---Build third album array, <cfset aBeachBoysAlbums[3][1] <cfset aBeachBoysAlbums[3][2] <cfset aBeachBoysAlbums[3][3] Surfer Girl---> = Surfer Girl> = Catch a Wave> = The Surfer Moon>

Comments
A multidimensional array is an array of arrays rather than an array of single values. Until this point in the chapter, one-dimensional arrays have been covered, and the array elements have all been simple valuesthe name of a song or a letter of the alphabet. In a multidimensional array, you use the array elements to store other arrays.

3.8.

Creating Arrays with More Than Three Dimensions

45

ColdFusion allows you to define up to three dimensions in an array with a call to the
ArrayNew() function. ArrayNew() takes one argument1, 2, or 3depending on

how many dimensions you need. This code is an example of creating a two-dimensional array. It continues using the Beach Boys as a model and uses a two-dimensional array to store album information in order of release. For this example, you use only the first three songs from each of their first three releases. If you want to display the values stored in the inner dimensions of an array, it is necessary first to get a handle on each element. Using cfloop is the best way to do so. Remember, you need exactly as many loops as you have dimensions in the array.

3.8. Creating Arrays with More Than Three Dimensions


You want to create an array with more than three dimensions.

Technique
Define multiple dimensions in an array by creating three dimensions at a time with ArrayNew().
<!---create three dimensional aDeepArray array---> <cfset aDeepArray = ArrayNew(3)> <!---build nine dimensions---> <cfset aDeepArray[1][1][1] = ArrayNew(3)> <cfset aDeepArray[1][1][1][1][1][1] = ArrayNew(3)> <cfset aDeepArray[1][1][1][1][1][1][1][1][1] = Im in the ninth dimension> <!----display first element in ninth dimension---> <cfoutput> #aDeepArray[1][1][1][1][1][1][1][1][1]# </cfoutput>

Comments
Although ArrayNew()lets you define only three dimensions, should the need arise you can bypass this constraint by setting any of the three dimension values to an array itself and repeat until you have the dimension depth you require.The code in this example places a string value in the lowly ninth dimension.

46

Chapter 3

Arrays

3.9. Array Aggregate Functions


You want to perform aggregate functions on array elements.

Technique
Use ColdFusion array aggregate functions to perform a function on all elements within an array.
<!---Create aArray array and set array values---> <cfset numList = 0,5,22,7,31> <cfset aArray = ListToArray(numList)> aArray aggregate functions <hr> <cfoutput> <!---Perform aggregate functions on aArray and display results---> Maximum Value: #ArrayMax(aArray)#<br> Minimum Value: #ArrayMin(aArray)#<br> Sum: #ArraySum(aArray)#<br> Average: #ArrayAvg(aArray)# </cfoutput>

Comments
Aggregate functions are functions executed on multiple valuesarray aggregate functions are functions executed on each array element value. Table 3.1 defines the four functions available in ColdFusion MX.
Table 3.1 CFMX Array Aggregate Functions Return Value Returns Returns Returns Returns the the the the maximum numeric value in array. smallest numeric value in array. numeric average of all values in array. numeric sum of all values in array.

Function ArrayMax(array) ArrayMin(array) ArrayAvg(array) ArraySum(array)

Two exceptions of note when dealing with array aggregate functions are as follows: If there is a non-numeric value in an array passed into an aggregate function, ColdFusion throws a runtime exception. If the array passed into an aggregate function is empty, each of the functions returns a 0.
n n

3.10.

Array Utility Functions

47

Note that ColdFusionMX returns 0 when an empty array is passed into an array aggregate function. ColdFusion 5 returned infinity.

3.10.

Array Utility Functions

You want to perform utility functions on arrays.

Technique
Use ColdFusion array utility functions.
<cfset aNewArray = ArrayNew(3)>

Comments
You have used a number of CFMX array utility functions in various techniques throughout this chapter.Table 3.2 defines each of the array utility functions, including ones you havent yet used. Dont worry if you dont understand them all yetthe more you work with arrays, the more youll find the need to reference this table.
Table 3.2 ColdFusion Array Utility Functions Return Value Places value in the (array length plus 1) element of the specified array. Returns true if successful. Deletes all values from the specified array. Returns true if successful. Deletes the value at index in the specified array. All elements with an index greater than index will be offset by -1. Returns true if successful.Throws InvalidArrayIndexException if index is not a valid index for array. Inserts value at index of array.The index for elements with an index greater than index will be offset by 1. Returns true if successful. Throws the InvalidArrayIndexException exception if index is not a valid index for array. Creates an array of the specified dimension, which must be 1, 2, or 3. Places value in first element of specified the array. All indexes are offset by 1. Returns true if successful.

Function
ArrayAppend(array, value) ArrayClear(array) ArrayDeleteAt(array, index)

ArrayInsertAt (array, index, value)

ArrayNew(dimension) ArrayPrepend(array, value)

48

Chapter 3

Arrays

Table 3.2

Continued Resets array length to the specified size. Returns true if successful. Sets elements from index startIndex to endIndex to specified value in array. Returns true if successful. Sorts all elements in array by sortType (numeric, text, or textnocase) in specified order (asc or desc). If array contains anything other than simple values, ArraySort throws ArraySortSimpleValueException exception. If array contains non-numeric values and sortType is numeric, ArraySort throws ValueNotNumeric exception. Returns true if successful. Swaps the values at index1 and index2 of specified array. Throws ArraySwapRangeException exception if either index1 or index2 is an invalid index for array. Returns true if successful. Returns a list delimited by delimiter (a comma by default) containing the values in array.Throws ArrayNotOneDimensionException exception if array contains more than one dimension. Returns an array populated with the contents of list using delimiter to specify indexes. Empty values in list are ignored.

ArrayResize(array, size) ArraySet(array, startIndex, endIndex, value) ArraySort(array, sortType [,order])

ArraySwap(array, index1, index2)

ArrayToList(array [, delimiter])

ListToArray(list [, delimiter])

3.11. Array Information Functions


You want to retrieve information about an array object.

Technique
Use ColdFusion array information functions to retrieve information about an array object.
<cfset aNewArray = ArrayNew(3)>

Comments
Array Information functions are useful for gathering information about array objects. There are three ColdFusion array information functions, described in detail in Table 3.3.

3.11.

Array Information Functions

49

Table 3.3

ColdFusion Array Utility Functions Return Value Determines whether the ColdFusion array object array is empty of elements. Returns true if empty. Returns the number of elements found in array. Determines whether array is a ColdFusion array object. If dimension is defined, determines whether the element contained in dimension is a ColdFusion array object. Returns true if the item is an array.

Function
ArrayIsEmpty(array)

ArrayLen(array)
IsArray(array [, dimension])

4
Structures
4.0. Introduction
A structure is a complex ColdFusion data type containing key/value pairs of unsorted data. You use a structure to store data when you want to create an unordered collection of associated keys and values.Table 4.1 shows an example of how you might imagine the data type in the form of a table.This example uses the University of Alabama and their most recent NCAA Division 1 Football National Championship.
Table 4.1 Representing a Structure Key
GameDate Opponent Coach

Value Jan 1, 1993 The University of Miami Gene Stallings

Those familiar with other programming languages might recognize this key/value pair concept as being an associative array. If so, you will be happy to know that you can refer to elements in a structure using traditional dot notation. If you arent familiar with associative arrays, no worriesthis chapter defines and discusses their similarities. This chapter discusses the many advantages of using structures to store data.You learn how to create, populate, refer to, loop over, and sort structures.You also might learn a bit about the great football tradition of the University of Alabama Crimson Tide.

52

Chapter 4

Structures

4.1. Creating a Structure


You want to create a new structure.

Technique
Create a ColdFusion structure explicitly by using the StructNew() function or implicitly by setting a key/value pair in a new structure.
<!---create stMyFirstStruct structure---> <cfset stMyFirstStruct = StructNew()> <!---create stAnotherStruct structure and populate new key someKey---> <cfset stAnotherStruct.someKey = someValue>

Comments
There are two ways to create a structure in ColdFusion.The first is to use the StructNew() function.The first example in the technique shown here illustrates how to create a structure using this function.The other way to create a structure is to simply set a key/value pair inside the structure, thereby creating the structure and an element in that structure with a single line of code.The second example in the current technique uses only a single line of code to create a structure and to populate an element with the key someKey and the value someValue.
Note
As a best practice, all structures in this chapter begin with the letters st, as in stStructureName, except for those defined by ColdFusion, such as CGI and the named scopes.

4.2. Using Structure Information Functions


You want to inspect a structure using ColdFusion structure information functions.

Technique
Use IsStruct(), StructIsEmpty(), StructKeyExists(), and information about a structure.
StructKeyList()

to get

<cfoutput> <!---determine if cgi is a structure---> is CGI a structure? #IsStruct(cgi)#<br> <!---determine if structure cgi is empty---> is the CGI structure empty? #StructIsEmpty(cgi)#<br> <!---determine if key HTTP_USER_AGENT exists in structure cgi---> does the CGI structure contain a HTTP_USER_AGENT key?

4.3.

Using Structure Management Functions

53

#StructKeyExists(cgi,HTTP_USER_AGENT)#<br> <!---Display all keys in structure cgi---> What keys does the CGI structure contain? #StructkeyList(cgi)#<br> </cfoutput>

Comments
Table 4.2 defines all four CFMX structure information functions and their return values.
Table 4.2 CFMX Structure Information Functions Function
IsStruct(variable) StructCount(structure) StructIsEmpty(structure) StructKeyExists(structure, key)

Return Value Returns true if variable is a valid structure, false if it is not. Returns the number of keys in structure. Returns true if structure contains no elements, false if it contains one or more. Returns true if key is a valid key in structure, false if it is not.

Notice the code in the previous technique doesnt include a call to StructNew(), nor does it create a value for the HTTP_USER_AGENT key. Actually, it doesnt create any of the keys listed in the output of the StructKeyList() function.The reason? The CGI structure is available for manipulation because it is a CFMX named variable scope. All named variable scopes in CFMX are made available as structures.
Note
CFMX, unlike previous versions of ColdFusion, provides the Caller, Client, Server, and Variables scopes as structures.

4.3. Using Structure Management Functions


You want to manage a structure using ColdFusion structure management functions.

Technique
<cfset structAppend(stStructA,stStructB)>

Comments
StructAppend() is one of nine CFMX structure management functions.Table 4.3 defines all nine CFMX structure management functions and describes their functionalities.

54

Chapter 4

Structures

Table 4.3 CFMX Structure Management Functions Function


StructAppend(structureA, structureB[, overwrite])

Functionality Appends elements in structureA to structureB. If overwrite is false, the

value for a duplicate element is not copied. If its true, it is copied.The default is true. Returns true if the operation is successful; false if not. StructClear(structure) Removes all keys and values from structure. Returns true if the operation is successful; false if not. StructCopy(structure) Copies and returns structure with the same keys and values. Note that all elements are copied by value except nested structures, which are copied by reference. StructDelete(structure, key Deletes key and its corresponding value [, booleanReturn]) from structure. If booleanReturn is true, returns value boolean true if key exists and false if not. If booleanReturn is false, the return value is true. booleanReturn is false by default. StructFind(structure, key) Finds key in structure and returns its corresponding value. If key does not exist in structure, throws a runtime IllegalStructAccessException exception. StructInsert(structure, key, value ) Inserts new element into structure with [, overwrite] key and value. If overwrite is false, the value for a duplicate element is not copied. If its true, it is copied.The default is false. Returns true if the operation is successful; false if not. If key exists and overwrite is false, throws a StructInsertException exception. StructNew() Creates and returns a new, empty structure. StructSort(structure [, sortType Sorts structure or element structure of [ , order] ] [, structureReference based on structureReference]| sortType and order. Returns an array of structures populated with sorted key names. StructUpdate(structure, key, value) Updates structure and replaces value in element key to value. If key does not exist in structure, throws a runtime IllegalStructAccessException exception.

4.4.

Inserting Values into a Structure

55

The CFMX structure management functions are for managing structures inside your application. Be careful when manipulating the ColdFusion named scope structures, as you might experience unintended results.
Note
Do not use StructClear() to clean up your session variables, because this call will also remove the CFID and CFTOKEN variables, effectively destroying the session as opposed to clearing the session variables. Use StructDelete() if you truly want to clear the session variables. For more on sessions, see Chapter 7, Application Flow.

4.4. Inserting Values into a Structure


You want to insert values into a structure.

Technique
Use StructInsert() to insert data into a structure.
<!---create parent structure stUndefeated---> <cfset stUndefeated = Structnew()> <!---create structure for 1992 team---> <cfset stTide1992 = StructNew()> <!---populate stTeam1992 structure using StructInsert() function---> <cfset StructInsert(stTide1992,Year,1992)> <cfset StructInsert(stTide1992,Coach,Gene Stallings)> <cfset StructInsert(stTide1992,Victories,13)> <cfset StructInsert(stTide1992,TotalPoints,366)> <cfset StructInsert(stTide1992,TotalOpponentPoints,122)> <!---insert stTeam1992 into the parent stUndefeated structure---> <cfset StructInsert(stUndefeated, stTide1992,stTide1992)> <!---create structure for 1979 team---> <cfset stTide1979 = StructNew()> <!---populate stTeam1997 structure using StructInsert() function---> <cfset StructInsert(stTide1979,Year,1979)> <cfset StructInsert(stTide1979,Coach,Paul Bryant)> <cfset StructInsert(stTide1979,Victories,12)> <cfset StructInsert(stTide1979,TotalPoints,383)> <cfset StructInsert(stTide1979,TotalOpponentPoints,67)> <!---insert stTeam1992 into the parent stUndefeated structure---> <cfset StructInsert(stUndefeated, stTide1979,stTide1979)>

Comments
After you create your structure, the natural next step is to populate it with data and otherwise manipulate the information stored therein.

56

Chapter 4

Structures

To simply place a key/value pair into a structure, you use the StructInsert() function. StructInsert() accepts four argumentsthe structure into which you need to insert the element, the key, the value, and an optional Boolean fourth argument defining whether you want to overwrite a value should the key you are attempting to insert already exist. The previous technique shows an example of creating a structure of structures that store simple string data about the associated years undefeated teams. To illustrate inserting data into a structure, theres a structure of structures representing the University of Alabamas last two undefeated national championship teams.The Crimson Tide had two undefeated national championship teams in 13 yearsone in 1979 and one in 1992. Remember though that there will be no inherent order in the parent structure, so you cant retrieve the teams in order by year until you use StructSort(), discussed later in this chapter. If you need to store the data in order, you use the CFMX data type Arrays, discussed in detail in Chapter 3, Arrays.There is no guarantee as to the order of the elements in a structurein fact, the very nature of a structure dictates that there is no order to its elements.

4.5. Updating Values in a Structure


You want to update an existing value in a structure.

Technique
Use StructUpdate() to update existing key/value pairs in a structure.
<!---create the stTide1979 structure---> <cfset stTide1979 = StructNew()> <!---populate stTide1979 structure using StructInsert() function---> <cfset StructInsert(stTide1979,Coach,Paul Bryant)> <cfoutput> <!---display Coach value before update---> Before Update: #StructFind(stTide1979,Coach)#<br> </cfoutput> <!---update Coach key in stTide1979 structure---> <cfset StructUpdate(stTide1979,Coach,Paul (Bear) Bryant)> <cfoutput> <!---display Coach value after update---> After Update: #StructFind(stTide1979,Coach)# </cfoutput>

Comments
StructUpdate() accepts three arguments: the structure to update, the existing key in the structure, and the new value to be associated with that key.The example here

4.6.

Displaying the Value of an Element in a Structure Using the Key Name

57

updates the value of the key coach from Paul Bryant to Paul (Bear) Bryant. Calling StructUpdate() with a key that does not exist in the target structure results in an IllegalStructAccessException exception.

4.6. Displaying the Value of an Element in a Structure Using the Key Name
You need to display the value of a structure element using the key name.

Technique
Use StructFind() to reference the value of an element in a structure using the key name.
<!---set variable theKey equal to string Coach---> <cfset theKey = Coach> <!---create the stTide1979 structure---> <cfset stTide1979 = StructNew()> <!---populate stTide1979 structure using StructInsert() function---> <cfset StructInsert(stTide1979,theKey,Paul Bryant)> <cfoutput> <!---display Coach value before update---> Before Update: #StructFind(stTide1979,theKey)#<br> </cfoutput> <!---update Coach key in stTide1979 structure---> <cfset StructUpdate(stTide1979,theKey,Paul (Bear) Bryant)> <cfoutput> <!---display Coach value after update---> After Update: #StructFind(stTide1979,theKey)# </cfoutput>

Comments
The technique in section 4.5 uses StructFind() to display the value of the Coach element in the stTide1979 structure.The technique in this section displays the same information but uses an expression to reference the desired value instead of the literal string name of the key. StructFind() is one of two ways to reference elements of a structuresection 4.7 describes the other.

58

Chapter 4

Structures

4.7. Using Dot Notation


You want to use dot notation to manipulate the contents of a structure.

Technique
Use dot notation or one of two other methods to insert values into and retrieve values from a structure.
<!---create stTide2003 structure--<cfset stTide2003 = StructNew()> <!---set coach value using dot notation---> <cfset stTide2003.Coach = Mike Price> <!---build stQuarterbacks structure, populate with three values---> <cfset stQuarterbacks.starter = Brodie Croyle> <cfset stQuarterbacks.backup = Spencer Pennington> <!---set stQuarterbacks value in stTide2002 using function StructInsert()---> <cfset StructInsert(stTide2003,stQuarterbacks,stQuarterbacks)> <!---set Projected Record value to 11-2 using inline method ---> <cfset stTide2003[Projected Record] = 11-2> <!---cfset ---> <cfoutput> <!---display Coach value using inline method---> 2003 Coach: #stTide2003[Coach]#<br> <!---display Projected Record value using function StructFind()---> 2003 Projected Record: #StructFind(stTide2003,Projected Record)#<br> <!---display stQuarterback value using dot notation---> 2003 Quarterbacks: #StructCount(stTide2003.stQuarterbacks)#<br> <ul> <li>Starter: #stTide2003.stQuarterbacks.starter#</li> <li>Backup: #StructFind(stTide2003.stQuarterbacks,backup)#</li> </ul> </cfoutput>

Comments
Dot notation is the means of referring to a value by using the syntax structureName.keyName (if you are familiar with object oriented programming concepts, you probably recognize dot notation from object/property relationships).The technique in this section shows examples of all three ways to insert values into and retrieve values from a structure, including using dot notation. If youve programmed in ColdFusion before, chances are youve been using dot notation to reference session variables, URL variables, form variables, or other values from CFMX variable scopes. Rememberall named variable scopes in CFMX are available as structures, so everything you learn in this chapter not only pertains to structures you build for your own purposes, but also to the CFMX named scopes.

4.8.

Looping Over a Structure

59

The technique in this section also takes advantage of populating structure elements with other structures (this was covered earlier in the chapter). Note that to refer to elements of sub-structures, you simply provide the path to the parent structure and use your function or dot notation as usual.

4.8. Looping Over a Structure


You want to iterate over the key/value pairs within a structure.

Technique
Use StructKeyList() and cfloop to iterate over the elements in a structure.
<!---create stGreatTidePlayers structure---> <cfset stGreatTidePlayers = StructNew()> <!---populate stGreatTidePlayers structure as an associative array---> <cfset stGreatTidePlayers[Joe Namath] = Quarterback> <cfset stGreatTidePlayers[Lee Roy Jordan] = Linebacker> <cfset stGreatTidePlayers[Derrick Thomas] = Linebacker> <cfset stGreatTidePlayers[Shuan Alexander] = Running Back> <cfset stGreatTidePlayers[Johnny Musso] = Halfback> <!---present stGreatTidePlayers to browser---> Famous Crimson Tide Football Players<br> <table border=1 cellpadding=5> <tr> <td width=50%> <b>Player Name</b> </td> <td width=50%> <b>Position</b> </td> </tr> <cfoutput> <!---use StructKeyList() to loop over keys in stGreatTidePlayers---> <cfloop list=#StructKeyList(stGreatTidePlayers)# index=i> <tr> <!---display i, which is the current key---> <td>#i#</td> <!---use i to find the value stored in appropriate element---> <td>#StructFind(stGreatTidePlayers,i)#</td> </tr> </cfloop> </cfoutput> </table>

60

Chapter 4

Structures

Comments
One of the more common tasks in dealing with structures is to loop over the structure to manipulate its elements. One important point to remember is that, because there is no order to elements in a structure, there is no way to predict the order in which the elements will appear in a loop. The technique in this section defines a structure that provides some information of just a few of the greatest Crimson Tide football players.You build the associative array by storing a player name in the key portion of the element and the position they played in the value portion. This example uses an associative array rather than a traditional structure. A traditional structure follows the object/property design defined earlier in the chapter. An associative array, by contrast, stores values in both the key and the value portion of a structure element. To iterate over the stGreatTidePlayers structure, you use the StructKeyList() function and loop over the returned list of keys. Each iteration of the list provides the current list item as an index, and you can use the current list item to reference the value of each element, because you know the item will be a key into the structure. Another way to loop over the structure is to pass the structure into the collection argument of cfloop.The technique code produces the same output should you change the cfloop line to read:
<cfloop collection=#stGreatTidePlayers# item=i>

Notice that you use StructFind() instead of dot notation to reference the value stored in each element of the stGreatTidePlayers structure.This is because the key portion of the element, in this case, contains a space (Lee Roy Jordan). Be careful if you dont know if the value of a key is valid within the dot notation syntax. CFMX will throw a ParseException exception when attempting to reference those keys.

4.9.

Using Structure Utility Functions

You want to manipulate a structure using ColdFusion structure utility functions.

Technique
Use structure utility functions to manipulate the contents of a structure.
<cfset cgiKeys = structKeyList(cgi)>

Comments
StructKeyList() is one of six CFMX structure utility functions.Table 4.4 defines all

six of these functions and describes their functionalities.

4.10.

Copying Structures

61

Table 4.4 CFMX Structure Utility Functions Function


Duplicate(structure) StructFindKey(structure, searchString [,searchScope])

Functionality Returns an exact copy of structure. All items are copied by value. Searches structure and its sub-collections for a key matching searchString. Returns an array of structures for each one found. Structure contains the elements owner, value, and path. If searchScope is set to all, all matching items are returned. If searchScope is set to one, only one item is returned.The default searchScope is one. Searches structure and its sub-collections for a value matching searchString. Returns an array of structures for each one found. Structure contains the elements owner, key, and path. If searchScope is set to all, all matching items are returned. If searchScope is set to one, only one item is returned.The default searchScope is one. Returns the item found in string structurePath. If no structure is found using structurePath, an empty one is created in structurePath and returned. Returns an array of keys found in structure.The order is unpredictable as structures do not impose order on their elements. Returns a list of keys found in structure. Delimiter is a comma by default unless otherwise specified.

StructFindValue()

StructGet(structurePath)

StructKeyArray()

StructKeyList(structure [,delimiter])

The CFMX structure utility functions manipulate structures inside your application. Remember that all named variable scopes are provided as structures in CFMX, making these utility functions powerful when dealing with session, URL, form, and other scopes.

4.10. Copying Structures


You want to copy the contents (deep or shallow) of a structure to another structure.

Technique
Use Duplicate() to perform a deep copy of a structure. Use StructCopy() to perform a shallow copy.

62

Chapter 4

Structures

<!---create stBamaTradition structure---> <cfset stBamaTradition = StructNew()> <cfset StructInsert(stBamaTradition,SECChampionships,21)> <!---create stNationalChampionships structure as an element of stBamaTradition---> <cfset stBamaTradition.stNationalChampionships.actual = 12> <!---some lesser schools dispute the actual number of Bama Championships---> <cfset stBamaTradition.stNationalChampionships.claimedByRivals = 8> <!---perform a deep copy and a shallow copy of stBamaTradition structure using Duplicate() and StructCopy()---> <cfset stDeepCopyBamaTradition = Duplicate(stBamatradition)> <cfset stShallowCopyBamaTradition = StructCopy(stBamatradition)> <!---Bama wins an SEC and a National Championship in 2004---> <cfset StructUpdate(stBamaTradition,SECChampionships,22)> <cfset StructUpdate(stBamaTradition.stNationalChampionships,claimedByRivals,9)> <cfset StructUpdate(stBamaTradition.stNationalChampionships,actual,13)> <cfoutput> <!---display information stored in stBamaTradition structure---> <b>stBamaTradition - original structure</b><br> SEC Championships: #stBamaTradition.SECChampionships#<br> National Championships claimed by rivals: #stBamaTradition.stNationalChampionships.claimedByRivals#<br> National Championships actual: #stBamaTradition.stNationalChampionships.actual#<br> <p> <!---display information stored in stDeepCopyBamaTradition structure---> <b>stDeepCopyBamaTradition - created using Duplicate()</b><br> SEC Championships: #stDeepCopyBamaTradition.SECChampionships#<br> National Championships claimed by rivals: #stDeepCopyBamaTradition.stNationalChampionships.claimedByRivals#<br> National Championships actual: #stDeepCopyBamaTradition.stNationalChampionships.actual#<br> <p> <!---display information stored in stSshallowCopyBamaTradition structure---> <b>stShallowCopyBamaTradition - created using StructCopy()</b><br> SEC Championships: #stShallowCopyBamaTradition.SECChampionships#<br> National Championships claimed by rivals: #stShallowCopyBamaTradition.stNationalChampionships.claimedByRivals#<br> National Championships actual: #stShallowCopyBamaTradition.stNationalChampionships.actual#<br> </cfoutput>

Comments
A deep copy copies all items, including sub-elements, by value. It returns an exact duplicate of the target structure. A shallow copy copies all items in the first dimension by value and all sub-elements by reference.

4.11.

Sorting Structures

63

Notice the output of the previous technique code.You created a first dimension element named SECChampionships.Then you performed both a deep copy and a shallow copy on the stBamaTradition structure before changing the value of SECChampionships. Notice the only structure that contains the updated value is the original stBamaTradition structure.This is because both the deep copy and shallow copy return the first dimension elements by value. The difference is in the items beneath the first dimension.The stNationalChampionships structure created to provide information about the Tides numerous championship years is copied by value into stDeepCopyBamaTradition using the Duplicate() function and copied by reference into stShallowCopyBamaTradition using the StructUpdate() function.This means that any changes to the stNationalChampionships structure will not appear in the stDeepCopyBamaTradition. After you update the values to reflect 2004 as a championship year, stDeepCopyBamaTradition still contains the value 12 for its actual key. stShallowCopyBamaTradition, however, contains the new correct value (13) because it does not contain a structure named stNationalChampionships. It instead contains a reference to the stNationalChampionships structure in stBamaTradition.

4.11. Sorting Structures


You want to sort a structure.

Technique
Use StructSort() to sort values in a structure.
<!---create and populate stTotalChampionships structure---> <cfset stTotalChampionships = StructNew()> <cfset structInsert(stTotalChampionships,Nebraska,5)> <cfset structInsert(stTotalChampionships,Oklahoma,7)> <cfset structInsert(stTotalChampionships,Notre Dame,9)> <cfset structInsert(stTotalChampionships,Alabama,7)> <cfset structInsert(stTotalChampionships,Ohio State,6)> <!---sort structure based on number of championships, set return array to aTCArray---> <cfset aTCArray = StructSort(stTotalChampionships,numeric,desc)> <cfoutput> <table cellpadding=5> <tr> <td><b>School</b></td> <td><b>Championships</b></td> </tr> <cfloop list=#ArrayToList(aTCArray)# index=i>

64

Chapter 4

Structures

<tr> <!---display school Name---> <td>#i#</td> <!---retrieve element from structure and display value---> <td>#stTotalChampionships[i]#</td> </tr> </cfloop> </table> </cfoutput>

Comments
The first rule of structures is that there is no order to their elements. It is obviously necessary for CFMX to provide programmers with a way to sort data stored in a structure. The StructSort() function enables you to do just that. StructSort() returns a sorted array of keys from the structure you provide to the function. It does not perform any action on the structure itself, so executing StructSort() does not change the elements of the structure in any way. Its impossible to sort an object without order, right? StructSort() accepts one required and three optional arguments.These arguments are presented in Table 4.5.
Table 4.5 StructSort()s Arguments Argument
Structure sortType

Description The structure containing the keys you want returned sorted in specified order. text, textnocase, or numeric. text sorts with case sensitivitylowercase values are grouped apart from uppercase values. textnocase disregards case in values. numeric sorts values numerically.The default is text. asc or desc. asc is for ascending sorts; desc is for descending sorts.The default is asc. The path to sub-element containing values required for the sort. Necessary for sorts based on sub-element values.

sortOrder structureReference

In the previous technique, the call to StructCopy() returned an array, which you set to the variable aTCArray.You then use the ArrayToList() function to create a list from the array values, which results in a sorted, comma-delimited list of keys for the structure. Now suppose you need to sort based on the value of a certain sub-element contained in the structure.The following code provides information about the four great coaches who won national championships at Alabama.The keys of the first dimension of the structure are the coaches names.The second dimension stores numerical data about the

4.11.

Sorting Structures

65

number of championships won and the most recent winning year for each coach. It then sorts the keys to the first dimension based on the values stored in the second.
<!---create championships structure---> <cfset stChampionships = structNew()> <!---build Wallace Wade---> <cfset stWW = StructNew()> <cfset stWW.count = 3> <cfset stWW.recent = 1930> <cfset stChampionships[Wallace Wade] = stWW> <!---build Frank Thomas---> <cfset stFT = StructNew()> <cfset stFT.count = 2> <cfset stFT.recent = 1941> <cfset stChampionships[Frank Thomas] = stFT> <!---build Paul Bryant---> <cfset stPB = StructNew()> <cfset stPB.count = 6> <cfset stPB.recent = 1979> <cfset stChampionships[Paul Bear Bryant] = stPB> <!---build Gene Stallings---> <cfset stGS = StructNew()> <cfset stGS.count = 1> <cfset stGS.recent = 1992> <cfset stChampionships[Gene Stallings] = stGS> <cfoutput> In order of most championships <hr> <table cellpadding=5> <tr> <td><b>Coach</b></td> <td><b>Number of Championships Won</b></td> </tr> <!---build list from sorted structure---> <cfset theCoachesList = ArrayToList(StructSort(stChampionships, numeric, DESC, count))> <cfloop list=#theCoachesList# index=i> <tr> <!---display coach name---> <td>#i#</td> <!---display count value---> <td>#stChampionships[i].count#</td> </tr> </cfloop> </table> <p> In order of most recent championship

66

Chapter 4

Structures

<hr> <table cellpadding=5> <tr> <td><b>Coach</b></td> <td><b>Last Championship Won</b></td> </tr> <cfset theCoachesList = ArrayToList(StructSort(stChampionships, numeric, DESC, recent))> <cfloop list=#theCoachesList# index=i> <tr> <!---display coach name---> <td>#i#</td> <!---display recent value---> <td>#stChampionships[i].recent#</td> </tr> </cfloop> </table> </cfoutput>

Remember that StructSort() returns an array of keys to the structure passed in the first argument.You need to reference the struct again to retrieve the value associated with that key.

5
Queries
5.0. Introduction
ColdFusions capability to connect to a data source and retrieve data is possibly the most widely used functionality in the language. Most developers are familiar with the query object through its use in the process required to connect to and retrieve data from a data source. In addition to the base query functionality, ColdFusion provides developers the capability to do the following:
n n

Manually create query objects for use in Web applications Query the recordset retrieved from a previous query (thus creating a query of queries) Manage the data contained within a query recordset Cache queries and limit the number of calls to the database

This chapter discusses and provides examples of all of these features and more.Youll learn that the ColdFusion query object is a powerful, useful construct and can be used in many ways.

5.1. Querying a Database


You want to retrieve values from a database.

Technique
Use the ColdFusion <cfquery> tag to connect to a database and return a recordset in the form of a ColdFusion query object.
<!---query the cfcookbook database---> <cfquery datasource=cfcookbook name=qGetUsers>

68

Chapter 5

Queries

SELECT UserName, PassWord FROM Users </cfquery> <cfoutput> is qGetUsers a query object? #isQuery(qGetUsers)# </cfoutput>

Comments
This technique shows a simple query against the Users table of the cfcookbook data source (the cfcookbook schema is defined in detail in Chapter 13, User Authentication and Authorization). Executing this code example presents a simple YES to the users the result of the isQuery() function call. This code example uses only those parameters required to execute a query against a data source using the <cfquery> tag: the data source and name (if you are performing a query of queries, as is discussed later in this chapter, the datasource parameter is not used).The ColdFusion <cfquery> tag accepts two required and nine optional parameters.Table 5.1 describes each of these parameters and describes their functions.
Table 5.1 cfquery Parameters Parameter
Datasource Name blockFactor cachedAfter

Function Required. Name of the ColdFusion data source for the query. Required. ColdFusion executes the query and returns a query object with the name specified in this parameter. This parameter pertains only to Oracle data sources and defines the number of records to return from the database at one time. Must be a valid ColdFusion date value. If query caching is enabled in the ColdFusion Administrator and this parameter value is less than the date of the original query, ColdFusion will return the cached recordset. Must be a valid ColdFusion time span (created with CreateTimeSpan() function). If query caching is enabled in the ColdFusion Administrator and the original query date falls within the specified time span, ColdFusion will return the cached recordset. Used in a query of queries to specify that the target dbType is a valid ColdFusion query object. Not used if the query target is a database. If debugging is enabled in the ColdFusion Administrator and Database Activity is disabled, a value of true overrides the Database Activity setting. Also, omitting the equals sign and the true value implies true. Maximum number of rows to return in the recordset. Password for the data source. Maximum time permitted to execute each operation performed in a query. Username of the data source.

cachedWithin

dbType Debug

maxRows Password Timeout Username

5.2.

Programmatically Creating Queries

69

The following attributes of the <cfquery> tag are deprecated as of ColdFusion MX: connectString, dbName, dbServer, provider, providerDSN, and sql. Using them in a ColdFusion MX environment may cause errors or otherwise unexpected results.
Note
As a best practice, all queries in this chapter begin with the letter q, as in qQueryName.

5.2. Programmatically Creating Queries


You want to manually create a new query object.

Technique
Use the QueryNew() function to programmatically create a ColdFusion query object. Use the QueryAddRow() and QuerySetCell() functions to programmatically add values to your query object.
<!---create a new query with two columns---> <cfset qSimpleQuery = QueryNew("firstName, lastname")> <!--- add a row to qSimpleQuery ---> <cfset newRow = QueryAddRow(qSimpleQuery)> <!--- add values to the row just added---> <cfset QuerySetCell(qSimpleQuery, "firstName", "Norman")> <cfset QuerySetCell(qSimpleQuery, "lastName", "Saen")> <!---add a column to the query---> <cfset qAgeArray = ArrayNew(1)> <cfset qAgeArray[1] = "12"> <cfset QueryAddColumn(qSimpleQuery, "age",qAgeArray)> <!--- Display the values in qSimpleQuery ---> <cfoutput query="qSimpleQuery"> #firstName# #lastName#, #age# years old<br> </cfoutput> <cfoutput>Total Records: #qSimpleQuery.recordCount#</cfoutput>

Comments
The capability to create ColdFusion query objects programmatically allows developers to take advantage of query-specific features, like the recordCount, currentRow, and columnList variables. As youll learn later in the chapter, a query object can also be the target of a <cfquery> executionenabling you to create a query of a query. The steps to create a query object are as follows:

70

Chapter 5

Queries

Create the query object using the QueryNew() function. Create one or more rows in the query object using the QueryAddRow() function. Add data to the query object using the QuerySetCell() function.

The QueryNew() function accepts a single required parameter, which is a list of columns to create in the query object.The previous technique created a query object named simpleQuery with the columns firstName and lastName. ColdFusion requires a column list to create a new query, even if it is empty. The QueryAddRow() function accepts one required and one optional parameter.The required parameter is the name of the query to which you need to add a row.The second and optional parameter is the integer number of rows you need to add. If you do not specify a number, the function adds only one.The example here shows only a single row and accepts the default value of 1. Note that the QueryAddColumn() function accepts three parameters, all required. The first is the name of the query to which you need to add the column.The second is the name of the new column.The third is an array of values.The values in the array are copied to the new column in the query in the order in which they appear in the array. If the query contains more rows than the array, the query will pad the column with empty values.

5.3. Displaying Query Results


You want to display the results of a query in a Web page.

Technique
Display the values stored in a query using <cfoutput>, <cfloop>, or by explicit reference.
<!---create presidents query object---> <cfset qPresidents = QueryNew(firstName, lastname)> <!--- add 2 rows to qPresidents ---> <cfset newRow = QueryAddRow(qPresidents,2)> <!--- populate the rows just added---> <cfset QuerySetCell(qPresidents, firstName, George,1)> <cfset QuerySetCell(qPresidents, lastName, Washington,1)> <cfset QuerySetCell(qPresidents, firstName, John,2)> <cfset QuerySetCell(qPresidents, lastName, Adams,2)> <!--- Display the values in qPresidents using cfoutput---> <b>Use cfoutput to display query values:</b><br> <cfoutput query=qPresidents> #firstName# #lastName#<br> </cfoutput>

5.4.

Using Query Variables

71

<p> <!--- Display the values in qPresidents using cfloop---> <b>Use cfloop to display query values:</b><br> <cfoutput> <cfloop query=qPresidents> #firstName# #lastName#<br> </cfloop> </cfoutput> <p> <!--- Display the values in qPresidents using an explicit reference---> <b>Use an explicit reference to display query values:</b><br> <cfoutput> <cfloop from=1 to=#qPresidents.recordCount# index=i> #qPresidents.firstName[i]# #qPresidents.lastName[i]#<br> </cfloop> </cfoutput>

Comments
This technique shows an example of each of the three ways to display the values stored in a query objectby using <cfoutput>, by using <cfloop>, and by explicitly referencing the value by column and row index. To use <cfoutput>, you must pass the name of the query into the query attribute of the <cfoutput> tag. Doing so causes the CFML inside the <cfoutput> to execute once for every record in the query, much like the behavior of a <cfloop> statement. Using <cfloop> with the query attribute is very similar to using <cfoutput> with the query attribute.The only difference is that there is no <cfoutput>. If you want to evaluate ColdFusion variables within the loop, you need to make sure your statements are contained within <cfoutput> tags. The third way to display the values stored in a query is to do so by explicitly referencing the value by column and by row index.The syntax is queryName.columnName[rowIndex], where queryName is the name of your query, columnName is the name of the column containing the variable you need to display, and rowIndex is the cardinal-based index of the row containing the value you need to display.

5.4. Using Query Variables


You want to inspect a query using ColdFusion query variables.

Technique
ColdFusion provides a number of variables along with the query scope to help you better understand the object. Reference the variables like you do variables of any scope to inspect their values.

72

Chapter 5

Queries

<!---query the cfcookbook database---> <cfquery datasource=cfcookbook name=qGetUsers> SELECT UserName, PassWord FROM Users </cfquery> <!---display information about the query---> <b>Query Information</b> <hr> <cfoutput> record count: #qGetUsers.recordCount#<br> column list: #qGetUsers.columnList#<br> current row: #qGetUsers.currentRow#<br> execution time: #cfquery.executionTime# </cfoutput>

Comments
The four ColdFusion Query Variables are recordCount, columnList, currentRow, and executionTime, and they are described as follows: recordCount provides the integer number of total records available in a query. columnList provides the list of column names present in the query. Note that the column names returned in the variable columnList are in alphabetical order rather than in the order provided in the SQL statement. currentRow provides the index of the current row.This is especially useful when executing cfloop statements. executionTime provides the cumulative number of seconds spent processing the query. Note that the syntax for retrieving the executionTime value is cfquery.executionTime rather than queryName.executionTime.
n n n n

The following code shows an example of inspecting the value of currentRow to interlace the background colors of the rows in a table. It uses columnList to display column headings and to retrieve query values. It also shows the query execution time and the total number of rows contained in the query.
<!---query the cfcookbook database---> <cfquery datasource=cfcookbook name=qGetUsers> SELECT FirstName, LastName, City, State FROM Users </cfquery> <table cellpadding=3> <cfoutput> <tr> <td colspan=#listLen(qGetUsers.columnList)#> <b>User Information</b> </td>

5.5.

Creating Queries of Queries

73

</tr> <tr> <!---display the column names---> <cfloop list=#qGetUsers.columnList# index=i> <td bgcolor=silver> <font color=white><b>#i#</b></font> </td> </cfloop> </tr> <cfloop query=qGetUsers> <!---interlace the background colors---> <tr bgcolor=#iif(currentRow MOD 2,de(f1f1f1),de(ffffff))#> <!---display the values---> <cfloop list=#qGetUsers.columnList# index=i> <td> #evaluate(i)# </td> </cfloop> </tr> </cfloop> <tr <td colspan=#listLen(qGetUsers.columnList)#> Total Records: #qGetUsers.recordCount<br> Execution Time: #cfquery.executionTime# </td> </tr> </cfoutput> </table>

5.5. Creating Queries of Queries


You want to perform SQL operations on an existing ColdFusion query.

Technique
Use <cfquery> to query an existing query object rather than a database. Pass in a value of query for the dbType parameter and the name of the existing query object for the datasource parameter.
<!---query the cfcookbook database---> <cfquery datasource=cfcookbook name=qGetUsers> SELECT FirstName, LastName, City, State FROM Users </cfquery> <!---query the qGetUsers query for people with state equal to NC--->

74

Chapter 5

Queries

<cfquery dbType=query name=qGetNCUsers> SELECT FirstName, LastName FROM qGetUsers WHERE State = NC </cfquery> <b>NC users</b> <hr> <cfoutput query=qGetNCUsers> #qGetNCUsers.FirstName# #qGetNCUsers.LastName#<br> </cfoutput> <p> <!---query the qGetUsers query for people with state not equal to NC---> <cfquery dbType=query name=qGetOtherUsers> SELECT FirstName, LastName FROM qGetUsers WHERE State <> NC </cfquery> <b>Other users</b> <hr> <cfoutput query=qGetOtherUsers> #qGetOtherUsers.FirstName# #qGetOtherUsers.LastName#<br> </cfoutput>

Comments
The query of queries (sometimes called an in-memory query) is a powerful ColdFusion feature. It can significantly improve performance if you find the need to select data from the same table more than once. Using a query of queries, you need only a single call to the database, as shown in the previous technique. Executing a <cfquery> call returns a dataset, which in many ways is similar to a database tablea recordset contains data structured as a virtual table with rows and named columns.This similarity is exploited in the query of queries functionality. ColdFusion treats the recordset as a database table against which you can execute SQL statements to retrieve data. Without a query of queries, the technique code would require two database calls to extract two separate recordsets with the desired dataone to retrieve users from NC and one to retrieve users not from NC. By using a query of queries, you can pull all the users in one database call and then query the returned recordset using the appropriate WHERE clauses. A query of queries is also beneficial in conjunction with cached queries.You can display the results of a single cached query in any number of ways across multiple templates using the cached recordset as a virtual table. Note also that you are not limited to database-returned recordsets when using a query of queries.You can use any valid ColdFusion recordset as the input.The result of a <cfdirectory> call, for example, is valid for input into a query of queries.

5.6.

Converting a Query Column to a List

75

5.6. Converting a Query Column to a List


You want to extract all query values for a specific column and store them in a list.

Technique
Use the ValueList() or QuotedValueList() functions to return a list of all values stored in a specific query column.
<!---query the cfcookbook database---> <cfquery datasource=cfcookbook name=qGetUsers> SELECT FirstName FROM Users </cfquery> <!---display a list of all the values in column UserName---> <b>All UserNames</b> <hr> <cfoutput>#ValueList(qGetUsers.FirstName)#</cfoutput> <p> <!---use quoted value list as IN clause in a new query---> <cfquery datasource=cfcookbook name=qGetEmployees> SELECT Name FROM Employees Where Name IN (#quotedValueList(qGetUsers.FirstName)#) </cfquery> <!---display Employees who share a first name with Users---> <b>Shared First Names</b> <hr> <cfoutput query=qGetEmployees> #Name#<br> </cfoutput>

Comments
The ValueList() and QuotedValueList() functions are used to create a list from a specific column in a query. ValueList() accepts one required and one optional argument.The required argument is the name of the query you need to transform into a list object.The optional argument is the delimiter to use for the list. If you do not provide a delimiter, a comma is used as the default. QuotedValueList() also creates a list from a query column but qualifies the values with single quotes.The function otherwise behaves exactly like ValueList(), with the same required and optional arguments.

76

Chapter 5

Queries

QuotedValueList() is especially useful in conjunction with a queryyou can use the quoted list as the IN clause of a SQL statement, as is shown in the previous technique. For more information on ColdFusion lists, see Chapter 2, Working with Lists.

5.7. Caching a Query


You want to use the cached result of a previous <cfquery> execution.

Technique
Use the cachedAfter or cachedWithin parameters of <cfquery> to use cached results if they exist.
<!---query cfcookbook for all users---> <!---use cached results if query executed since yesterday---> <cfquery datasource=cfcookbook name=qGetUsersA cachedAfter=#DateFormat(Now()-1)#> SELECT FirstName, LastName, City, State FROM Users ORDER BY LastName </cfquery> <!---display results---> <b>Users</b> <hr> <cfoutput query=qGetUsersA> #LastName#, #FirstName# - #City#, #State#<br> </cfoutput> <!---execution time should be 0 if cache is used---> <cfoutput> Execution Time: #cfquery.ExecutionTime# </cfoutput> <p> <!---use cached results if query executed within the last 2 minutes---> <cfquery datasource=cfcookbook name=qGetUsersB cachedWithin=#CreateTimeSpan(0, 0, 2, 0)#> SELECT FirstName, LastName, City, State FROM Users ORDER BY LastName </cfquery> <!---display results---> <b>Users</b>

5.7.

Caching a Query

77

<hr> <cfoutput query=qGetUsersB> #LastName#, #FirstName# - #City#, #State#<br> </cfoutput> <!---execution time should be 0 if cache is used---> <cfoutput> Execution Time: #cfquery.ExecutionTime# </cfoutput>

Comments
Query caching is a great tool for improving Web application performance.The fewer the number of calls you make to the database, the faster your application. You can control the number of queries ColdFusion will cache by changing the default setting. Up to 100 queries are cached by default.The stack will eliminate the oldest query if the threshold is reached. In order to use a cached query, the following stipulations must be met: Both queries must have the same name value Both queries must have the same datasource value Both queries must use the same SQL statement Both queries must have the same username, if applicable Both queries must have the same password, if applicable The cached query must have been executed within the supplied time span if cachedWithin parameter is provided The cached query must have been executed since the supplied date if the cachedAfter parameter is provided
n n n n n n n

The technique code shows an example of using cached queries with cachedAfter and cachedWithin.The first execution of the technique code should show a value for the execution time for both queries. An immediate refresh of the page will show the same data, but with execution times of 0, because ColdFusion used the cached query results rather than executing a new call to the database.

6
Dates and Times
6.0. Introduction
A date is a data type that represents an instant. A date is really both a date and a time all date objects represent both values. ColdFusion dates are accurate to the nearest second. Dates can be formatted in a number of ways.The same date object can be formatted as a date string, such as 05/16/67 in one case, and then formatted as a time, such as 7:08 a.m., in another. It is up to the developer to determine how a date should be rendered.There are some standard formats (called masks) that can help you present a date in the appropriate format. In one instance you may simply display a date as a string. In another, you may need to format the date as an ODBC date for a database query. In another case, the date may need formatting as an HTTP time string for setting cookie expiration. This chapter explores ColdFusion date/time support, including creating, formatting, displaying, and performing computations on date and time objects.

6.1. Creating a Date, Time, or DateTime Object


You want to create a ColdFusion Date, Time, or DateTime object.

Technique
Use the appropriate ColdFusion date and time create function to create a ColdFusion Date, Time, or DateTime object.
<cfset mycreatedate = CreateDate(2002, 5, 22)> <cfset mycreatetime = CreateTime(8, 12, 30)> <cfset mycreatedatetime = CreateDateTime(2002, 5, 22, 8, 12, 30)>

80

Chapter 6

Dates and Times

Comments
This technique is a simple example of creating three date objects representing a Date, a Time, and a DateTime.These date objects can now be used in any ColdFusion functions that expect a date object as a parameter. The parameters passed to each function used in the technique are presented in Table 6.1.
Table 6.1 Date and Time Create Parameters Function
CreateDate(year,month,day)

Parameters
year (0-9999),month (1-12),day (1-31). Note the time portion of the returned date object is set to 00:00:00. hour (0-23), minute (0-59), second (0-59). Note that the date portion of the returned date object is set to 1899-12-30 (December 30,

CreateTime(hour,minute,second)

1899).
CreateDateTime year (0-9999),month (1-12),day (1-31), hour (0-23), minute (0-59), second (0-59).

Notice that when you output the values created in the technique without a mask, the display is a timestampessentially unreadable unless your users are expecting it.The following code shows the result of displaying the three date objects created as raw date objects, without using a formatting mask.
mycreatedate = {ts 2002-05-22 00:00:00} mycreatetime = {ts 1899-12-30 08:12:30} mycreatedatetime = {ts 2002-05-22 08:12:30}

So the primary use of these create functions is to create a date object representing a point in time.These functions are not for display purposes.

6.2. Formatting Dates


You want to format a date for user-friendly display.

Technique
Use the ColdFusionDateFormat() function with the appropriate mask to format a date object for display.
<cfoutput>#DateFormat(CreateDate(2002,11,17),mmmm d, yyyy)#</cfoutput>

6.2.

Formatting Dates

81

Comments
The DateFormat() function is essential when using ColdFusion date objects. DateFormat() accepts two parameters: the date object (or string representation of one) to be formatted and the mask with which to format that date object.The mask parameter is a string representing the format to display.Table 6.2 describes available characters to create masks.
Table 6.2 DateFormat Mask Values Date Part
Year

Character Masks and Examples (formatting September 05,2002 6:06:05 PM) y: 2 yy: 02 yyyy: 2002 m: 9 mm: 09 mmm: Sep mmmm: September d: 5 dd: 05 ddd: Sun dddd: Sunday h: 6 hh: 06 H: 18 HH: 18 m: 6 mm: 06 s: 5 ss: 05 t: P tt: PM

Month

Day

Hour

Minute Second AM / PM suffix

ColdFusion also supplies preformatted masks to display date values.These are listed in Table 6.3.

82

Chapter 6

Dates and Times

Table 6.3 DateFormat Mask Values Mask Name


short medium long full

Example 11/17/02 Nov 17, 2002 November 17, 2002 Sunday, November 17, 2002

To display todays date, use the ColdFusion function Now() as the date parameter for the DateFormat() function. Now() returns a date object representing the present moment in time, from the current year to the current second.The following example uses DateFormat() to place different formatting masks on the returned value of Now(). Run this code on your machine and notice the different displays.
<cfoutput> <table border=1> <tr> <th>Mask</th> <th>Result</th> </tr> <tr> <td>full</td> <td>#DateFormat(Now(),full)#</td> </tr> <tr> <td>mm - dd - yy</td> <td>#DateFormat(Now(),mm - dd - yy)#</td> </tr> <tr> <td>d mmm</td> <td>#DateFormat(Now(),d mmm)#</td> </tr> </table> </cfoutput>

6.3. Comparing Dates


You want to determine whether a date is greater than, less than, or equal to another date.

Technique
Use the ColdFusion DateCompare() function to compare two date objects.

6.4.

Determining Date Differences

83

<cfset birthday = CreateDateTime(1975,9,22,9,7,0)> <cfoutput> #DateCompare(Now(),birthday)# </cfoutput>

Comments
The ColdFusion DateCompare() function accepts two required parameters and one optional parameter.The two required parameters are date objects, whereas the optional parameter is the precision you want to use for the comparison. DateCompare() returns one of three values: 1, 0, or 1. A 1 return value means the first date parameter is less than the second. A 0 return value means the two dates are identical. A 1 return value means the first date parameter is greater than the second. Executing the code in the previous technique will return a 1 because the date returned from the call to the Now() function is greater than the date value for the variable named birthday. You can also alter the precision with which DateCompare() compares two dates by providing a third parameter.This third parameter is the shorthand value for the date part you want to compareinstructing DateCompare() to be precise only to the supplied date part.Table 6.4 describes the values acceptable for this precision parameter.
Table 6.4 DateCompare Precision Values Precision Value
yyyy m d h n s

Description Year Month Day Hour Minute Second

Altering the precision used in DateCompare() is useful for comparing two date objects when, for example, you only need to know that they represent dates within the same minute or within the same hour.

6.4. Determining Date Differences


You want to determine the difference in time between two date objects.

Technique
Use the ColdFusion DateDiff() function to determine the difference in time between two date objects.

84

Chapter 6

Dates and Times

<cfset birthday = CreateDateTime(1975,9,22,9,7,0)> <cfoutput> #DateDiff(s,birthday,Now())# </cfoutput>

Comments
The ColdFusion function DateDiff() compares two dates and returns the number of selected date parts between the two.The previous technique returns the difference in seconds between the return value of the Now() function call and the value for the date object variable birthday. Executing this code displays a number value of nearly 800 million.Table 6.5 describes the accepted values for the date part parameter.
Table 6.5 DateDiff Date Part Parameter Values Date Part Value
s n h w ww d y m q yyyy

Description Second Minute Hour Week Weekday Day Day of Year Month Quarter Year

6.5. Formatting Times


You want to format a time for user-friendly display.

Technique
Use the ColdFusion TimeFormat() function with the appropriate mask to format a date object for display.
<cfoutput>#TimeFormat(now(), h:mm tt)#</cfoutput>

Comments
Use the ColdFusion TimeFormat() function with the appropriate mask to format the time portion of a date object for display. TimeFormat() accepts two parameters: the

6.6.

Defining Time Spans

85

date object (or a string representation of one) containing the time to be formatted and the mask with which to format that time.The mask parameter is a string representing the format to display.Table 6.6 describes available characters to create masks.
Table 6.6 TimeFormat Mask Values Time Part
Hour

Character Masks and Examples (formatting 6:05:04 PM Eastern Standard) h: 6 hh: 06 H: 18 HH: 18 (capital H used for 24 hour time) m: 5 mm: 05 s: 4 ss: 04 t: P tt: PM

Minute Second Suffix

ColdFusion also supplies preformatted masks to display time values.These are listed in Table 6.7.
Table 6.7 TimeFormat Mask Values Mask Name
Short Medium Long Full

Example 6:05 PM 6:05:04 AM 6:05:04 PM EDT 6:05:04 PM EDT

6.6. Defining Time Spans


You want to define a date or time period.

Technique
Use the ColdFusion function CreateTimeSpan() to define a date or time period.
<cfset myTimeSpan = #CreateTimeSpan(1, 12, 0, 0)#>

86

Chapter 6

Dates and Times

Comments
The CreateTimeSpan() function is useful in three ways. First, it is used as an attribute value in the <cfapplication> tag for defining session and application timeout (see Chapter 7, Application Flow, for details on ColdFusion applications). Second, it used as an attribute in <cfquery> tags in conjunction with query caching to define the time period for which a cached query is valid (see Chapter 5, Queries, for more detail on ColdFusion queries).Third, it is used to subtract from or add to DateTime objects. To add or subtract time to an existing date object, use CreateTimeSpan() and simple arithmetic on an existing date. See the following code; it shows todays, yesterdays, and tomorrows date using only the Now() function and a time span object representing a 24-hour period.
<cfset today = Now()> <cfset myTimeSpan = #CreateTimeSpan(0, 24, 0, 0)#> <cfset yesterday = today - myTimeSpan> <cfset tomorrow = today + myTimeSpan> <cfoutput> Today is #DateFormat(today,dddd, mmmm d)#<br> Yesterday was #DateFormat(yesterday,dddd, mmmm d)#<br> Tomorrow is #DateFormat(tomorrow,dddd, mmmm d)# </cfoutput>

6.7. Using Date Information Functions


You want to inspect a date object for specific information.

Technique
Use one of the many ColdFusion information functions to inspect a date object for specific information.
<cfoutput>#isLeapYear(Year(Now()))#</cfoutput>

Comments
ColdFusion provides a number of date information functions useful for inspecting date contents and for extracting information from date objects and portions of date objects. The previous technique uses two of these said functions to determine whether the current year is a leap year. If you execute this code during a leap year, the code will display a value of YES, otherwise it will display NO. Table 6.8 lists the ColdFusion information functions and their return values.

6.7.

Using Date Information Functions

87

Table 6.8 ColdFusion Date Information Functions Function


DayOfYear(dateObject) DaysInYear(dateObject) FirstDayOfMonth(dateObject)

Return Value The numeric day of the year contained in dateObject. The number of days of the year contained in dateObject. The number relating the first day of the month in dateObject to the beginning of the year contained in dateObject. The number of days of the month contained in dateObject. Integer must be in the range 1-12. Returns the month as a string corresponding to the supplied integer. The numeric day of the week contained in dateObject. Sunday is a 1; Saturday a 7. Integer must be in the range 1-7. Returns the day of the week as a string corresponding to the supplied integer. YES if year integer is a leap year; NO if it is not. The year contained in dateObject. The numeric quarter of the year contained in dateObject (1-4). The numeric month contained in dateObject. January is a 1; December is a 12. The number relating the week in dateObject to the beginning of the year contained in dateObject. The numeric day of the month contained in dateObject. The numeric hour contained in dateObject, returned as a 24-hour value. The numeric minute contained in dateObject. The numeric second contained in dateObject.

DaysInMonth(dateObject) MonthAsString(integer) DayOfWeek(dateObject) DayOfWeekAsString(integer)

IsLeapYear(integer) Year(dateObject) Quarter(dateObject) Month(date) Week(dateObject) Day(dateObject) Hour(dateObject) Minute(dateObject) Second(dateObject)

88

Chapter 6

Dates and Times

6.8. Managing Dates and Times in Different Locales


You want to format ColdFusion date objects in a locale-specific manner.

Technique
Use ColdFusion locale-specifc date functions to manage dates over different locales.
<cfoutput> Format for Locale #GetLocale()# is: #LSDateFormat(Now(),long)#<br> <cfset SetLocale(Italian (Standard))> Format for Italian (Standard) is: #LSDateFormat(Now(),long)#<br> </cfoutput>

Comments
Dates are closely linked to the locale. A locale setting tells ColdFusion which part of the world is using the application. Learning to take advantage of locale settings is a powerful skill for developers, especially in todays 24-hour e-business environment. ColdFusions default locale is the same as the default local of the underlying Java Virtual Machine. Locales allow ColdFusion to format dates according to a variety of pre-defined locations, so you dont need to know the various formats of those locations. ColdFusion provides rich functionality for managing date objects in a locale-specific manner. Executing the code in the previous technique will display the current time formatted for your default locale. It then changes the default locale for your client (which will last the extent of your session) to Italian (Standard). Displaying the date, using the same mask for the LSDateFormat() function, produces a new resultthis time formatted for the Italian (Standard) locale.
Note
ColdFusion can manage dates as locale-specific as long as the locale needed is defined on the ColdFusionMX Server. The Server.ColdFusion.SupportedLocales variable contains a commadelimited list of supported locales for the current server. Attempting to display date and time values in locales not defined on the server generates a runtime critical CFLocaleMgrException exception.

There is also a similar formatting function for displaying time values per locale, called LSTimeFormat(). Change the code in the previous technique to use LSTimeFormat() instead of LSDateFormat() to see the time formatted for different locales.

II
Controlling Application Flow
7 8 Application Flow Exception Handling

7
Application Flow
7.0. Introduction
A ColdFusion application is a group of ColdFusion templates associated to perform specific functions. Most ColdFusion Web sites are ColdFusion applications. Anything from a simple form presentation and data-capture mechanism to a full-blown back office intranet system can be considered a ColdFusion application. A ColdFusion application consists of an Application.cfm page and shared variable scopes for the ColdFusion templates associated with the application. Applications allow template access to the session scope, the client scope, and the application scope. This chapter defines how to create and manage an application and also describes the various scopes available within an application.

7.1. Creating a ColdFusion Application


You want to create a ColdFusion application.

Technique
Use the <cfapplication> tag to create a ColdFusion application.
<cfapplication name=myApplication>

Comments
The only requirement to create a ColdFusion application is the presence of the <cfapplication> tag.This tag is usually placed in a file named Application.cfm located in the root directory of your application.The Application.cfm file is useful because if ColdFusion finds this file in the directory or any parent directories up to the

92

Chapter 7

Application Flow

Web root, code inside the file will be executed before any logic in the requested page. For that reason, its typical to set global variables in what is called the application scope so that they will always be available to the templates comprising the application. The <cfapplication> tag allows you to associate ColdFusion templates for the purposes of defining a ColdFusion application.There are no required attributes for the <cfapplication> tag, although there are numerous optional ones.Table 7.1 defines these attributes and describes their purposes.
Table 7.1 cfapplication Optional Attributes Attribute
applicationTimeout clientManagement clientStorage

Description A timespan value that defines the lifetime of variables in the application scope.The default is set in the ColdFusion Administrator. Defines whether the client variable scope exists for the application. Accepted values are Yes and No. Default is No. If Client scope variables are used, this variable defines the storage mechanism for those values. Accepted values are cookie, registry, or the name of any valid ColdFusion data source configured to store client variables. Client storage is discussed in detail later in this chapter. Default is registry. The string name of your ColdFusion application. If you choose to use the application or session variable scopes, this attribute is required. Defines whether the session variable scope exists for the application. Accepted values are Yes and No. Default is No. A timespan value that defines the lifetime of variables in the session scope.The default is set in the ColdFusion Administrator. Defines whether ColdFusion sends cookies to the client machine containing client identifiers for the application. Default is Yes. Sets domain-level client identifiers, used primarily for applications running in a clustered environment. Accepted values are Yes and No. Default is No.

name

sessionManagement sessionTimeout setClientCookies setDomainCookies

When ColdFusion executes any .CFM template, it will look up the directory tree for the presence of an Application.cfm file and should it find one, it will execute the code in that file before processing the requested template.The <cfapplication> tag is usually the first line of code in the Application.cfm file.The <cfapplication> tag is used to define the type of application the requested file belongs to.
Note
Users are not allowed to browse directly to pages named Application.cfm. If they attempt to do so, ColdFusion throws a ReservedTemplateException exception.

7.1.

Creating a ColdFusion Application

93

The applicationTimeout attribute defines the lifetime of variables in the application scope. If the application scope is used, the name attribute of the <cfapplication> tag is required.The value of the applicationTimeout attribute is a valid ColdFusion timespan usually created with a call to the CreateTimeSpan() function. For more on the CreateTimeSpan() function, see Chapter 6, Dates and Times.The following line of code defines a ColdFusion application with an application scope lifetime of 2 days and 12 hours:
<cfapplication name=myApplication applicationTimeout=#CreateTimeSpan(2,12,0,0)#>

You can also omit the applicationTimeout attribute, thereby accepting the default value for the parameter.The default for this value is a server-wide setting controlled by the ColdFusion Administrator.To change this setting, log into your ColdFusion Administrator and browse to the Memory Variables page. Note that from this page you can also disable the use of the application variable scope, as well as define a maximum timeout value. If you need to use the client variable scope, you need to set the value for clientManagement to Yes and also define a storage mechanism for the client variables.You also need to decide on a value for setClientCookies. If set to Yes, setClientCookies instructs ColdFusion to store the client identifier as a cookie on the client machine. Note that users can reject acceptance for cookies, so setClientCookies should be used with caution if your application is not used in a controlled client environment. Storage strategies for client variables are discussed later in the chapter. Likewise, if you need to use the session variable scope, you need to set the value for sessionManagement to Yes.The ColdFusion administrator allows you the option to choose ColdFusion Session Management or J2EE Session Management. If you use ColdFusion Session Management, session variables use the same client identifier as ColdFusion client variables (CFID and CFTOKEN) and are not available to any Java Server Pages (JSPs) or Java servlets you may access inside of your application. If you use J2EE session management, your client identifier is available through a variable named jSessionId and variables inside the session scope are made available to any JSPs you may access inside of your application. Client and session variable scopes effectively store information about a single client inside a single application.The best practice is to use the session variable scope to store single-session specific variables and to use the client variable scope to store values that you need to access over a number of different client sessions. For example, a good place to store shopping cart-related values is in the client scope, because youll likely want the application to recall the contents of a users shopping cart over a number of client sessions.

94

Chapter 7

Application Flow

7.2. Using the Application Variable Scope


You want to initialize application-wide settings and variables before each page request.

Technique
Use <cfset> and the application prefix inside Application.cfm to initialize variables in the application scope.
<cfapplication name=myApplication> <cfset application.ds = myDataSource> <cfset application.dsu = dsUsername> <cfset application.dspw = dsPassword>

Comments
This technique initializes three application scope variables to be executed before each .CFM page request. One common variable set in database-driven Web sites is the name of the primary data source for the application. Using the code in the technique allows developers responsible for individual page development to access the primary database without needing to know the name, username, or password necessary for access.This way, the developer responsible for managing the ColdFusion data source can change the username or password, and even change the name of the DSN without worrying about seeking out each call to the database inside the application and changing the connection information.Without setting the DSN name, username, and password in the application page, each query to the database might look like:
<cfquery datasource=myDSN username=username password=password> Select ... </cfquery>

But if the developer responsible for managing the entire ColdFusion application defines the values for the DSN name, username, and password inside the application scope, developers responsible for individual template development can simply use the application variables for the necessary values, thereby allowing the global change of these values in a single setting, like so:
<cfquery datasource=#Application.ds# username=#Application.dsu# password=#Application.dspw#> Select ... </cfquery>

7.3.

Implementing Session Management

95

7.3. Implementing Session Management


You want to use ColdFusion Session Management in your application.

Technique
Use the session prefix to manage variables in the session scope.
<cfif not isDefined(session.todaysDate)> <cfset session.todaysDate = Now()> </cfif>

Comments
This technique determines whether the session variable named todaysDate exists, and if it does not, creates it and sets it to the value returned from a call to the Now() function. Now() returns a ColdFusion Datetime value equal to the current date and time on the application server. For more information about the Now() function, refer back to Chapter 6. In order to use the session scope you need to first add the sessionManagement attribute to your <cfapplication> tag and set it to Yes. After youve done so, you can manage variables stored in the session scope. If you are using ColdFusion Session Management, an initial page request from a client sets four variables immediately in the session scope for that user: CFID, CFTOKEN, sessionId, and urltoken.
Note
The variables set by ColdFusion in the session scope are writable, just as the variables placed there by a developer are. Changing the variables set by ColdFusion in the session scope can produce unexpected results and is not recommended.

The session.CFID and session.CFTOKEN will be the same value as the client.CFID and client.CFTOKEN if you are also using Client Management.These two variables represent the unique client identifier ColdFusion will use to retrieve client-specific variables in the session and client scopes.The session.sessionId will be a concatenated value of the application name, the CFID, and the CFTOKEN.The urltoken will be a URL-formatted value of the CFID and CFTOKEN. It is a good idea to pass this urltoken between page requests to ensure session integrity.The following code shows how to pass the CFID and CFTOKEN identifiers across URLs:
<cfoutput> <a href=someTemplate.cfm?#session.urltoken#>link</a> </cfoutput>

96

Chapter 7

Application Flow

This code displays as the following link in the HTML sent to the client browser (of course, the values for CFID and CFTOKEN are specific to each application and each user):
<a href=someTemplate.cfm?CFID=1601&CFTOKEN=16915231>link</a>

Passing this client identifier along with page requests allows ColdFusion to reference the individual clients session scope variables. The session scope, like the application scope, is stored in memory on the application server. For high load applications, Client Management with an optimized storage mechanism may be a better idea for storing client specific data.
Note
The session scope should be used for values that you want to exist only for the current session. If you need to set a variable whose lifetime should exist over multiple sessions, use the client scope instead.

7.4. Implementing Client Management


You want to use ColdFusion Client Management in your application.

Technique
Use the client prefix to manage variables in the client scope.
<cfif not isDefined(client.navigationPreference)> <cfset client.navigationPreference = application.defaultNavigationPreference> </cfif>

Comments
This technique determines whether the client variable named navigationPreference exists, and if it does not, creates it and sets it to the value for the application variable defaultNavigationPreference. In order to use the client scope, you first need to add the clientManagement attribute to your <cfapplication> tag and set it to Yes.You can also set the clientStorage value to define the storage mechanism for the client scope (client scope storage is discussed later in this chapter). After youve done so, you can manage variables stored in the client scope. An initial page request from a client immediately initializes six variables in the client scope for that user: CFID, CFTOKEN, hitCount, lastVisit, timeCreated, and urltoken.

7.5.

Storing Client State Using the Registry

97

Note
The variables set by ColdFusion in the client scope are writable, just as the variables placed there by a developer are. Changing the variables set by ColdFusion in the client scope can produce unexpected results and is not recommended.

The client.CFID and client.CFTOKEN will be the same value as the session.CFID and session.CFTOKEN if you are also using Session Management. These two variables represent the unique client identifier that ColdFusion will use to retrieve client-specific variables in the client and session scope. The hitCount value is an integer defining the number of times the client has requested a page within your application.The lastVisit value is a ColdFusion DateTime value equal to the date and time of the last page request within this application from this client.The timeCreated value is a ColdFusion DateTime value equal to the date and time the client store was created for this user in this application.
Tip
The client scope should be used for values that you want to exist across user sessions. If you need to set a variable whose lifetime should exist only for the extent of the current user session, use the session scope instead.

One primary difference between the client scope and the session scope is that client scope can only store simple values, whereas the session scope can include complex values. A simple value is one that can be converted into string, which includes variables of type string, boolean, datetime, and number.Variables such as structures and arrays cannot be stored in the client scope in their native form.You must store structures and arrays (or other complex data types) in the client scope, serialize the complex object to a wddx string, and then store that value instead. For more on WDDX, see Chapter 21, WDDX.

7.5. Storing Client State Using the Registry


You want to store client-state information in the application server registry.

Technique
Set the <cfapplication> attribute clientStorage to registry to store client state data in the application server registry:
<cfapplication name=myApplication clientManagement=yes clientStorage=registry>

98

Chapter 7

Application Flow

Comments
This technique creates a ColdFusion application, initializes the client variable scope, and instructs ColdFusion to use the application server system registry to store client scope information. ColdFusion installs with registry client storage as the default, so unless changes have been made in the ColdFusion administrator, you can omit the clientStorage attribute and accept the registry as the default setting. If your ColdFusion Application Server is on a UNIX machine, registry storage is not an option. Also note that you cannot use the registry storage option in a clustered application environment. Although registry client storage is the default setting, there are many disadvantages. The primary disadvantage is that registry storage does not scale well for many clients. When using registry storage, each client is provided a singular registry entry. ColdFusion stores these entries in the registry locationHKEY_LOCAL_MACHINE\SOFTWARE\ Macromedia\ColdFusion\CurrentVersion\Clients.You will have as many entries in the registry as you have clients who have accessed applications on your application server.

7.6. Storing Client State Using Cookies


You want to store client state information in the application server registry.

Technique
Set the <cfapplication> attribute clientStorage to cookie to store client state data in client cookies.
<cfapplication name=myApplication clientManagement=yes clientStorage=cookie>

Comments
This technique creates a ColdFusion application, initializes the client variable scope, and instructs ColdFusion to use cookies to store client scope information. Cookies are encrypted text files sent to the client machine typically used to store small bits of information, such as client identifiers. Cookies are not human readable and are only accessible by the domain that created them. Storing client scope data in cookies provides the attractive benefit of high scalability, as you arent using any application server resources to store client data. However, there are also a number of disadvantages. One disadvantage for storing state information in cookies is that some browsers limit cookie sizewhich prevents you from storing large amounts of data in the client scope, and ColdFusion throws an exception if you attempt to store too much data in a client variable.

7.7.

Storing Client State Using a Database

99

The primary disadvantage related to using cookies to store client data is that users can simply not accept cookies and can edit or delete cookies from their system. For these two reasons, storing client data in cookies should be an option only if your users are in a controlled environment and cannot edit cookie data, or if you are storing simple, nonessential data in the client scope.

7.7. Storing Client State Using a Database


You want to store client state information in a database.

Technique
Set the <cfapplication> attribute clientStorage to the name of a valid ColdFusion data source configured to store client scope data.
<cfapplication name=myApplication clientManagement=yes clientStorage=myDatasource>

Comments
This technique creates a ColdFusion application, initializes the client variable scope, and then instructs ColdFusion to use the data source named myDatasource to store client scope information. To store client scope information in a database, you must first ensure the data source is configured to accept client state information. If the database type you are using supports creating tables through SQL scripts, you simply need to create a blank database and point the client storage to that database. ColdFusion builds the table schema required to store client scope data for you. If your database does not support SQL table creation, you need to build the tables yourself. ColdFusion requires two tables to store client scope data in a database: CDATA and CGLOBAL.These tables and their fields are described in Tables 7.2 and 7.3, respectively.
Table 7.2 CDATA Table Columns, Data Types, and Descriptions Column Name
cfid app Data

Data Type String of variable length at least 64 characters String of variable length at least 64 characters String of variable length with an indeterminate number of total characters acceptable

Description Stores the CFID and CFTOKEN for the appropriate user in the format CFID:CFTOKEN. Stores the name of the application used to create the client entry. Stores pound-sign delimited client scope key/value combinations created by the application.

100

Chapter 7

Application Flow

Table 7.3 The CGLOBAL Table Column, Data Types, and Descriptions Column Name
cfid

Data Type String of variable length at least 64 characters String of variable length with an indeterminate number of total characters acceptable
DateTime

Description Stores the CFID and CFTOKEN for the appropriate user in the format CFID:CFTOKEN. Stores pound-sign delimited client scope key/value combinations created by ColdFusion. Stores the DateTime value representing the last time this user visited the application.

data

lvisit

Note
ColdFusion does not create indexes on the tables it creates to store client scope information. If you want to create indexes to improve performance, you should index the cfid columns of both tables.

Storing client scope information in databases is more often than not the ideal way to manage client data.The disadvantage, obviously, is that you need access to a database system. The advantages though are tremendousyou have complete control over the storage of the data, the system scales much better than when storing in the registry, the system is much more reliable than when storing in client cookies, and security responsibility is delegated to the security options for your database system.

8
Exception Handling
8.0. Introduction
You just learned about the various ways to control application flow in your ColdFusion application.The CFML programming language allows you to group statements into conditional blocks (<cfif> and <cfswitch>) and iterative blocks (<cfloop>).The use of proper flow-control and code-formatting techniques can make your program easy to understand, even for someone unfamiliar with your code. Traditional error handling in C and BASIC subroutines involved a demon that would obscure the logic of the most elegantly written program. Although essential, handling errors should never be the object of your program. After all, your error handling code wont even be executed if everything goes according to plan, right? Error handling can also clutter an otherwise clean programming API.Traditional functions not only had to return the expected result, they also had to return a myriad of error codes and messages to report if anything went wrong.This often led to confusing semantics, in which the result of the function was not the intended result but was, instead, an error code that your program had to check.The actual result was returned by reference through one of the functions arguments. For example, the StructAppend() CFML function returns a true/false error code rather than a pointer to the changed structure.The actual result is returned by reference via the struct1 argument. Exception handling was invented to clean up and isolate error handling logic. C++ was one of the first programming languages to support this new concept. ColdFusion has long supported exception handling. ColdFusion MX has refined the exception-handling API a bit to provide additional error information to the programmer.This chapter explores exception handling in ColdFusion MX.You will learn about the <cftry>/<cfcatch> tag syntax to trap and handle different types of errors.You will also look at the <cfthrow>/<cfrethrow> tags for raising new exceptions in your program. Finally, you will build some actual examples that demonstrate proper ColdFusion error handling.

102

Chapter 8

Exception Handling

Before diving headlong into exceptions, pause for a moment to look at the way you would handle errors without exception handling (and the way some programmers might be guilty of using).The following pseudocode illustrates cumbersome error handling.
// We will now attempt an operation, such as reading a file <cfset errorcode = doSomething(arg1,arg2,arg3,result)> <cfif errorcode eq error1> // handle the first type of error <cfelseif errorcode eq error2> // handle the second type of error <cfelseif errorcode == error3> // handle the third error ... <cfelseif errorcode == errorN> // handle the nth error </cfif> // actual program logic goes here. <cfoutput>#result#</cfoutput>

This pseudo-routine contains two lines of getting-stuff-done code and many, many more lines of error handling.The error handling code is not the problem as much as its placement in the template. It breaks up the flow of the routine and confuses the algorithm. Notice, also, that the return value of the doSomething() function is an error code rather than something more useful. ColdFusion provides a way to separate the error conditions from the main line algorithm through the use of the <cftry> and <cfcatch> tags. <cftry> creates a block of potentially problematic code, whereas <cfcatch> isolates specific error-handling logic.You can use try and catch to simplify the pseudocode:
<cftry> // Attempt an operation, returning a useful result // rather than an error code. <cfset result = doSomething(arg1,arg2,arg3)> // actual program logic goes here> <cfoutput>#result#</cfoutput> <cfcatch type=error1> // handle the first error type </cfcatch> <cfcatch type=error2> // handle the second error type </cfcatch> ... <cfcatch type=errorn> // handle the nth error type </cfcatch> </cftry>

8.1.

Catching Exceptions

103

This incarnation of the program is much easier on the eyes.The code that you really care about (that is, the programs primary logic) appears, uninterrupted, at the top of the <cftry> block.The error-handling logic takes a back seat to the main algorithm and appears at the end of the <cftry> block.This is as it should be. Exception handling will enhance your programs durability as well as its aesthetics. By handling ColdFusion errors in your code, you save your users from being exposed to the all-too-common An error occurred while processing your request standard-issue error templates.

8.1. Catching Exceptions


You want to anticipate and handle exceptions within your ColdFusion template.

Technique
Use the <cftry> and <cfcatch> tags to anticipate and handle errors in your ColdFusion template.
<cftry> <cffile action=read file=this file doesnt exist variable=thefile> <cfoutput>#thefile#</cfoutput> <cfcatch> The file you requested does not exist. </cfcatch> </cftry>

Comments
ColdFusion exception handling is quite simple to understand and use.There are only two little tags you need to worry about, so there is absolutely no excuse for not using them. Use <cftry> to isolate your mainline logic and <cfcatch> to respond to your errors. ColdFusion will abort processing the <cftry> block the moment an error occurs and will shift control to the appropriate <cfcatch> block.This example attempts to read a non-existent file. Reading a file, like any I/O operation, is prone to failure. In this example, you anticipate a possible error by isolating the logic in question with the <cftry> tag.You use the <cfcatch> tag to handle the error within the application and display a friendly error message to the users.

104

Chapter 8

Exception Handling

8.2. Catching Multiple Exceptions


You want to catch multiple exceptions within your ColdFusion template.

Technique
Use multiple instances of <cfcatch> within a single <cftry> block to handle different types of errors.
<cftry> <cfset thefilename = bad_variable_name&.txt> <cffile action=read file=#thefilename# variable=thefile> <cfoutput>#thefile#</cfoutput> <cfcatch type=expression> <!--- Catch errors in the filename expressions ---> The expression is not valid </cfcatch> <cfcatch type=application> <!--- Catch errors in the <cffile> operation ---> The file you requested does not exist. </cfcatch> <cfcatch type=any> <!--- Catch all other exceptions ---> Some other exception occurred </cfcatch> </cftry>

Comments
What happens if more than just one type of error can occur in the midst of a <cftry> block? ColdFusion allows you to specify multiple <cfcatch> blocks, one for each type of error.The type attribute of the <cfcatch> tag specifies a specific exception type to be caught.Table 8.1 lists the exception types supported by ColdFusion.
Table 8.1 ColdFusion Exception Types Exception Type
Application Database Template Security Object MissingInclude Expression Lock

Description Catches Catches Catches Catches Catches Catches Catches Catches application exceptions. exceptions that occur during database operations. ColdFusion page exceptions. exceptions surrounding user security. exceptions thrown when working with objects. exceptions generated by a missing include template. exceptions thrown by invalid CFML expressions. exceptions thrown during synchronized code segments.

8.3.

Examining an Exception

105

Table 8.1 Continued Exception Type


Custom_Type SearchEngine Any

Description Catches exceptions that are defined by you, the developer. Catches exceptions thrown by the Verity search engine. The default. Catches any type of thrown exception.

This example catches, several types of exceptions. I modified the previous example to use a potentially erroneous expression to determine the filename.You must now catch at least two types of exceptions: expression (to catch an exception in determining the filename) and application (to catch an exception in the file I/O). It also catches expressions of type=any to be safe. Astute readers may notice a potential ambiguity in the examples exception handling code. Expression exceptions can be handled by <cfcatch> blocks with type Expression or Any (likewise, I/O exceptions could be considered Application or Any). Both types of <cfcatch> blocks appear in the code, so where will expression exceptions be handled? ColdFusion will always use the most precise <cfcatch> block available for handling an exception. <cfcatch type=any> is used only as a last resort, regardless of the order in which the different <cfcatch> blocks appear in the code.
Note
In addition to the primary 11 exception types, ColdFusion MX introduced over 50 new advanced exception types for even more fine-grained control of your application. Consult the MX documentation for a full listing of these new exception types.

8.3. Examining an Exception


You want to find out more information about an exception that you have caught.

Technique
Use the ColdFusion cfcatch structure to gather more detail about an exception.
<cftry> <cfset thefilename = bad_variable_name&.txt> <cffile action=read file=#thefilename# variable=thefile> <cfcatch type=any> <!--- Catch all other exceptions ---> <cfoutput> A #cfcatch.type# exception occurred. #cfcatch.message#: #cfcatch.detail#<br>

106

Chapter 8

Exception Handling

<cfdump var=#cfcatch.tagcontext#> </cfoutput> </cfcatch> </cftry>

Comments
Just knowing that an error occurred is often not enough. Usually you want to know what error occurred and why it happened.You can access this information in a <cfcatch> block via the implicit cfcatch variable.This variable is populated with useful information at the start of any <cfcatch> block.Table 8.2 lists the cfcatch structures keys and values.
Table 8.2 The Contents of the cfcatch Variable Key
cfcatch.detail

Value A ColdFusion-supplied detailed message.This message contains HTML formatting and is intended to be displayed in a Web page. Use this variable to help with debugging. A string that represents a specific exception error code.This is used only with exceptions of type=Custom. An internal expression error number that is specific to exceptions of type=Expression. Custom error information that is not normally displayed. This is used with exceptions of type Custom and Application. The name of the affected lock for exceptions of type=Lock.This variable will contain the string anonymous if the locks name is unspecified. The lock operation that failed, causing a type=Lock exception to be thrown.Values may be Timeout, Create Mutex, or unknown. A string representing the exceptions diagnostic message, if available. The name of the missing template in the case of an exception of type=MissingInclude. A database-specific error code that is native to the database driver.This variable is provided for exceptions of type=Database. SQL state information, if provided by the database driver, associated with exceptions of type=database. An array of structures containing the stack trace of all tags that led to the exception, including the tag that threw the exception. The exception type specified in the <cfcatch> block.

cfcatch.ErrorCode cfcatch.ErrNumber cfcatch.ExtendedInfo

cfcatch.LockName

cfcatch.LockOperation

cfcatch.Message cfcatch.MissingFileName cfcatch.NativeErrorCode

cfcatch.SQLState cfcatch.TagContext

cfcatch.Type

8.4.

Throwing Exceptions

107

The example in the previous technique returns you to the familiar I/O example.The code is augmented to output some diagnostic information to help debug the problem. The code includes a debug statement to the <cfcatch> block to output the expressions message, detail, and tagcontext variables. Note that you use the <cfdump> tag to automate the rendering of the contents of the cfcatch.tagcontent variable. This is a good way to become familiar with the values of the cfcatch structure.

8.4. Throwing Exceptions


You want to throw custom exceptions in your application.

Technique
Use the <cfthrow> tag to raise your own exceptions.
<cfthrow type=MyException message=A custom exception occurred. detail=This is an example of a custom ColdFusion exception.>

Comments
ColdFusion gives programmers the capability to create their own types of exceptions that can be thrown and caught just like standard exceptions. Custom exceptions enable your CFML templates to communicate errors that are unique to your application. You can (and should!) use custom exceptions in your application to propagate error conditions.This technique is invaluable when you are writing custom tags or functions. Custom exceptions are the preferred way to notify the calling CFML template that something went horribly wrong. You can roll your own exceptions with the <cfthrow> tag.The tag uses the following syntax:
<cfthrow type=exception_type message=message detail=detailed_description errorCode=error_code extendedInfo=extra_useful_information object=java_exception_object>

The type attribute can be any of the following: Application, Any, or a custom exception string of your choosing.The message attribute should describe the exception.The detail attribute should provide useful documentation to aid in debugging. ColdFusion will display the value of this attribute if the exception is not caught.The errorCode and extendedInfo attributes are left for you to fill out or leave blank. These optional attributes can be used to provide extra information about a user-defined exception.The object attribute allows you to pass in a subclass of the Java Exception object.You must first define this exception by using the <cfobject> tag.

108

Chapter 8

Exception Handling

The previous technique shows an example of throwing a custom exception.The exception is of type MyException and includes a brief message and a more detailed explanation of the error.
Note
Some compilers, such as the Java compiler, will not compile a source file until all possible exceptions are caught or re-thrown in some fashion. Compile-time error checking like this can help strengthen the quality of your code. The ColdFusion compiler does not warn you about the potential for uncaught exceptions. So be vigilant, ColdFusion developers.

Custom exceptions are handled exactly like their standard counterparts. Custom exceptions are an elegant means for your ColdFusion templates to report errors.You should be liberal with your exception types. Many programmers fall into the trap of throwing and catching exceptions at the broadest level.Try to avoid throwing and catching too many exceptions with type=Any. Although doing so may seem expedient at first, you will be robbing your application of some flexibility. An application with many small and focused <cfcatch> blocks is more extensible than an application with fewer, but much larger, <cfcatch type=any> blocks.

8.5. Re-Throwing Exceptions


You want to propagate an exception up to the calling page.

Technique
Use the <cfrethrow> tag to defer the handling of an exception to a template higher up on the call stack.
<cftry> <cfquery...> <cfcatch type=database> <cfif cfcatch.NativeErrorCode eq 100012> <!--- ignore this benign error ---> <cfelse> <!--- uh oh. Better let everyone know ---> <cfrethrow> </cfif> </cfcatch> </cftry>

Comments
What happens if you catch something that you did not mean to catch? Ask any old fisherman this questionjust throw the darned thing back. ColdFusion provides the <cfrethrow> tag to do just that.

8.5.

Re-Throwing Exceptions

109

Sometimes re-throwing an exception is the most appropriate course of action to take. This is usually the case if the template that caught the exception is not completely sure what to do with it. In this circumstance, it is generally preferable to re-throw the exception rather than to make a bad guess. For example, a database query can throw many types of exceptions (distinguished by the SQLState or NativeErrorCode cfcatch variables). All of these exceptions will fall under the broad type, database.Your template is left to decide which types of database exceptions it can handle safely and which are important enough to report to the calling template.The pseudocode fragment shown in the previous technique demonstrates this idea. Note that the <cfrethrow> tag does not require any attributes.This is because the tag operates in the scope of the <cfcatch> block in which it is placed. It merely propagates the current exception, as-is, up the call stack in order for a higher-level template to handle the error.

III
Using Utility Tags
9 10 11 12 13 Charting Data Files and Directories Using Internet Protocols Using Verity User Authentication and Authorization

9
Charting Data
9.0. Introduction
Data can be boring. Although spreadsheets and list views may technically provide all the information necessary to make business decisions, they dont necessarily make for the most visually appealing applications. Determining the best way to visually display information is one of the primary roles of a ColdFusion developer, and ColdFusion provides the creatively inclined many tools to assist in this ever evolving technical art.The <cfchart> tag is one of these tools. There are numerous reasons to include charts in Web applications. Charts are visually appealing; they add color and spice to your data, and quite simply, they add to the user experience.This chapter discusses the charting capabilities provided by ColdFusion MX and how to best use that functionality to obtain your desired results.

9.1. Creating a Simple Chart


You want to create a simple chart.

Technique
Use cfchart, cfchartseries, and cfchartdata to create a simple chart.
<cfchart> <cfchartseries type=scatter> <cfchartdata value=5> <cfchartdata value=8> <cfchartdata value=6> </cfchartseries> </cfchart>

114

Chapter 9

Charting Data

Comments
To create the most basic chart in ColdFusion, you must create a chart object using the <cfchart> tag, you must create a chart series object inside of the chart object using the <cfchartseries> tag, and then you must define the value points for the series. The code in the previous technique provides values only for the required items needed to create a chart. As you can probably imagine, there are numerous optional attributes you can use to further manipulate the display and behavior of a chart. Execute the code in the previous technique and take a look at the generated chart.The next few sections cover how to manage the appearance and behavior of the chart container, the series in the chart, as well as the individual data points.

9.2. Managing the Chart Container


You want to manage the chart container portion of a chart object.

Technique
Provide values for the optional <cfchart> attributes to manage the chart container portion of a chart object.
<cfchart chartHeight = 500 chartWidth = 500 gridlines = 11 scaleTo = 10 tipStyle=mouseDown> <cfchartseries type=scatter> <cfchartdata value=5> <cfchartdata value=8> <cfchartdata value=6> </cfchartseries> </cfchart>

Comments
Rarely will you create a chart in ColdFusion and accept the default values for all the optional attributes in the <cfchart> tag as you did in section 9.1.You will most likely control the display of the chart to complement the design of your specific application. There are numerous optional attributes you can supply to the cfchart tag to control the chart container. Basically, the chart container is responsible for everything in the chart except for the actual series and data points.That means you can provide attributes

9.2.

Managing the Chart Container

115

defining the width and height of the chart container, manage the format and display of the axis values, control display of the legend, and define colors for different sections of the container, among other things. In the previous technique, you defined the height and width of the chart container, defined how many gridlines should appear on the y-axis of the chart, scaled the chart to a specific value on the y-axis, and adjusted the tooltip behavior to appear on mouseDown. These are just a few of the many attributes available for the <cfchart> tag.Table 9.1 lists all available attributes and values and describes their respective behaviors.
Table 9.1 Optional <cfchart> Attributes and Behaviors Attribute
backgroundColor chartHeight chartWidth dataBackgroundColor font fontBold fontItalic fontSize foregroundColor

Optional Values and Behaviors Hexadecimal value or named Web color. Controls the color between the border and the chart data area. Default is white. Desired chart height in pixels. Default is 240. Desired chart width in pixels. Default is 320. Hexadecimal value or named Web color. Controls the color between the border and the chart data area. Default is white. One of four font names: arial, arialUnicodeMS, times, or courier. Default is arial. Defines font bold style. Yes or No. Default is No. Defines font italic style. Yes or No. Default is No. Defines font size as an integer value. Default is 11. Hexadecimal value or named Web color. Controls the color of objects on the foreground such as the gridlines, the axis lines, the axis value markers, and the border. Default is black. Defines the format of the chart output. Accepted values are flash, jpg, and png. Default is flash. Integer number of gridlines to display on the chart value axis. Default is 3. Format to display values on y-axis. Accepted values are currency, date, number, and percent. Default is number. Desired marker size in pixels. Default is determined at runtime. String value for creating a ColdFusion variable from the output of the chart. Primarily used for creating a chart and then writing the chart to a file. Note also providing a value for this attribute causes the chart to not display on the resulting page. For a pie chart, defines whether the pie is sliced or not. Accepted values are sliced and solid. Default is sliced. Creates a horizontal bar chart by rotating the graph axis. Yes or No. Default is No.

format gridlines labelFormat markerSize name

pieSliceStyle rotated

116

Chapter 9

Charting Data

Table 9.1 Continued Attribute


scaleFrom scaleTo seriesPlacement

show3d showBorder showLegend

showMarkers

showXGridlines showYGridlines sortXAxis tipBGColor

tipStyle

url xAxisTitle xOffset yAxisTitle yOffset

Optional Values and Behaviors Integer value for minimum y-axis point. Default is determined at runtime. Integer value for maximum y-axis point. Default is determined at runtime. For charts with more than one series, defines the position of the series combinations. Accepted values are cluster, default, percent, and stacked. Default is default. Determines whether the chart will display as three-dimensional. Yes or No. Default is No. Determines whether a border will be placed around the entire chart container. Yes or No. Default is No. For a chart with more than one series, determines whether a legend is displayed. Yes or No. Default is Yes if there is more than one series. For series with type of curve, line, and scatter, determines whether markers are shown at value points. Yes or No. Default is Yes. Determines whether grid is shown for x-axis. Yes or No. Default is No. Determines whether grid is shown for y-axis. Yes or No. Default is Yes. Alphabetizes values along the x-axis. Yes or No. Default is No. Hexadecimal value or named Web color. Controls the background color of the value points in the chart. Note this applies only to flash format charts. Default is white. Defines which action causes the tip to show for a value point. Accepted values are mouseDown, mouseOver, and Off. If chart format is flash, mouseDown requires a mouse left-click to show the tip. If not flash, the mouseDown behavior is the same as mouseOver. mouseOver shows the tip on a mouse fly-over. Off turns off the tip functionality. Default is mouseOver. Defines the URL to jump to when a value point is clicked. No default. String value to display as a title for the x-axis. No default. For 3D charts, any number between 1 and 1. Defines the angle to use in the third dimension for the x-axis. Default is .1. String value to display as a title for the y-axis. No default. For 3D charts, any number between 1 and 1. Defines the angle to use in the third dimension for the y-axis. Default is .1.

9.3.

Managing Chart Series

117

9.3. Managing Chart Series


You want to manage the series objects inside a chart object.

Technique
Provide values for <cfchartseries> attributes to manage the series inside a chart object.
<cfchart> <cfchartseries type = area paintStyle = shade> <cfchartdata value = 5> <cfchartdata value = 8> <cfchartdata value = 6> <cfchartdata value = 6> <cfchartdata value = 5> </cfchartseries> <cfchartseries type = line markerstyle = diamond> <cfchartdata value = 12> <cfchartdata value = 10> <cfchartdata value = 8> <cfchartdata value = 10> <cfchartdata value = 9> </cfchartseries> </cfchart>

Comments
Section 9.2 defined how to control the display and behavior of a chart container. A chart container is useless without a series of data to actually populate the chart.You manage the display of the series object using the <cfchartseries> tag.You use this tag to define the type of series to display the style and color of the markers among other attributes. Table 9.2 lists all available <cfchartseries> attributes and values and describes their respective behaviors. A number of these optional attributes involve using a query object as data for populating the chart. Using queries to create graphs is discussed further in the next section.

118

Chapter 9

Charting Data

Table 9.2 Optional <cfchartseries> Attributes and Behaviors Attribute


colorList

Optional Values and Behaviors Hexadecimal value or named Web color. Used only for chart series with type of Pie. Controls the colors displayed for each value in the chart. Default is determined at runtime. Defines which column of a query object represents the item along the xaxis. Determines the display style of value points when chart is of type curve, line, and scatter. Accepted values are circle, diamond, letter, mcross, rectangle, rcross, snow, and triangle. Default is rectangle. Determines the display style of a filled data series. Accepted values are light, plain, raise, and shade. The name of the ColdFusion query object containing the itemColumn and valueColumn to populate the data series. Hexadecimal value or named Web color. Controls the color of the series object. String name of the series; appears in the tip displayed for each value point and in the legend if more than one series is defined. One of the 10 supported ColdFusion chart types: area, bar, cone, curve, cylinder, line, pie, pyramid, scatter, or step. Defines which column of a query object represents the item along the yaxis.

itemColumn markerStyle

paintStyle query seriesColor seriesLabel type valueColumn

Charts with multiple series are also useful tools.You can create multiple series of data within a chart by using more than one <cfchartseries> tag. It is up to the chart designer, of course, to determine the best way to present the data within the chartwith a number of tools at your fingertips, the <cfchartseries> helps you create the most appealing, useful chart for your application. The previous technique created a chart with two series.The first series is an area type series with shaded fill color.The second series is a line series with diamond value points. You can add as many series to your chart as you see fit but be careful not to clutter the information so much that it becomes difficult to understand. The only required attribute for <cfchartseries> is the type attribute.You can define the type of series as one of the following: area, bar, cone, curve, cylinder, line, pie, pyramid, step, and scatter. Figures 9.1 through 9.3 display the many different types of charts, using the code in Listing 9.1. Note that if the cfchart attribute show3d was not set to yes that the bar and cylinder chart types would look identical, as would the cone and pyramid types.

9.3.

Managing Chart Series

119

Listing 9.1 The 10 Supported CFMX Chart Types


<cfset chartList = area,bar,cone,curve,cylinder,line,pie,pyramid,step,scatter> <cfloop list=#chartList# index=i> <cfchart show3d = yes> <cfchartseries type = #i#> <cfchartdata value = 5> <cfchartdata value = 8> <cfchartdata value = 6> </cfchartseries> </cfchart> &nbsp;&nbsp;

</cfloop>

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

Figure 9.1 Chart types: area, bar, cone, and curve.

120

Chapter 9

Charting Data

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

8 7.111 6.222

0 5(26%) 1 8(42%) 2 6(32%)

5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

Figure 9.2 Chart types: cylinder, line, pie, and pyramid.


8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2 8 7.111 6.222 5.333 4.444 3.556 2.667 1.778 0.889 0 0 1 2

Figure 9.3 Chart types: step and scatter.

9.5.

Populating Charts with Query Values

121

9.4. Populating a Chart Series with Explicit Values


You want to define data points inside a chart.

Technique
Use the <cfchartdata> tag to explicitly define data points within a chart series.
<cfchart> <cfchartseries type = bar> <cfchartdata item = January value = 6> <cfchartdata item = February value = 8> <cfchartdata item = March value = 5> </cfchartseries> </cfchart>

Comments
The <cfchartdata> tag is used to explicitly define data points to populate a chart series. <cfchartdata> accepts one required and one optional attribute.The required attribute is value and it must be a simple value.The value represents the y-axis.The optional attribute is item. Item represents the label for the x-axis.
Note
The item attribute of the <cfchartdata> tag must contain a simple value, because ColdFusion must be able to convert the value to a numeric. Simple values include variables of the following types: boolean, date/time, string, and numbers.

9.5. Populating Charts with Query Values


You want to use a query object to define the data points inside a chart.

Technique
To populate a chart with query data, use the appropriate cfchartseries optional attributes to define the name of the query used, the column representing the x-axis, and the column representing the y-axis.
<cfchart> <cfchartseries type = bar query = myQuery itemColumn = itemColumn valuecolumn = valueColumn

122

Chapter 9

Charting Data

/> </cfchart>

Comments
Creating graphs from ColdFusion query objects is a powerful feature of cfchart.(For more information on ColdFusion query objects, see Chapter 5, Queries.) To take advantage of this feature, you need to provide a valid query object name as the value for the query attribute of the <cfchartseries> tag.You also need to define which column of the query contains data for the x-axis (or valueColumn) and which column contains data for the y-axis (or itemColumn).

9.6 Implementing Click-Through Behaviors in Charts


Youc want to allow the users to click a specific value in the chart and execute a URL.

Technique
Use the url attribute of the <cfchart> tag to define the target URL on a click event.
<cfchart url=chart.cfm> <cfchartseries type = bar> <cfchartdata item = January value = 6> <cfchartdata item = February value = 8> <cfchartdata item = March value = 5> </cfchartseries> </cfchart>

Comments
The url attribute of the <cfchart> tag enables you to click through charts and drill down into more specific data or jump to another URL altogether. Note however that the same URL is used for the entire chart. ColdFusion does provide a variable scope for the developer to capture which series, value, and label the user clicked.These variables appear syntactically slightly different than other ColdFusion variable scopes.They are named SeriesLabel, ItemLabel, and Value for the clicked series, item, and value, respectively. Listing 9.2 adds to the technique code to pass through these values. Notice the dollar signs ($) encasing the variable names in the url attribute.
Listing 9.2 Chart Drill Down with the url Attribute
<cfchart url=chart.cfm?item=$itemLabel$&value=$value$&series=$seriesLabel$> <cfchartseries type = bar seriesLabel = Items by Month> <cfchartdata item = January value = 6> <cfchartdata item = February value = 8>

9.7

Administering Charts Through the ColdFusion Administrator

123

Listing 9.2 Continued


<cfchartdata item = March value = 5> </cfchartseries> </cfchart>

9.7 Administering Charts Through the ColdFusion Administrator


You want to manage chart behavior through the ColdFusion Administrator.

Technique
Open ColdFusion Administrator (/cfide/Administrator.index.cfm), log in, and click Charting from the left navigation bar to administer chart behavior.

Comments
The ColdFusion MX administrator panel provides a management area for chart-specific settings.Through the administrator you can manage where charts are stored, how many charts to cache, the maximum number of charting threads to run at a given time, and the location to store cached charts. You can choose to have charts cached in memory or on disk, the default being on disk.To serve cached charts faster you can choose to have them stored in memory. If you store them in memory they will serve up faster, although you are reserving a block of memory in which to store them. If you manage a memory-intensive Cold Fusion application, it is better to store the charts on disk. You can also define how many charts to store in the cache.The default is 50.This means that once the 51st chart is requested, the oldest is removed from the cache and the new request takes its place. By default, ColdFusion will allow four charting threads to run at a given time.You can set this number anywhere between 1 and 5.The higher this number, the more stress placed on your application (although only at times when multiple charts are requested simultaneously). If you choose to store the chart cache on disk, which is the default setting, ColdFusion needs a block of space to store the images or Flash files. By default, the cache directory is [install drive letter]:\CFusionMX\charting\cache. If necessary, you can change this directory to any location. As every application is different, there is no global optimal setting for administering charts. However, if you begin to experience slow response in your charting applications, discuss these issues with your ColdFusion MX System Administrator to determine the best charting settings for your application.

10
Files and Directories
10.0. Introduction
Working with files and directories is a powerful function of ColdFusion MX. Using fileand directory-management tags, you can upload files from a client machine, traverse the file system on the application server, and manipulate files and directories on the application server. In this chapter, you learn how to use the <cffile> and <cfdirectory> tags to manipulate your file system.

10.1. Reading Files


You want to read a file from the application servers file system.

Technique
Use the <cffile> tag to read the contents of a text file on the application servers file system.
<cffile action=read variable=filecontents file=c:\documents\myFile.txt>

Comments
ColdFusion provides you the capability to read files from the application server file system through the action=read setting on a <cffile> tag.There are three required attributes and one optional attribute for the read action of a cffile.These attributes are explained in detail in Table 10.1.

126

Chapter 10

Files and Directories

Table 10.1 Read Action cffile Attributes Attribute


action variable file charset

Description Required. Must be set to read for a read action. Required. Defines the name of a variable to contain the contents of the file. Required. Defines the path to the file on the application server file system. Optional. Defines the character set of the file contents. Default is UTF-8.

Once you execute the cffile call, the contents of the file are available by referencing the variable name specified by the variable attribute.To display the contents of the file, simply wrap <cfoutput> tags and pound signs around the variable name in your template, like so:
<cfoutput>#filecontents#</cfoutput>

ColdFusion also provides the capability to read binary files through the readBinary value of the action attribute.This populates the variable name defined in the variable attribute with the actual binary data stored in the requested file. If you need to read a binary file from the file system and then transfer that data through a form or need to write the contents to another file, you must use the ToBase64() function to convert the returned binary data into its Base64 equivalent.

10.2. Writing Files


You want to create a file on the application servers file system.

Technique
Use the <cffile> tag to write a file to the application servers file system.
<cfset theContent = Content to place in a new file> <cffile action = write file = c:\documents\testfile.txt output = #theContent# attributes = readOnly addNewLine = No>

Comments
ColdFusion provides you the capability to write files to the application server file system through the action=write setting on the <cffile> tag. When using <cffile> with the action attribute set to write, there are three required and four optional parameters.These parameters are described in detail in Table 10.2.

10.3.

Appending to Files

127

Table 10.2 Write Action cffile Attributes Attribute


action file

Description Required. Must be set to write for a write action. Required. Defines the absolute path for the new file, including the new filename.Windows uses backslashes in the absolute path; UNIX systems use forward slashes.r a write action. Required. Contains the content to place inside the new file. Optional. Accepted values are Yes and No. If Yes, a new line is appended to the end of the content contained in the output attribute. If No, no new line is appended. Default is Yes. Optional list of attributes to set on the new file. Optional. Defines the character set used to create the new file. Default is UTF-8. Optional. Defines the permission set to UNIX files.

output addNewLine

attributes charset mode

Note that if the file defined in the file attribute of a write action <cffile> call already exists (and if the permissions are set to allow an overwrite on the file system), the content provided in the output attribute will overwrite the content in the existing file. If you want to append to a file instead of overwriting it, see the technique provided in the following section.

10.3. Appending to Files


You want to append content to an existing file on the application servers file system.

Technique
Use the <cffile> tag to append content to an existing file on the application servers file system.
<cfset theContent = New content to place in a new file> <cffile action = append file = c:\documents\testfile.txt output = #theContent# addNewLine = No>

Comments
ColdFusion provides you the capability to append to existing files on the application server file system through the action=append setting on the <cffile> tag.

128

Chapter 10

Files and Directories

When using <cffile> with the action attribute set to append, there are three required and four optional parameters.These parameters are described in detail in Table 10.3.
Table 10.3 Append Action cffile Attributes Attribute
action file

Description Required. Must be set to append for an append action. Required. Defines the absolute path for the existing file including the new filename.Windows uses backslashes in the absolute path; UNIX systems use forward slashes. Required. Contains the content to append to the existing file. Optional. Accepted values are Yes and No. If Yes, a new line is appended to the end of the content contained in the output attribute. If No, no new line is appended. Default is Yes. Optional list of attributes to set on the new file. Optional. Defines the character set used to create the new file. Default is UTF-8. Optional. Defines the permission set to UNIX files.

output addNewLine

attributes charset mode

Note that if the file defined in the file attribute does not already exist, the append action will behave exactly like a write action. It will write a new file to the file system without throwing an exception. If you only want to append content to an existing file, you first need to use the FileExists() function to determine whether the file does indeed exist.The following code provides an example of using FileExists() in conjunction with an append action <cffile> call:
<cfset myPath = c:\documents\myfile.txt> <cfif FileExists(myPath)> <cfset theContent = New content to place in a new file> <cffile action = append file = #myPath# output = #theContent# addNewLine = No> <cfoutput>New content added to file #myPath#</cfoutput> <cfelse> <cfoutput>File #myPath# doesnt exist!</cfoutput> </cfif>

In the previous code, the FileExists() function determines whether the file defined by the myPath variable already exists. If it does exist, content is appended to the file and a message is displayed confirming the action. If the file doesnt exist, a message is displayed stating that the file doesnt yet exist.

10.4.

Uploading Files

129

10.4. Uploading Files


You want to upload files from a client machine to the application servers file system.

Technique
Use the <cffile> tag to upload files from a client machine to the application servers file system.
<cfif isDefined(submitted)> <cffile action=upload filefield=myFile destination=c:\documents\ nameConflict=makeUnique> <cfset success = true> </cfif> <html> <body> <form name=myForm action=./fileUpload.cfm method=POST enctype=multipart/form-data> <cfif isDefined(success)> File Uploaded!<br> </cfif> Please browse for the file to upload and click submit<br> <input type=file name=myFile><br> <input type=hidden name=submitted> <input type=submit> </form> </body> </html>

Comments
ColdFusion provides you the capability to upload files to the application server file system through the action=upload setting on the <cffile> tag. When using <cffile> with the action attribute set to upload, there are three required and four optional parameters.These parameters are described in detail in Table 10.4.

130

Chapter 10

Files and Directories

Table 10.4 Upload Action cffile Attributes Attribute


action destination

Description Required. Must be set to upload for an upload action. Required. Defines the absolute path destination for the uploaded file, excluding the filename.Windows uses backslashes in the absolute path; UNIX systems use forward slashes. Required. Defines the name of the form input that contains the file to be uploaded. Optional. Defines a comma-delimited list of accepted MIME types for upload. Optional list of attributes to set on the new file. Optional. Defines the permission set to UNIX files. Optional. Instructs ColdFusion what to do in case of a naming conflict. Accepted values are Error, MakeUnique, Skip, and Overwrite.

filefield accept attributes mode nameConflict

Note
The value of the filefield attribute is simply the name of the field containing the path of the file to be uploaded. Do not wrap the name of this field in pound signs as doing so will dereference the variable. This is a common mistake when uploading files with <cffile>.

The upload action of <cffile> can only be used in conjunction with a form containing a file to be uploaded. In the previous technique, the user is first presented with a form prompting for a file to upload. After the user clicks submit, ColdFusion uploads the file using the <cffile> tag and presents the user with a message stating that the file was uploaded. There are two requirements of the form tag necessary to create a form capable of uploading a file to the server. First, the method argument of the form tag must be set to POST. A form without the method argument defaults to a method of GETwhich does not permit sending multipart data as a GET means that form data is posted through the URL. Secondly, the form must have an enctype argument set to multipart/ form-data.Without these two arguments set correctly, your form will simply post the client path of the file attempting upload, not the file itself. Note that if you use ColdFusion <cfform> tags instead of HTML <form> tags, the method defaults to POST instead of GET, although the enctype does not default to multipart/form-data. It is always a good idea to use the <cffile> accept parameter when uploading files. Using this parameter specifies which file types your form will accept. If the user attempts to upload a file which has a MIME type not found in the accept parameter, ColdFusion will throw an InvalidUploadTypeException exception.Without the presence of the accept parameter, malicious users are permitted to upload whatever they choose.

10.4.

Uploading Files

131

The nameConflict attribute is also useful, especially in high traffic situations. If two users attempt to upload a file with the same name, you can control the behavior of the files with the nameConflict attribute. Accepted values for the nameConflict attribute are Error, MakeUnique, Skip, and Overwrite. Specifying Error instructs ColdFusion not to save the file and to throw a FileOverwriteException exception. Specifying MakeUnique instructs ColdFusion to save the file but to provide it a new name (the new name of the file is stored in the cffile.serverFile variable, discussed later in this section). Specifying Skip instructs ColdFusion not to save the file but to continue processing the rest of the page. Specifying Overwrite instructs ColdFusion to overwrite the existing file with the new one. When you execute the upload action on cffile, ColdFusion creates a special readonly structure named cffile which provides the developer detailed information about the upload attempt.The complete contents of the cffile structure are detailed in Table 10.5.
Table 10.5 cffile Keys and Descriptions Key Name
attemptedServerFile clientDirectory clientFile clientFileExt clientFileName contentSubType contentType fileExisted fileSize fileWasAppended fileWasOverwritten fileWasRenamed fileWasSaved oldFileSize

Description The name of the file ColdFusion attempted to save. The absolute path on the client machine of the file ColdFusion attempted to upload. The name of the file ColdFusion uploaded from the client machine, including the extension. The extension (without the .) of the file ColdFusion attempted to upload. The name of the file ColdFusion uploaded from the client machine, excluding the extension. The MIME subtype of the file ColdFusion saved. The MIME content type of the file ColdFusion saved. Boolean value defining whether a file with the same path and name already exists on the application server file system. The size of the file ColdFusion saved in kilobytes. Boolean defining whether ColdFusion appended the uploaded file to another file on the application server file system. Boolean defining whether ColdFusion overwrote an existing file with the new file. Boolean defining whether ColdFusion renamed the file before saving it on the application server file system. Boolean defining whether ColdFusion saved the file on the application server file system. The size of the file ColdFusion overwrote to save the new file if an overwrite was necessary.

132

Chapter 10

Files and Directories

Table 10.5 Continued


serverDirectory serverFile clientFileExt clientFileName timeCreated timeLastModified

The absolute path of the directory used to save the file on the application server file system. The name of the file ColdFusion saved on the application server file system, including the extension. The extension (without the .) of the file ColdFusion saved on the application server file system including the extension. The name of the file ColdFusion saved on the application server file system, including the extension. Date stamp defining the time the file was saved on the application server file system. Date stamp defining the time the file was last modified on the application server file system.

As with all structures, a useful way to display the keys and values returned with the cffile collection is with the <cfdump> tag. For more information on how to use <cfdump> with structures, and more on structures in general, see Chapter 4, Structures. The structure returned from the upload action on a cffile execution has the base name cffile. So to reference the directory path ColdFusion used to save an uploaded file, you simply use the following code after the cffile execution:
<cfoutput> The file was saved to: #cffile.serverDirectory# </cfoutput>

10.5. Manipulating Files


You want to move, rename, copy, or delete a file on the application servers file system.

Technique
Use the <cffile> tag to move, rename, copy, or delete an existing file on the application server file system.
<cfset myPath = c:\documents\myfile.txt> <cfset myNewPath = c:\documents\new\> <cfif FileExists(myPath)> <cffile action = move source = #myPath# destination = #myNewPath#>

10.5.

Manipulating Files

133

<cfoutput>File #myPath# moved to #myNewPath#</cfoutput> <cfelse> <cfoutput>File #myPath# doesnt exist!</cfoutput> </cfif>

Comments
ColdFusion provides you the capability to manage existing files on the application server file system through four action paramenters of the action attribute of the cffile tag: move, rename, copy, and delete. The code in this technique provides an example of moving a file from one location (the source) to a new location (the destination).When using <cffile> with the action attribute set to move, there are three required and four optional parameters. These parameters are described in detail in Table 10.6.
Table 10.6 Move Action cffile Attributes Attribute
action destination

Description Required. Must be set to move for a move action. Required. Defines the absolute path destination for the moved file. Windows uses backslashes in the absolute path; UNIX systems use forward slashes. Required. Defines the absolute path of the file you wish to move, including the filename.Windows uses backslashes in the absolute path; UNIX systems use forward slashes. Optional. List of attributes to set on the new file. Optional. Defines the character set used to create the new file. Default is UTF-8. Optional. Defines the permission set to UNIX files.

source

attributes charset mode

ColdFusion also provides you the capability to rename files on the application server file system through the action=rename setting on the <cffile> tag, as shown in the following code:
<cfset myPath = c:\documents\myfile.txt> <cfset myNewPath = c:\documents\myfileRenamed.txt> <cfif FileExists(myPath)> <cffile action = rename source = #myPath# destination = #myNewPath#> <cfoutput>File #myPath# renamed to #myNewPath#</cfoutput> <cfelse> <cfoutput>File #myPath# doesnt exist!</cfoutput> </cfif>

134

Chapter 10

Files and Directories

The available attributes for renaming a file are identical to those for moving a file, except there is no charset attribute. Also remember that the destination must contain the filename for the renamed file in addition to the absolute path. You can copy files on the application server file system through the action=copy setting on the <cffile> tag, as shown in the following code:
<cfset myPath = c:\documents\myfile.txt> <cfset myNewPath = c:\documents\myfileCopied.txt> <cfif FileExists(myPath)> <cffile action = copy source = #myPath# destination = #myNewPath#> <cfoutput>File #myPath# copied to #myNewPath#</cfoutput> <cfelse> <cfoutput>File #myPath# doesnt exist!</cfoutput> </cfif>

The available attributes for copying a file are identical to those for renaming a file except that the destination attribute can optionally supply a filename. You can delete a file on the application server file system through the action=delete setting on the <cffile> tag, as shown in the following code:
<cfset myPath = c:\documents\myfile.txt> <cfif FileExists(myPath)> <cffile action = delete file = #myPath#> <cfoutput>File #myPath# deleted</cfoutput> <cfelse> <cfoutput>File #myPath# doesnt exist!</cfoutput> </cfif>

The action=delete setting on the <cffile> tag accepts only one attribute in addition to action; file should contain the full absolute path of the file you want to delete.

10.6. Listing Directories


You want to discover the contents of a directory on the application server file system.

Technique
Use the <cfdirectory> tag to inspect the contents of a directory on the application server file system.
<cfdirectory directory=c:\ name=getDirectory>

10.6.

Listing Directories

135

Comments
ColdFusion provides you the capability to inspect the contents of a directory on the application server file system through the action=list setting on the <cfdirectory> tag. When using <cfdirectory> with the action attribute set to list, there are two required and three optional parameters.These parameters are described in detail in Table 10.7.
Table 10.7 List Action cfdirectory Attributes Attribute
action directory filter name sort

Description Optional because the default is list, this parameter must be left as the default or explicitly set to list for a list action. Required.The absolute path of the directory whose contents you want to list. Optional. File extension restriction for list of returned items. Required. ColdFusion variable name for returned query object. Optional. Comma-separated list of sorted columns and sort directions (asc or desc) to place on the returned query object. Example: columnName ASC

Executing the list action with <cfdirectory> returns a query object containing six columns: attributes, dateLastModified, mode, name, size, and type. Attributes contains the file attributes, dateLastModified contains the date string for the last modification of the item, mode contains the UNIX mode for the item (mode returns empty on Windows systems), name contains the name of the item, size contains the size of the item (directories return 0), and type returns dir for directories or file for files. With the <cfdirectory> list action, it is simple to create a ColdFusion template that will return to the user a page displaying the contents of a directory. An example of this functionality is shown in the following code.
<cfparam name=directory default=c:\> <cfdirectory directory=#directory# name=getDirectory sort=type, name> <cfoutput query=getDirectory> <cfif type eq dir> <cfset path = directory & name & \> <a href=./directory.cfm?directory=#path#>#name#</a> <cfelse> #name# </cfif> <p> </cfoutput>

136

Chapter 10

Files and Directories

This example code provides a simple page allowing the user to traverse the application servers file system starting at the c:\ drive. If the type value for an item is dir, the code builds a link to expand that directory. If the type value is file, it simply displays the name of the file. Save the previous code as directory.cfm on your machine and try it for yourself.

10.7. Manipulating Directories


You want to create, rename, or remove a directory on the application server file system.

Technique
Use the <cfdirectory> tag to manipulate a directory on the application server file system.
<cfdirectory action=create directory=c:\newdirectory>

Comments
ColdFusion provides you the capability to manipulate a directory on the application server file system through the create, rename, and delete actions on the <cfdirectory> tag. The code in the previous technique creates a new directory at the path c:\ newdirectory. Save this code to your machine and execute it. Now rename the directory to newdirectoryTest using the following code:
<cfdirectory action=rename directory=c:\newdirectory newDirectory=c:\newdirectoryTest>

The preceding code renames the directory c:\newdirectory to c:\newdirectoryTest. Save this code to your machine and execute it. Now delete that directory with the following code:
<cfdirectory action=delete directory=c:\newdirectoryTest>

The preceding code deleted the directory located at c:\newdirecoryTest. Note that only empty directories can be deleted. If a directory contains files or other directories, and you attempt delete the directory using <cfdirectory>, ColdFusion will throw a NonEmptyDirectoryCannotBeDeletedException exception.

11
Using Internet Protocols
11.0. Introduction
ColdFusion is an effective mediator for marshalling data in and out of different data repositories. Its communications API is as broad as it is practical and easy to use. ColdFusions Internet Protocol API rounds out its I/O library. A mere seven tags allow you to leverage the most popular Internet protocols to enhance the capabilities of your ColdFusion application. In this chapter, you learn how to issue HTTP requests, send and receive email, query LDAP directories, and communicate with FTP servers.

11.1. HTTP Requests


You want to issue a server-to-server HTTP request.

Technique
Use the <cfhttp> tag to issue a simple HTTP request and inspect the results.
<cfset destination = http://www.macromedia.com> <cfhttp url=#destination#> </cfhttp> <cfdump var=#cfhttp#>

Comments
This technique involves using the <cfhttp> tag to connect to a URL. It then prints out the result for the users to see. The <cfhttp> tag requires but a single attribute: url (see Table 11.1 for a complete listing of the allowed attributes for <cfhttp>).The tag connects to the URL, retrieves the results from the server, stores them in a ColdFusion structure, and returns the results.

138

Chapter 11

Using Internet Protocols

ColdFusion stores these results in a structure named cfhttp.This structure will contain the output of the HTTP request in its filecontent property. A number of other tidbits of information are available to you as well (see Table 11.2 for a complete listing of the cfhttp structure), such as the MIME type, the status code, and the full response header sent by the Web server. The current example uses the <cfdump> tag to display the contents of the cfhttp structure to the users.This is an effective way to examine the breadth of information and metadata that ColdFusion provides to you about an HTTP request.
Table 11.1 <cfhttp> Syntax Attribute
Url

Value Required.The absolute URL of the document to be retrieved.The URL must include the protocol (http or https) and the host name. It may or may not include the port. Default: http. Optional.The port number that should be used when issuing the HTTP request. Any port specified in the URL string (for example, http://hostname:port/) takes priority over this argument. Default: 80. Optional.The method to use when issuing the request. Get can be used to retrieve any type of document. Post should be used when posting form data to a CGI program.The <cfhttpparam> tag must be used with the Post option. Default: get. Optional.The username to be used when requesting a secured document. Optional.The password to be used when requesting a secured document. Optional.The name to be assigned to a query constructed out of the results of a call to <cfhttp>. Optional. Used when creating a query out of a text document downloaded using the Get option.This attribute specifies the column names to use for the different fields. Leave this option blank if the first row in the text document defines the column names. Optional. If the columns attribute is unspecified, the first row of data in the text file will be used to determine the column names for the query. Optional.The path to a directory in which to store the result of the HTTP request. A cfhttp.filecontent variable is created if this attribute is not specified. Optional (required if Path and Post options are specified).The filename to use when saving the result of the HTTP request.You must specify this variable when using the Post method option. Optional.The delimiter to use when creating a query out of the result document. Default: comma.

Port

Method

Username Password Name Columns

FirstRowAsHeaders

Path

File

Delimiter

11.1.

HTTP Requests

139

Table 11.1 Continued Attribute


TextQualifier ResolveURL

ProxyServer ProxyPort UserAgent ThrowOnError

Redirect

Timeout

Charset

Value Optional.The text qualifier (if any) to be used when parsing the result document to create a query. Default: . Optional.When this option is set to Yes, ColdFusion will resolve all URLs in the result document so that the links remain intact. Specifically, ColdFusion will insert the protocol, host name, and port into all embedded references. Default: No. Optional. Host name or IP address of a proxy server (if any) controlling outbound HTTP requests. Optional.The port to be used when issuing an HTTP request to a proxy server. Default: 80. Optional.The user agent request header to be sent to the Web server. Optional.This specifies whether ColdFusion should throw an error if the document cannot be retrieved. If set to Yes, <cftry> and <cfcatch> should be used to handle the error. Default: No. Optional.This specifies whether ColdFusion should follow a redirect request from the Web server. If set to No, you can use the cfhttp.responseHeader.LOCATION variable to determine whether a redirect request was issued. Default: Yes. Optional.The amount of time, in seconds, the ColdFusion server should wait for an HTTP request to return. ColdFusion will use the lesser of this attribute and the Administrator timeout. If neither are specified, ColdFusion will wait indefinitely for the request to terminate. Optional. A Java character set for the file or URL in an HTTP request. Default: UTF-8.

Table 11.2 Key

cfhttp Response Variable Value The content of the HTTP response, for text and MME files only. The MIME type of the response (for example, text/html). A CFML data structure representing the response headers returned from the Web server. Most often, this variable will be a structure with multiple keys (one key per header variable). The raw response header text. The HTTP error code and message string.

cfhttp.filecontent cfhttp.mimeType cfhttp.responseHeader

cfhttp.header cfhttp.statuscode

140

Chapter 11

Using Internet Protocols

11.2. Sending Email


You want your ColdFusion page to send an email message to one or more people.

Technique
Use the <cfmail> tag to send a text email.
<cfparam name=emailaddress default=somebody@somedomain.com> <cfmail to=#emailaddress# from=ColdFusionCookbook@localhost.com subject=Test email message MIMEAttach=#GetCurrentTemplatePath()#> The attached document contains the source code for this ColdFusion MX template. </cfmail>

Comments
In this example, the ColdFusion template checks for a submitted email address and uses the <cfmail> tag to send a one-line email message back to the users. This example requires that a valid SMTP mail server has been configured in the ColdFusion administrator and ColdFusion will throw an exception if no such server is present. However, you can use the optional server attribute to specify a mail server at runtime. The required <cfmail> attributes are to, the recipients email address; from, the senders email address; and subject, the subject of the message.The <cfmail> tag also accepts many optional (but useful) attributes, including cc and bcc, a list of addresses to carbon copy or blind carbon copy the message; type, which can specify text or HTML content; and MIMEAttach, a server-based path to a file that should be attached to the email.

11.3. Receiving Email


You want your ColdFusion page to check for email on a mail server.

Technique
Use the <cfpop> tag to connect to a POP server and download email messages.
<cfparam name=popserver default=mail.mydomain.com> <cfparam name=username default=myusername> <cfparam name=password default=mypassword>

11.4.

Querying an LDAP Directory

141

<cfpop server=#popserver# username=#username# password=#password# action=getAll name=emailquery maxrows=10> <cfdump var=#emailquery#>

Comments
This example gathers the minimum amount of information from the users in order to connect to a POP server and download some messages.The template expects the following three variables: popserver, username, and password.The popserver should be the host name or IP address of an available POP server on which the user has an account.The username and password variables should, naturally, contain the username and password for the users account. The ColdFusion template uses the <cfpop> tag to pull 10 messages (specified by the maxrows attribute) off of the POP server.The action attribute accepts three possible values: getHeaderOnly, getAll, and delete.The getHeaderOnly option will force the tag to return only the email headers.This will take less time and is the best option to use when displaying a summary listing of email messages. The getAll option causes the entire email message body and any attachments (in addition to the header) to be returned to the calling template.This option is best used to display a detailed view for a smaller number of messages. The delete action will delete one or more messages from the POP server.When using this action, you must also set the messageNumber attribute.This attribute contains one or more message IDs to be deleted.You should note that to delete a message, you must make two calls to <cfpop>: one call to retrieve the headers and message IDs and a second to actually delete the messages.

11.4. Querying an LDAP Directory


You want to query an LDAP directory for use in your page.

Technique
Use the <cfldap> tag to query an LDAP directory.
<cfparam <cfparam <cfparam <cfparam <cfparam name=ldapserver default=ldap.mydomain.com> name=username default=myusername> name=password default=mypassword> name=filter default=> name=attributes default=>

142

Chapter 11

Using Internet Protocols

<cfparam name=start default=> <cfldap server=#ldapserver# action=query username=#username# password=#password# start=#start# filter=#filter# attributes=#attributes# name=ldapquery> <cfdump var=#ldapquery#>

Comments
The Lightweight Directory Access Protocol (LDAP) was invented in 1995 at the University of Michigan.The protocol was designed to provide access to online directories over TCP/IP. LDAP was a lightweight replacement for the DAP (Directory Access Protocol), which served information housed in OSI X.500-compliant directory servers. LDAP has since found its way into countless back offices, providing access to directories of users, email services, organizational units, and many other types of information. LDAP directories organize the information they contain in a hierarchy. Each element in the tree is assigned a distinguished name (DN) that uniquely identifies it in the directory.The DN is comprised of the elements relative distinguished name (RDN) and the RDN of its parents. For example, a computer called computer1 might be given the RDN, CN=computer1.This name would uniquely identify the element in the context of its parent container. If the computer belonged to the sales department of the company mycompany.com, its DN might be CN=computer1, OU=sales, DC=mycompany, DC=com.The computer would also have the following canonical name: mycompany.com/sales/computer1. LDAP queries all have a few things in common. First, they all begin their searches at a base DN.This could be the top of the tree (for example, DC=mycompany, DC=com) or any other node in the tree. Second, LDAP queries usually have one or more filters to narrow the search. LDAP filters use a prefix format in their syntax. For example, the filter (&(l=Raleigh)(st=North Carolina)) would find all directory entries with a location of Raleigh and a state of North Carolina. Finally, an LDAP query usually specifies a list of attributes to return. Attributes are the different fields that an LDAP entry contains.The values that these attributes contain can be stored on the entry or derived by some other means. Attribute names are specific to the type of LDAP entry to which they belong. The example in this section uses the <cfldap> tag with its action attribute set to query to perform a query against a user-specified LDAP database.You must define the following attributes in the <cfldap> tag when executing a query: server (the

11.5.

Adding an LDAP Entry

143

name or IP address of the LDAP server), username (the username of a user authorized to perform the query), password (the users password), start (the starting DN where the query will begin), filter (the filter test to be applied to all nodes in the search scope), attributes (the LDAP attributes to be returned from the query), and name (the name of the ColdFusion query object that will house the results).

11.5. Adding an LDAP Entry


You want to add an entry to an LDAP directory.

Technique
Use the <cfldap> tag with its action attribute set to add to add a new entry to an LDAP server.
<cfparam <cfparam <cfparam <cfparam <cfparam <cfparam <cfparam <cfparam <cfparam <cfparam <cfparam name=dn default=CN=jsmith,OU=Departments,DC=mycompany,DC=com> name=username default=jsmith> name=firstName default=John> name=lastName default=Smith> name=displayName default=John Smith> name=city default=Raleigh> name=state default=North Carolina> name=email default=jsmith@mycompany.com> name=username default=administrator@mycompany.com> name=password default=password> name=ldapserver default=ldap1.mycompany.com>

<cfset attributelist = objectclass=top, person, organizationalPerson, user;> <cfset attributelist = attributelist& cn=#Trim(displayName)#;givenName=#firstName#; sn=#lastName#;> <cfset attributelist = attributelist&l=#city#;st=#state#;mail=#email#> <cftry> <cfldap action=add attributes=#attributelist# dn=#dn# server=#ldapserver# username=#username# password=#password#> <cfcatch> <cfdump var=#cfcatch#> </cfcatch> </cftry>

144

Chapter 11

Using Internet Protocols

Comments
This example uses the <cfldap> tag to add a new entry to the LDAP directory by assigning the value add to the tags action attribute and providing the necessary information to create the new entry.The example catches any exceptions that might be thrown by the LDAP request, such as a connection failure or an authentication failure. To add a new entry, you must also provide the following attributes: attributes (a semicolon-delimited list of attribute name/value pairs), dn (the distinguished name to assign the new entry), server (the name or IP address of the LDAP server), username (the username of a user who is authorized to perform this transaction), and password (the authorized users password). Take care that you specify all of the required attributes for the type of entry that you are adding. Consult your LDAP servers documentation to determine the required attributes.

11.6. Updating an LDAP Entry


You want to update an existing entry in an LDAP directory.

Technique
Use the <cfldap> tag with its action attribute set to modify to update an existing entry on an LDAP server.
<cfparam <cfparam <cfparam <cfparam name=dn default=CN=John Smith,OU=Departments,DC=mycompany,DC=com> name=username default=administrator@mycompany.com> name=password default=password> name=ldapserver default=ldap1.mycompany.com>

<cfset attributelist = title=VP of Product Development> <cftry> <cfldap action=modify modifytype=replace attributes=#attributelist# dn=#dn# server=#ldapserver# username=#username# password=#password#> <cfcatch> <cfdump var=#cfcatch#> </cfcatch> </cftry>

11.7.

Adding and Deleting an LDAP Entrys Attributes

145

Comments
This example updates an existing attribute on an existing LDAP entry. Specifically, it sets John Smiths title to VP of Product Development.To perform this operation, you must specify the following <cfldap> attributes: action (must be set to modify), modifytype (what to do with the specified attributes in the case of a multi-valued attribute: add, delete, or replace), attributes (a semicolon-delimited list of attribute name/value pairs to be updated on the entry), server (the name or IP address of the LDAP server), username (the username of an authorized user), and password (the users password).

11.7. Adding and Deleting an LDAP Entrys Attributes


You want to add or delete attributes on an existing LDAP entry.

Technique
Use the <cfldap> tag with its action attribute set to modify to add or delete an attribute on an LDAP entry.
<cfparam <cfparam <cfparam <cfparam name=dn default=CN=John Smith,OU=Departments,DC=mycompany,DC=com> name=username default=administrator@mycompany.com> name=password default=password> name=ldapserver default=ldap1.mycompany.com>

<cfset attributelist = title=VP of Product Development> <cftry> <cfldap action=modify modifytype=add attributes=#attributelist# dn=#dn# server=#ldapserver# username=#username# password=#password#> <cfcatch> <cfdump var=#cfcatch#> </cfcatch> </cftry>

146

Chapter 11

Using Internet Protocols

Comments
The technique you use to add or delete an attribute is almost identical to how you update an attribute.The only difference is the value of the modifytype attribute in the <cfldap> tag. Setting the attribute to add will add the specified attributes if they do not exist. Conversely, setting the attribute to delete will remove the attributes from the entry.

11.8. Renaming an Entry


You want to change the distinguished name of an LDAP entry.

Technique
Use the <cfldap> tag with its action attribute set to modifyDN to change an LDAP entrys distinguished name.
<cfparam name=olddn default=CN=John Smith,OU=Departments,DC=mycompany,DC=com> <cfset attributelist = dn=CN=John C. Smith, OU=Departments, DC=mycompany, DC=com> <cftry> <cfldap action=modifyDN attributes=#attributelist# dn=#dn# server=#ldapserver# username=#username# password=#password#> <cfcatch> <cfdump var=#cfcatch#> </cfcatch> </cftry>

Comments
To rename an LDAP entry, you set the <cfldap> tags action attribute to modifyDN.The rest of the <cfldap> attributes are the same as when updating an entrys attribute; only the attributes attribute specifies the items new distinguished name.

11.10.

Sending a File via FTP

147

11.9. Deleting an LDAP Entry


You want to delete an entry from an LDAP directory.

Technique
Use the <cfldap> tag with its action attribute set to delete to remove an entry from an LDAP directory.
<cfparam name=dn default=CN=John Smith,OU=Departments,DC=mycompany,DC=com> <cftry> <cfldap action=delete dn=#dn# server=#ldapserver# username=#username# password=#password#> <cfcatch> <cfdump var=#cfcatch#> </cfcatch> </cftry>

Comments
Deleting an LDAP entry is really easy; just set the action attribute to delete and specify the DN of the item to delete via the <cfldap> dn attribute. Once again, the usual suspectsserver, username, and passwordare in attendance.

11.10. Sending a File via FTP


You want to upload a file from the ColdFusion server to an FTP server.

Technique
Use the <cfftp> tag to move a file up to an FTP server.
<cfparam <cfparam <cfparam <cfparam <cfparam name=username default=myusername> name=password default=mypassword> name=ftpserver default=ftp.mydomain.com> name=localfile default=/myfile.txt> name=remotefile default=/myfile.txt>

<cfftp action=putfile username=#username#

148

Chapter 11

Using Internet Protocols

password=#password# server=#ftpserver# localfile=#localfile# remotefile=#remotefile#> <cfdump var=#cfftp#>

Comments
ColdFusion MX provides FTP support with its <cfftp> tag.The tag can be used to perform any of the various FTP commands that can be executed from a command prompt, including uploading files, downloading files, creating directories, and listing directories. Like the <cfldap> tag, the <cfftp> tag relies on its action attribute to dictate its behavior. In most cases, <cfftp> creates a connection to the FTP server, performs the requested action, closes the connection, and returns the results of the command to the user using the cfftp.returnValue variable. This behavior can be undesirable if you need to perform multiple commands on the same FTP server. In such cases, ColdFusion allows you to cache FTP connections. By using the open action, you can create a persistent connection to an FTP server and save the handle in a ColdFusion variable. Subsequent calls to <cfftp> can re-use this connection via the tags connection attribute.When you are finished using the FTP connection, you can close it with a final call to <cfftp> using the close action. In this example, you do not need to use connection caching because you just need to perform a single FTP operationuploading a file to an FTP server.The putfile action requires the following attributes to be set: username (the username to use when connecting the FTP server), password (the password to use when connecting), server (the name or IP address of the FTP server), localfile (the full path description of the file to be sent), and remotefile (the name of the file to create on the server).

11.11. Getting a File via FTP


You want to download a file from an FTP server.

Technique
Use the <cfftp> tag to download a file from an FTP server.
<cfparam <cfparam <cfparam <cfparam <cfparam name=username name=password name=ftpserver name=localfile name=remotefile

11.11.

Getting a File via FTP

149

<cfftp action=getfile username=#username# password=#password# server=#ftpserver# localfile=#localfile# remotefile=#remotefile#> <cfdump var=#cfftp#>

Comments
Downloading a file from an FTP server is very similar to uploading one.The action attribute must be set to getFile.The rest of the arguments are the same; only the remotefile attribute now represents the file to be retrieved and the localfile attribute represents the file to be created.

12
Using Verity
12.0. Introduction
One common component of easy-to-use Web applications is a search interface. As the amount of pure content grows in a Web application, searching tools become more critical.Thankfully, ColdFusion includes a simple interface into one of the most powerful search engines on the market, Verity.Verity ships with ColdFusion MX and can be used with relative ease by a developer with limited Verity knowledge. This chapter is simply an introduction to the Verity Search Engine and the associated tools ColdFusion provides.Verity is a complex, mature searching tool. If you do not find the information in this chapter sufficient to solve your search-engine development, you can find more information at the original manufacturerss Web site: http://www. verity.com.

12.1. Creating a Collection


You want to create a Verity collection and define its properties.

Technique
Use the <cfcollection> tag or the ColdFusion Administrator to create a Verity collection.
<cfcollection action=Create collection=myCollection path=c:\cfusionmx\collections\>

152

Chapter 12

Using Verity

Comments
There are two ways to create a Verity collection. One way is to use the <cfcollection> tag as is shown in the previous technique.When used to create a collection, the <cfcollection> tag accepts one optional and three required attributes.These attributes are defined in Table 12.1.
Table 12.1 cfcollection Create Task Attributes Attribute
action collection path language

Description Required. Must be create to create a collection. Required. String name of the collection you want to create. Required. Defines the absolute path to the directory where you want to store the collection. Optional. Defines the locale used to index the collection.You must have the Verity Locale Language Pack installed for the Locale you plan to use. Default is English.

You can also create a new Verity collection using the ColdFusion Administrator.To do so, log into the administrator and browse to the Verity Collections page under Data & Services.This page presents a form to create new collections. Simply enter the name of your new collection, change the path or accept the default, and click Submit.

12.2. Adding Information to a Verity Collection


You want to add information to a Verity collection.

Technique
Use the <cfindex> tag or the ColdFusion Administrator to add data to a Verity collection.
<cfindex collection=myCollection action=update type=file key=c:\inetpub\wwwroot\index.html urlPath=http://localhost/>

Comments
There are two ways to add data to an existing Verity collection. One way is to use the <cfindex> tag as is shown in the previous technique.When used to add data to a

12.2.

Adding Information to a Verity Collection

153

collection, the <cfindex> tag behaves differently depending on whether you want to add a single file to the collection, all files contained in a directory (and optionally, its sub-directories), or data contained in a query set.These attributes are defined in Table 12.2.
Table 12.2 cfindex Update Task Attributes Attribute
action body collection extensions

Description Required. Must be update to add data to a collection. Required only if type is custom. Defines the query columns containing the data you want to add to the collection as a comma-delimited list. Required. String name of the collection to which you want to add data. Optional. Defines an extension filter to use when type is set to path. ColdFusion will only update the collection with data from files that satisfy the extension criteria. Default is .cfm,.cfml,.dbm,.dbml,.htm,.html. Optional. Defines the absolute path to the desired file if the type attribute is set to file. Defines the absolute path to the desired directory if type is set to path. Defines the name of the column used for the key if type is set to custom. Default is an empty string. Optional. Defines the locale used to index the collection.You must have the Verity Locale Language Pack installed for the Locale you plan to use. Default is English. Required if type is set to custom. Defines the name of the query containing the data used to update the collection. Optional. Applies only if type is set to path. Determines whether subdirectories of the path defined should be included in the collection. Default is No. Optional. Accepted values are file, path, and custom. If file is specified, this attribute updates the collection with data in the file defined by the key attribute. If path is specified, this attribute updates the collection with data from files in the path defined by the key attribute. If custom is specified, this attribute updates the collection with data specified by the key column of the provided query. Default is custom if query is provided, file otherwise. Optional. Defines the URL path for retrieving the desired items to be returned from a <cfsearch> execution.

key

language

query recurse type

urlPath

There are numerous methods to add data to an existing collection.To add a single file to the collection, take a look at the following code example:
<cfindex collection=myCollection action=update type=file key=c:\documents\someFile.txt>

154

Chapter 12

Using Verity

This example adds the content in file c:\documents\someFile.txt to the existing myCollection Verity collection.The action is set to update, as is the case when adding any type of data to a collection.The type is set to file, which alerts ColdFusion that this update will take place only on a specific file.The key field then is used to define the absolute path to the desired file. You can also add data from all files in a directory and a sub-directory to a Verity collection.To add content from a hierarchy of directories, take a look at the following example:
<cfindex collection=myCollection action=update type=path key=c:\documents recurse=yes extensions=.pdf,.txt>

This example adds all the documents found in the directory structure beneath c:\ documents that satisfy the extensions filter to the myCollection Verity collection. Because the type is path, the key attribute defines the absolute path to the parent directory. Recurse is set to yes to instruct ColdFusion to recurse through the directory structure found beneath the directory defined by the key attribute.The extensions filter is also used to define the file types to be included in the collection. Notice that one of the file types is .PDFAcrobat documents are supported in Verity collections. You can also add data from a query record set to a Verity collection.To add data to a Verity collection using the results of a query, take a look at the following example:
<cfquery name=getContent datasource=myDS> SELECT id,content FROM myTable </cfquery> <cfindex collection=myCollection action=update type=custom body=content key=id query=getContent>

This example adds data from the content field into the myCollections Verity collection.The type is defined as custom, which is required when using a query object as input.The body attribute defines which column or columns contain the data you want

12.3.

Searching a Verity Collection with Simple Search

155

to add to the collection. Multiple column names should be delimited by commas.The key defines the column used to create the key; the best practice is to use the primary key defined for the table.The query attribute simply defines the name of the query used to populate the collection. The refresh action can also be used to update an index, but it will delete all the existing keys before adding the new content. In all the code examples in this section, refresh can be used as the type attribute instead of update. You can also add content to an existing Verity collection using the ColdFusion Administrator.To do so, log into the Administrator and browse to the Verity Collections page under Data & Services.This page lists all the existing Verity collections on the server. Click the link displaying the name of the collection you want to update.This presents a form allowing you to browse for a directory, define whether the update should include subdirectories of the parent, define a file extension filter, and define a return URL. After you fill in the appropriate form fields and click Submit, your collection is updated. Another added benefit of the ColdFusion <cfindex> tag is the capability to add custom fields to collection entries. <cfindex> allows up to two custom fields per record.The following code example adds the variables appName and username to the new collection entry:
<cfindex collection=myCollection action=update type=file key=c:\documents\someFile.txt custom1=#appName# custom2=#username#>

12.3. Searching a Verity Collection with Simple Search


You want to perform a simple search against an existing Verity collection.

Technique
Use the <cfsearch> tag with the type attribute set to simple to perform a simple search against an existing Verity collection.
<cfsearch name = collectionSearch collection = myCollection criteria = test type=simple>

156

Chapter 12

Using Verity

Comments
There are two types of search methods you can use against Verity collections: simple and explicit.To perform a simple search against a collection, use the <cfsearch> tag with the type attribute set to simple.The previous technique performs a simple search against the myCollection collection for any entries containing the string test. When used as a simple search, the <cfsearch> tag accepts two required and five optional attributes.These attributes are defined in Table 12.3.
Table 12.3 cfsearch Attributes Attribute
collection criteria language

Description Required. Comma-delimited list of registered collection names or absolute paths to other collections to include in the search. Optional. Defines the criteria used for the search. If left blank, all rows are returned following the startRow. Optional. Defines the locale used to index the collection.You must have the Verity Locale Language Pack installed for the Locale you plan to use. Default is English. Optional. Defines the maximum number of records to return in the search. Required. Defines the name of the query object returned from the search execution. Optional. Defines the first record number to retrieve. Usually used in combination with maxRows. Required. Must be set to simple to perform a simple search.

maxRows name startRow type

A simple search will suffice for the majority of search tools. Simple search implies a STEM operator, meaning that searches for the word test will return records containing the words test, tests, tested, and testing. Note that STEM does not work on word fragmentsthat is, searching for tes will not function the same as searching for test.You can, however, use a wildcard character (*) to return the same results with tes. (This search would also return records containing the word testimony.) Executing the <cfsearch> tag returns a query object containing one row per item in the collection matching your search criteria and satisfying your extensions filter. For more on ColdFusion query objects, see Chapter 5, Queries. The returned query object contains eight columns.These columns are defined in Table 12.4.
Table 12.4 cfsearch Returned Query Object Attribute
custom1 custom2

Description The value of the custom1 field for this record. If its not defined, an empty string is returned. The value of the custom1 field for this record. If its not defined, an empty string is returned.

12.4.

Searching a Verity Collection with Explicit Search

157

Table 12.4 Continued Attribute


key recordsSearched score summary title url

Description The key value defined for the entry in the index. The number of records searched in the collection. The Verity score for the record based on the search criteria. A perfect match gets a score of 1. The content searched for this record. The value of the title property for the record. If its undefined, an empty string is returned. The value of the URL property for the record. If its undefined, an empty string is returned.

12.4. Searching a Verity Collection with Explicit Search


You want to perform an explicit search against an existing Verity collection.

Technique
Use the <cfsearch> tag with the type attribute set to explicit to perform an explicit search against an existing Verity collection.
<cfsearch name = collectionSearch collection = myCollection criteria = test type=explicit>

Comments
Explicit searches differ from simple searches in that the STEM operator is not implied. To perform an explicit search, you wrap the search string in double quotation marks as shown in the previous technique. Executing the search in this technique will only return documents containing the literal string test. It will not return testing or tests as will the code in the previous technique.
Note
Search terms in mixed case will perform a case-sensitive search. Search terms in all lowercase will perform a case-insensitive search.

13
User Authentication and Authorization

13.0.

Introduction

Nearly every application developed these days requires that the users log in and authenticate their identity before performing certain actions. Recognizing this common requirement, ColdFusion MX introduced a framework for building authentication and authorization logic. The authentication and authorization framework in ColdFusion is independent of the authentication repository (sometimes called the authentication realm), which provides a great deal of flexibility. In other words, it doesnt matter where your usernames, passwords, and other user data is storedyou could use a database, an LDAP directory, a Kerberos repository, files,Web services, or anything you can access with ColdFusion.You can also build authentication that combines multiple repositories. In section 13.7, Authentication Example, you will build such an application. The framework provides a new variable scope that allows the user credentials to come from a number of sources, including a form variable, a URL variable, and HTTP basic authentication.With this capability, the same code can be used to authenticate Web services, Flash applications, mobile applications, or any Web application. In addition to providing a flexible authentication framework, ColdFusion provides a simple yet robust authorization scheme using roles. Roles allow you to easily determine whether a user has permission to perform a specific action.

160

Chapter 13

User Authentication and Authorization

13.1. Logging in a User


You want to tell ColdFusion that a user is logged in.

Technique
Use the <cflogin> and <cfloginuser> tags to tell ColdFusion that a user has logged in.
<cflogin> <cfloginuser name=anonymous password=guest roles=guest> Now reload this page, and you wont see this message. </cflogin>

Comments
The <cflogin> tag is used to contain a block of code where the authentication logic resides.The best way to think about the <cflogin> tag is If a user is not logged in then run this code. The <cfloginuser> tag tells ColdFusion which user is logged in, and what actions they are allowed to perform (roles). ColdFusion uses its own type of built-in session variables and non-persistent cookies to store this data.
Note
Because the cflogin scope is different from the session scope, it is possible for your session scope to expire before the cflogin scope. It is a good idea to set your session scope to have the same timeout as your cflogin scope. You should also always validate that a session variable is defined before you attempt to use it.

The first time you run this technique, the code inside the <cflogin> tags will execute, and your browser session will be logged in. Subsequent requests to the page will not display the reload message; the <cflogin> code is skipped because you are already logged in by the <cfloginuser> tag.

13.2. Logging out a User


You want to tell ColdFusion to log out a user.

Technique
Use the <cflogout> tag to log out a user.
<cflogout> You have been logged out, please come again.

13.3.

Authenticating a User

161

Comments
ColdFusion will log a user out automatically when the browser is closed, or when the idleTimeout expires.The idleTimeout is an attribute of the <cflogin> tag; it defaults to 1800 seconds (30 minutes). It is often necessary to provide a mechanism that allows users to log out before their cflogin session expires.The <cflogout> tag provides such a mechanism.When its executed, ColdFusion clears the cflogin scope and the user is effectively logged out.

13.3. Authenticating a User


You want to log in a user if the credentials are valid.

Technique
Use the j_username and j_password form field names to authenticate a user.
<!--- if user is not logged in ---> <cflogin> <cfset displayLoginForm = true> <!--- if username and password present ---> <cfif IsDefined(cflogin.name) AND IsDefined(cflogin.password)> <!--- if user and pass are authentic ---> <cfif cflogin.name is pete AND cflogin.password is go> <!--- tell CF who they are, and what they can do ---> <cfloginuser name=#cflogin.name# password=#cflogin.password# roles=user> <!--- no need to display login form ---> <cfset displayLoginForm = false> </cfif> </cfif> <cfif displayLoginForm> <form method=post> user: <input type=text name=j_username><br> pass: <input type=password name=j_password><br> <input type=submit value=login> </form> <cfabort> </cfif> </cflogin>

162

Chapter 13

User Authentication and Authorization

Comments
This technique determines whether a username and password was submitted to the page. If the credentials authenticate, the user is logged in; otherwise, the login form is displayed. The field names in the login form have a special meaning to ColdFusion.When a form is submitted with the field names j_username and j_password, ColdFusion automatically populates the variables cflogin.name and cflogin.password with their respective values. Although you can simply use form.j_username and form.j_password to access the value of the username and password, using the cflogin scope enables you to use the same authentication code for URL variables or basic HTTP authentication. If you want to restrict access to every page in your application, the application.cfm file is an ideal place to implement <cflogin> logic, because it is implicitly executed before every page request, including when CFCs are invoked as Web services.

13.4. Obtaining the Logged-in User ID


You want to determine the user ID of the currently logged in user.

Technique
Use the GetAuthUser function to get the user ID of the currently authenticated user.
<cflogin> <cfloginuser name=pete password=go roles=user> </cflogin> <cfoutput>#GetAuthUser()#</cfoutput> is cool!

Comments
The function GetAuthUser returns the value passed in to the name attribute of the <cfloginuser> tag.The function returns an empty string if the client is not authenticated.

13.5. Authorizing a Block of Code


You want to ensure that only users in a specific role can execute a block of code.

Technique
Use the IsUserInRole function to limit the blocks of code that users are allowed to execute.

13.5.

Authorizing a Block of Code

163

<cflogin> <cfloginuser name=pete password=go roles=user,admin> </cflogin> <cfif IsUserInRole(user)> In role: user <cfelse> Not in role: user </cfif> <cfif IsUserInRole(admin)> In role: admin <cfelse> Not in role: admin </cfif>

Comments
The IsUserInRole function returns true or false depending on whether the currently logged in user is in the role passed into the function. Recall that when you invoke the <cfloginuser> tag, you pass a comma-delimited list of role names into the roles attribute.The IsUserInRole function determines its result by using the list of roles passed in to the <cfloginuser> tag. Roles tell the application which actions the user can perform. Roles are also known as groups in some security policies. An example of where you can find roles-based security is in a bank. At a basic level, a banks employees can be broken into two groups: tellers and account managers.Tellers can make deposits to and withdrawals from a bank account. Account managers can do anything that a teller can do, but also have added permissions to create and close accounts. Even with this simple example, there are several ways to assign the roles. One possibility is to create two roles: one called tellers and the other called account_managers. Another possibility is to create a role for each privileged action.The roles may be called deposit, withdraw, createAccount, and closeAccount.Then, each user would be assigned multiple roles. The roles you define are key to the flexibility and complexity of your authorization system.When you define roles to job titles and then the job descriptions change, you have to change your code. If you define roles to each action, you can end up with a too many roles, which can be complex to handle. Because of this tradeoff between complexity and flexibility, its worth spending the time in your design process to contemplate your particular situation before you define the roles.

164

Chapter 13

User Authentication and Authorization

13.6. Authorizing a Function


You want to ensure that only users in a specific role can execute a particular function.

Technique
Use the roles attribute of the <cffunction> tag to specify which roles can execute the function.
<cflogin> <cfloginuser name=pete password=go roles=user,admin> </cflogin> <cffunction name=userFunc roles=user,admin> <cfoutput>#GetAuthUser()# has permission to execute userFunc</cfoutput> </cffunction> <cffunction name=adminOnly roles=admin> <cfoutput>#GetAuthUser()# has permission to execute adminOnly</cfoutput> </cffunction> <cfoutput> #userFunc()#<br> #adminOnly()# </cfoutput>

Comments
Roles security can be implemented with the <cffunction> tag through either ColdFusion components (see Chapter 19, Components, for more information on CFCs) or user-defined functions (see Chapter 16, User-Defined Functions, for more information on UDFs).The <cffunction> tag has an attribute called roles that allows you to specify a comma-delimited list of roles that have permission to execute the function. <cffunction> will throw an exception if the user does not have permission to execute the function.The error message generated by <cffunction> when a user is not in the list of roles is not typically one you want to show to your users. Consider using <cftry>, <cfcatch>, or <cferror> to catch the exception. See Chapter 8, Exception Handling, for more information on dealing with these errors.
Note
The documentation for ColdFusion MX specifies that the roles attribute of the <cffunction> can only be used when <cffunction> is used in a ColdFusion component. This is not the caseit can be used in a UDF as proven by the example in this technique. Because the actual intent of Macromedia may be different from how it currently works, take note of this issue in future releases.

13.7.

Authentication Example

165

13.7. Authentication Example


You want to authenticate multiple users against multiple repositories.

Technique
Use an abstract component and inheritance to build an authentication architecture that can handle multiple users and repositories.
application.cfm:
<cfapplication name=acmeintranet sessionmanagement=yes> <!--- variable for datasource name ---> <cfset request.dsn = cfcookbook> <cflogin> <cfset authType = Simple,Database,LDAP> <cfinclude template=loginLogic.cfm> </cflogin>

loginLogic.cfm:
<!--- if user is not logged in ---> <!--- if username and password present ---> <cfif IsDefined(cflogin.name) AND IsDefined(cflogin.password)> <cfset loggedIn = false> <!--- loop through the list of authorization types (e.g. ldap, database, etc) ---> <cfloop list=#authType# index=theAuthType> <cfset authenticator = CreateObject(component, #theAuthType#Authenticator)> <cfset loggedIn = authenticator.authenticate(cflogin.name, cflogin.password)> <cfif loggedIn is true> <cfset session.user = authenticator.getUser()> <cfloginuser name=#cflogin.name# password=#cflogin.password# roles=#session.user.roles#> <cfbreak> </cfif> </cfloop> <cfif not loggedIn> <cfset loginMessage = Incorrect Login. Please try again> <cfinclude template=loginForm.cfm> <cfabort> </cfif> <!--- display login form ---> <cfelse> <cfinclude template=loginForm.cfm>

166

Chapter 13

User Authentication and Authorization

<cfabort> </cfif>

loginForm.cfm:
<cfparam name=loginMessage type=string default=> <!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.0 Transitional//EN> <html> <head> <title>ACME Intranet</title> <style> BODY { font-family:verdana,tahoma,sans-serif;font-size:10pt;} TD { font-family:verdana,tahoma,sans-serif;font-size:10pt; } .title { color:006699;font-weight:bold;font-size:16pt;} .header { color:gray;font-weight:bold;font-size:11pt; } .msg { color: red; font-weight: bold; font-size:8pt; } .bottomLine { border-bottom : 1px solid gray; } </style> </head> <body> <div class=title>&nbsp;ACME Intranet</div> <table border=1 bordercolor=white cellpadding=1 cellspacing=1 width=100%> <tr height=40> <td bgcolor=C2CAC0 width=4%>&nbsp;</td> <td bgcolor=96B3B5 width=16%>&nbsp;</td> <td bgcolor=C1A99B width=25%>&nbsp;</td> <td bgcolor=888483 width=55%>&nbsp;</td> </tr> <tr> <td colspan=4> <hr color=silver width=100% noshade size=2 align=center> <form method=post> <table border=0 width=400 align=center> <tr> <td class=bottomLine colspan=2> <span class=header>Please Login</span> </td> </tr> <tr> <td colspan=2 align=center> <span class=msg> &nbsp;<cfoutput>#loginMessage#</cfoutput> </span> </td> </tr> <tr> <td align=right>username:</td>

13.7.

Authentication Example

167

<td> <input type=text name=j_username style=border: 1px solid gray;> </td> </tr> <tr> <td align=right>password:</td> <td> <input type=password name=j_password style=border: 1px solid gray;> </td> </tr> <tr><td class=bottomLine colspan=2>&nbsp;</td></tr> <tr> <td>&nbsp;</td> <td align=right> <input type=submit value=login style=border: 1px solid gray;background-color:white;> &nbsp;&nbsp; </td> </tr> </table> </form> <hr color=silver width=100% noshade size=2 align=center> </td> </tr> </table> </body> </html>

user.cfc:
<cfcomponent displayname=User hint=Represents a logged in user> <cfset this.userID = -1> <cfset this.displayName = > <cfset this.firstName = > <cfset this.city = > <cfset this.state = > <cfset this.country = > <cfset this.email = > <cfset this.phone = > <cfset this.roles = > </cfcomponent>

Authenticator.cfc:
<cfcomponent displayname=Authenticator hint=defines base functionality for authentication> <cfset user = CreateObject(component, User)>

168

Chapter 13

User Authentication and Authorization

<cffunction name=authenticate returntype=boolean output=false hint=returns true if credentials authenticate> <cfargument name=username type=string required=true> <cfargument name=password type=string required=true> <!--- you must extend AuthenticationMethod cfc ---> <cfthrow message=authenticate must be defined in a sub class> </cffunction> <cffunction name=getUser returntype=User output=false hint=returns the user that was authenticated> <cfreturn user> </cffunction> </cfcomponent>

SimpleAuthenticator.cfc:
<cfcomponent extends=Authenticator displayname=SimpleAuthenticator hint=simple auth.> <cffunction name=authenticate returntype=boolean output=false> <cfargument name=username type=string required=true> <cfargument name=password type=string required=true> <!--- simply check to see if they are the same ---> <cfif arguments.password IS arguments.username> <cfset user.userID = arguments.username> <cfset user.displayName = arguments.username> <cfset user.roles = users> <cfreturn true> <cfelse> <!--- pass not = to user, not authenticated ---> <cfreturn false> </cfif> </cffunction> </cfcomponent>

DatabaseAuthenticator.cfc:
<cfcomponent extends=Authenticator displayname=DatabaseAuthentication hint=uses database> <cffunction name=authenticate returntype=boolean output=false> <cfargument name=username type=string required=true> <cfargument name=password type=string required=true> <!--- is it a valid user? ---> <cfquery datasource=#request.dsn# name=userInfo> SELECT UserID, FirstName, LastName, City, State,

13.7.

Authentication Example

169

Country, Email, Phone FROM users WHERE UserName = #arguments.username# AND PassWord = #arguments.password# </cfquery> <cfif userInfo.recordcount> <!--- get roles for user ---> <cfquery datasource=#request.dsn# name=userroles> SELECT r.name FROM roles AS r, user_roles AS ur WHERE ur.UserID = #userInfo.UserID# AND ur.RoleID = r.RoleID </cfquery> <!--- convert query column to list ---> <cfset roles = ValueList(userroles.name)> <!--- populate the user cfc object ---> <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset user.displayName = userInfo.FirstName> user.firstName = userInfo.FirstName> user.lastName = userInfo.LastName> user.city = userInfo.City> user.state = userInfo.State> user.country = userInfo.Country> user.email = userInfo.Email> user.phone = userInfo.Phone> user.roles = roles> user.userID = userInfo.UserID>

<cfreturn true> <!--- bad username or password ---> <cfelse> <!--- try login again ---> <cfreturn false> </cfif> </cffunction> </cfcomponent>

LDAPAuthenticator.cfc:
<cfcomponent extends=Authenticator displayname=LDAPAuthenticator hint=LDAP auth.> <!--- your Active Directory Domain ---> <cfset domain = mycompany.com> <!--- the address of your ldap server ---> <cfset ldapServer = localhost> <!--- the base DN to use when querying the server --->

170

Chapter 13

User Authentication and Authorization

<cfset ldapBaseDn = dc=mycompany,dc=com> <!--- the user ldap attribute for comparing against the username ---> <cfset ldapUidAttr = sAMAccountName> <!--- the user ldap attribute for finding a users groups or roles ---> <cfset ldapRoleAttr = memberOf> <!--- the group ldap attribute for getting a roles name ---> <cfset ldapRoleNameAttr = name> <!--- map ldap user attribute names to expected session variable names ---> <cfset ldapmap = StructNew()> <cfset ldapmap.displayName = displayName> <cfset ldapmap.firstName = givenName> <cfset ldapmap.lastName = sn> <cfset ldapmap.city = l> <cfset ldapmap.state = st> <cfset ldapmap.country = c> <cfset ldapmap.email = mail> <cfset ldapmap.phone = telephonenumber> <cffunction name=authenticate returntype=boolean output=false> <cfargument name=username type=string required=true> <cfargument name=password type=string required=true> <cftry> <cfset filter=(#ldapUidAttr#=#username#)> <cfset attributes=> <cfloop list=#StructKeyList(ldapmap)# index=prop> <cfset ldapattr = StructFind(ldapmap,prop)> <cfset attributes = ListAppend(attributes,ldapattr)> </cfloop> <!--- attempt to authenticate the user and download his/her profile info ---> <cfldap server=#ldapServer# username=#arguments.username#@#domain# password=#arguments.password# action=query start=#ldapBaseDn# filter=#filter# attributes=#attributes# name=results> <!--- set the user properties ---> <cfloop list=#StructKeyList(ldapmap)# index=prop> <cfset ldapattr = StructFind(ldapmap,prop)> <cfset ldapval = StructFind(results,ldapattr)> <cfset tmp = SetVariable(user.#prop#,ldapval)> </cfloop>

13.7.

Authentication Example

171

<!--- get the users roles ---> <cfldap server=#ldapServer# username=#arguments.username#@#domain# password=#arguments.password# action=query start=#ldapBaseDn# filter=#filter# attributes=#ldapRoleAttr# name=groups> <cfset roles = ArrayNew(1)> <cfset i = 1> <cfset grouplist = StructFind(groups,ldapRoleAttr)> <cfset grouplist = Replace(grouplist,, ,chr(253),all)> <cfloop list=#grouplist# index=group delimiters=#chr(253)#> <!--- get the role detail ---> <cfldap server=#ldapServer# username=#arguments.username#@#domain# password=#arguments.password# action=query start=#group# filter=(objectClass=*) attributes=#ldapRoleNameAttr# name=thisGroup> <cfset roleName = StructFind(thisGroup,ldapRoleNameAttr)> <cfset roles[i] = roleName> <cfset i = i + 1> </cfloop> <cfset rolelist = ArrayToList(roles)> <cfset user.userID = arguments.username> <cfset user.roles = rolelist> <cfreturn true> <cfcatch type=any> <cfreturn false> </cfcatch> </cftry> </cffunction> <cffunction name=setDomain output=false returntype=void> <cfargument name=domain type=string required=true> <cfset domain = arguments.domain> </cffunction> <cffunction name=setServer output=false returntype=void> <cfargument name=server type=string required=true>

172

Chapter 13

User Authentication and Authorization

<cfset ldapServer = arguments.server> </cffunction> <cffunction name=setBaseDN output=false returntype=void> <cfargument name=baseDN type=string required=true> <cfset ldapBaseDN = arguments.basedN> </cffunction> </cfcomponent>

acmeIntranet.cfm:
<!--- make sure session variables dont expire ---> <cfif NOT IsDefined(session.user)> <cflogout> <cflocation url=loginForm.cfm> </cfif> <!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.0 Transitional//EN> <html> <head> <title>ACME Intranet</title> <style> BODY { font-family:verdana,tahoma,sans-serif;font-size:10pt;} TD { font-family:verdana,tahoma,sans-serif;font-size:10pt; } .title { color:006699;font-weight:bold;font-size:16pt;} .header { color:gray;font-weight:bold;font-size:11pt; } .bottomLine { border-bottom : 1px solid gray; } A { color:006699;} </style> </head> <body> <div class=title>&nbsp;ACME Intranet</div> <table border=1 bordercolor=white cellpadding=1 cellspacing=0 width=100%> <tr height=40> <td bgcolor=C2CAC0 width=4%>&nbsp;</td> <td bgcolor=96B3B5 width=16%>&nbsp;</td> <td bgcolor=C1A99B width=25%>&nbsp;</td> <td bgcolor=888483 width=55%>&nbsp;</td> </tr> <tr> <td colspan=4> <hr color=silver width=100% noshade size=2 align=center> <div class=header> Welcome <cfoutput>#session.user.displayName#</cfoutput>,

13.7.

Authentication Example

173

</div> <p> Below you will find operations that you can perform in the intranet. </p> <cfif IsUserInRole(users)> <table border=0 cellpadding=2 cellspacing=0 width=80%> <tr> <td class=bottomLine> <span class=header>User Options</span> </td> </tr> <tr> <td> <ul type=square style=color:gray> <li> <a href=editUser.cfm>Edit User Settings</a> change your password, or contact info </li> <li> <a href=addDoc.cfm>Add a Document</a> add a document to the intranet </li> <li> <a href=viewDoc.cfm>View Documents</a> view documents in the intranet </li> <li> <a href=email.cfm>Check Email</a> check your email </li> </ul> </td> </tr> </table> </cfif> <cfif IsUserInRole(administrators)> <table border=0 cellpadding=2 cellspacing=0 width=80%> <tr> <td class=bottomLine> <span class=header>Administrator Options</span> </td> </tr> <tr> <td>

174

Chapter 13

User Authentication and Authorization

<ul type=square style=color:gray> <li> <a href=editUsers.cfm>Edit User Settings</a> change password, or contact info for other users </li> <li> <a href=addUser.cfm>Add a User</a> add a new user to the intranet </li> <li> <a href=deleteUser.cfm>Delete User</a> view documents in the intranet </li> </ul> </td> </tr> </table> </cfif> <div align=right><A href=logout.cfm>Log Out</A></div> <hr color=silver width=100% noshade size=2 align=center> </td> </tr> </table> </body> </html>

Comments
Thus far, you have not seen any authentication code that can work with more than one user.This section shows you how to build an application that not only authenticates more than one user, but can also authenticate from several repositories (database, LDAP, and simple). The application.cfm file is easy to read because all the authentication logic has been moved into loginLogic.cfm and a <cfinclude> is used to include it. The loginLogic.cfm page includes loginForm.cfm if a username and password are not sent in the page request, or if authentication fails.The login form will prompt the user for a username and password, and display an error message if applicable (see Figure 13.1). If the username and password are supplied, loginLogic.cfm attempts to authenticate the credentials against each authenticator component.The authType variable in application.cfm defines a list of names for each authenticator used by the application. In loginLogic.cfm, instances of each authenticator are created dynamically by looping through the authType list.

13.7.

Authentication Example

175

Figure 13.1 The output of loginForm.cfm, where the user logs in to the application.

The main design goal of this example is to be able to easily plug-and-play different authentication methods. CFCs (covered in Chapter 19) are a great way to accomplish this goal.The first CFC in this example is a CFC called User, which is used to hold information about a user when logged in. By using the this keyword, the CFC variables are publicly accessible, and can be accessed with dot notation (cfcInstance.variableName). The next CFC is called Authenticator; it defines a framework for the authentication methods (database, LDAP, and simple) to inherit from. Each authentication method will define a CFC that inherits from the Authenticator CFC, and overrides the pseudo-abstract method authenticate.The Authenticator CFC holds an instance of a User CFC, which is accessible through the getUser function. CFCs that inherit from Authenticator populate the User object with information when the authenticate method is called. The simple authenticator CFC simply determines whether the username and password are the same. If they match, the user will be logged in.You may notice that the example doesnt populate all of the fields in the user object because it doesnt have all the information.This is one advantage to using a CFC to store the user information over a structureyou can define default values. Next take a take a look at the database authenticator.The example uses a database with the following structure, listed in Tables 13.1-13.3.

176

Chapter 13

User Authentication and Authorization

Table 13.1 The user Table Column Name


UserID UserName PassWord FirstName LastName City State Country Email Phone

Data Type Auto incrementing integer (identity field), primary key Text/varchar Text/varchar Text/varchar Text/varchar Text/varchar Text/varchar Text/varchar Text/varchar Text/varchar

Table 13.2 The role Table Column Name


RoleID Name

Data Type Auto incrementing integer (identity field), primary key Text/varchar

Table 13.3 The user_roles Table Column Name


UserID RoleID

Data Type Integer Integer

The database design allows you to easily modify both the users and the roles independently.The design also allows users and roles to have a many-to-many relationship, meaning each user can have multiple roles, and each role can be assigned to multiple users. The final authenticator, the LDAP authenticator, is perhaps the most powerful yet, because it allows you to connect to enterprise directory servers such as Microsoft Active Directory. By changing some of the default values, or by calling the setDomain, setServer, and setBaseDN functions, you should be able to connect to any Windows 2000 Active Directory and authenticate users against it.The <cfldap> tag is covered in detail in Chapter 11, Using Internet Protocols. The main page of the Acme Intranet example (acmeIntranet.cfm) uses the IsUserInRole function to determine which links to show the user, as shown in Figure 13.2.The administrators have more options than regular users. Each page in your application that is restricted to specific roles should call the IsUserInRole function before displaying the page.

13.7.

Authentication Example

177

Figure 13.2 The main page, acmeIntranet.cfm, shows the appropriate content based on the users access level.

Notice that, at the top of the page, the program determines whether the session variables are defined. As mentioned, <cflogin> doesnt exactly use session variables to maintain state, and because of this, it is possible for session variables to expire before the <cflogin> variables do.

IV
Database Integration
14 15 Structured Query Language (SQL) Stored Procedures

14
Structured Query Language (SQL)

14.0.

Introduction

When ColdFusion 1.0 was released there were no .CFM files, no <cfset> tag, not even <cfif>. ColdFusion did however have .DBM files and a <dbif> tag.The DB stood for database; ColdFusion was born to connect the database and the Web browser. ColdFusion can do much more today than it could in its inception; however, its primary purpose is still to connect databases with the Web. Although databases are often integral parts of ColdFusion applications, database design and query performance are often overlooked in the development process. Improving SQL queries and utilizing the facilities of the database server can solve many performance problems. Structured Query Language (SQL) is the common language that all relational databases speak, with a few different dialects. Most of the time, your SQL statement will run without modification on several database servers. SQL has several underused capabilities that I hope you will unleash after reading this chapter. This chapter uses the same database tables to present all of the examples. Table 14.1 stores all of the employees and information related strictly to each employee.
Table 14.1 employees Table Column Name
EmployeeID Name Age Salary

Data Type Auto Incrementing integer (identity field), primary key Text/varchar Number/integer Currency/money

182

Chapter 14

Structured Query Language (SQL)

Table 14.1 Continued Column Name


Resume DepartmentID

Data Type Text Integer (foreign key)

Table 14.2 stores the departments. Each employee must belong to exactly one department, and each department can have several employees.The relationship between departments and employees is called a one-to-many relationship, sometimes noted as 1-N.
Table 14.2 department Table Column Name
DepartmentID Name

Data Type Auto Incrementing integer (identity field), primary key Text/Varchar

Next, you need to store information about the projects; the structure for the projects table is listed in Table 14.3.
Table 14.3 projects Table Column Name
ProjectID Name

Data Type Auto Incrementing integer (identity field), primary key Text/Varchar

Each employee can work on more than one project and each project can have more than one employee assigned to it.This is known as a many-to-many relationship or a N-N relationship, and is represented in Table 14.4.
Table 14.4 The employee_projects Table Is a Many-to-Many Relationship Column Name
EmployeeID ProjectID

Data Type Integer Integer

The employee_projects table uses two foreign keys to link the employees and projects table together.The two foreign keys together become the primary key for the employee_projects table. It would be a good idea to populate your database in order to run the examples in the following sections. Most databases provide a user interface that allows you to add data to a table.

14.2.

Updating Data

183

14.1. Inserting Data


You want to add a new row to a table.

Technique
Use the INSERT INTO and VALUES keywords to insert a new row in to a table.
<cfquery datasource=cfcookbook> INSERT INTO employees (Name, Age, Salary, DepartmentID) VALUES(Bob, 31, 50000, 1) </cfquery>

Comments
Inserting data into a database is fairly easy compared to selecting data, which is why its covered first.The INSERT keyword is used to tell the database that you want to add data to it.You use the INTO keyword to tell the database which table you are inserting into. After the table name, a list of columns is specified, and finally a list of values. You may notice that you dont need to specify every column defined in the table to insert. EmployeeID isnt included because it is defined in the database as an autonumbering identity, which means the database will populate that field automatically when you perform the insert.The Resume field is not included in this INSERT statement either; you may leave fields out of the INSERT statement if they are defined in the database to allow NULL values, or dont require a value.You can also set up your database to populate fields with default values. Although its not very common anymore, from time to time developers may omit the list of column names from the table in their insert statement.This will not cause an error on most databases, but it will eventually make someones life difficult, and possibly your own.When the column list is not present, the database will use the order in which the fields were created to determine where to place which value.This is something to avoid if youre at all concerned with writing maintainable code.
INSERT INTO employees VALUES(Bob, 31, 50000, 1)

14.2. Updating Data


You want to update one or more columns in an existing row.

Technique
Use the UPDATE and SET keywords to change data in your database.

184

Chapter 14

Structured Query Language (SQL)

<cfquery datasource=cfcookbook> UPDATE employees SET Salary = 1000000, Name = Rich WHERE EmployeeID = 1 </cfquery>

Comments
To change the data in your tables, the UPDATE keyword is used. After the UPDATE keyword, the table name is specified, and then the SET statement tells the database which values to update. Several fields can be updated at once by separating them with commas in the SET clause. Rows are typically updated one at a time, and a good way to do this is by testing the primary key in the WHERE clause. If you omit the WHERE clause, all the rows in the table will be updated.The WHERE clause is discussed in detail later in this chapter.

14.3. Deleting Data


You want to delete a row from a table.

Technique
Use the DELETE keyword to delete rows from your table.
<cfquery datasource=cfcookbook> DELETE FROM employees WHERE EmployeeID = 1 </cfquery>

Comments
When you delete data from a database with SQL, you should use a WHERE clause to limit which rows will be deleted (just as you do with the UPDATE statement). If you dont include a WHERE clause, every row in your table will be deleted. Microsoft Access requires that you specify columns to delete from after the DELETE keyword.
DELETE * FROM employees WHERE EmployeeID = 1

14.5.

Limiting Rows Returned

185

14.4. Selecting Data


You want to select some data from a database.

Technique
Use the SELECT statement to retrieve information from the database.
<cfquery datasource=cfcookbook name=employeeNames> SELECT Name FROM employees </cfquery> <cfdump var=#employeeNames#>

Comments
The trick to mastering SQL is mastering the SELECT statement. Retrieving information from the database that you need is typically much more difficult than manipulating or adding data. The query shown here can be used to select the names of all employees from the database.The SELECT keyword tells the database which action you are performing. After the SELECT keyword, you specify a list of columns. In this example, you just want the employees name, so you simply write Name.The FROM keyword tells the database from which table(s) to return data. Many novice SQL developers might write the SQL in this technique the following way:
SELECT * FROM employees

This code still works, but it returns the EmployeeID, Age, Salary, Resume, and DepartmentID along with the employee names. SELECT * means to return all columns in the table; this often means that you are asking the database to do more than it needs to. Never select more information than you need. Not only can this shortcut get you into trouble with the performance police (theyre out there, watch out!), but it also makes it difficult to maintain the code. The <cfdump> tag is used to display the results of any variable (passed in through the var attribute) neatly in a table. Its used here, and throughout the chapter, to display the results of the query.

14.5. Limiting Rows Returned


You want to return rows that meet a certain condition.

Technique
Use the WHERE keyword to limit the rows returned.
<cfquery datasource=cfcookbook name=petes> SELECT Name FROM employees

186

Chapter 14

Structured Query Language (SQL)

WHERE Name = Pete </cfquery> <cfdump var=#petes#>

Comments
Because you usually dont need to return all the rows in a table, SQL offers the WHERE clause.The WHERE clause allows you to limit the results returned.This example returns all employees named Pete. Notice that you must place single quotes around Pete to indicate that it is a string.This is required for strings. On numeric fields, the single quotes are not allowed.This requirement is a common source of error. Several operators are available for use within a WHERE clause; some of the common ones are outlined in Table 14.5.
Table 14.5 Common SQL Operators Operator
= <> > < >= <= IN LIKE AND OR

Meaning Equal to Not equal to Greater than Less than Greater than or equal to Less than or equal to In a list of values (see Section 14.6) Matches a pattern (see Section 14.7) Logical AND Logical OR

14.6. Specifying Multiple Conditions


You want to return rows that meet multiple conditions.

Technique
Use the AND or OR operators to specify multiple conditions in the WHERE clause.
<cfquery datasource=cfcookbook name=richKids> SELECT Name FROM employees WHERE Salary > 100000 AND Age < 30 </cfquery> <cfdump var=#richKids#>

14.8.

Using Aliases

187

Comments
The SQL in this technique selects all employees that have salaries larger than $100,000 and are younger than 30 years of age.The AND operator is used to indicate that both conditions must be true.The OR operator is used syntactically exactly the same way as the AND operator but it holds a different meaning. You can use parentheses to group together OR and AND statements, much like you can with <cfif>.

14.7. Sorting Results


You want your result set to be sorted by one or more columns.

Technique
Use the ORDER BY keywords to sort the result of a query by a specific column or columns.
<cfquery datasource=cfcookbook name=sortedEmployees> SELECT Name, Age FROM employees ORDER BY Name DESC, Age </cfquery> <cfdump var=#sortedEmployees#>

Comments
The ORDER BY keywords are followed by a list of columns that have been selected in the SELECT statement.The results are sorted in ascending order by default.You can also change the order by using the DESC (descending order) keyword or the ASC (ascending order) keyword.This SQL statement returns all names and ages first sorted by name (Z-A descending order). If any rows have the same name, they are sorted by age.

14.8. Using Aliases


You want to refer to columns or tables as different names.

Technique
Use the AS keyword to create an alias.
<cfquery datasource=cfcookbook name=alias> SELECT e.Name AS EmployeeName FROM employees AS e </cfquery>

188

Chapter 14

Structured Query Language (SQL)

<cfoutput query=alias> #alias.EmployeeName# <br> </cfoutput>

Comments
The AS keyword can be used to create an alias to a column name in the result set, or to create an alias to a table name used within the SQL statement.The syntax for the keyword is the same in both cases; after the real name for the object, you add AS AliasName. Using column name aliases is useful in aggregate functions (covered in Section 14.12).Table name aliases are useful when joining tables, which is covered next.

14.9. Selecting from Multiple Tables


You want to pull data from more than one table.

Technique
Specify multiple tables in the FROM statement to select data from multiple tables.
<cfquery datasource=cfcookbook name=empDepartments> SELECT e.EmployeeID, e.Name, e.DepartmentID, d.DepartmentID, d.Name FROM employees AS e, departments AS d </cfquery> <cfdump var=#empDepartments#>

Comments
A join allows you to retrieve related information from multiple tables in a single query. The output of the code in this technique, which includes two employees and two departments in the database, is shown in Table 14.6.
Table 14.6 Result of This Technique EmployeeID 1 1 2 2 Name
Emp1 Emp1 Emp2 Emp2

DepartmentID 1 1 2 2

DepartmentID 1 2 1 2

Name
Dept1 Dept2 Dept1 Dept2

The database returns the cross product (or Cartesian product) of employees and departments.This is known as a cross join, and is essential to understanding inner and outer

14.11.

Joining Three Tables

189

joins.The total number of records returned (the recordcount) in a cross join is always the number of rows in the first table multiplied by the number of rows in the second. This means that if either your employees table or your departments table have zero records, the query would not return any rows, because zero multiplied by anything is zero.That rule is important to keep in mind when you are performing inner joins, and motivates the need for outer joins.

14.10. Joining Two Tables


You want to select related data from two tables.

Technique
Use a WHERE statement that requires that the primary key in one table match a foreign key in the other table.
<cfquery datasource=cfcookbook name=joinEmpDept> SELECT e.Name AS EmpName, d.Name AS DeptName FROM employees AS e, departments AS d WHERE e.DepartmentID = d.DepartmentID </cfquery> <cfdump var=#joinEmpDept#>

Comments
The SQL in this technique is very similar to the previous technique except that it adds a WHERE statement.The WHERE statement limits the result of the cross product to the rows in which the DepartmentIDs from the employees and departments tables match.You can see in Table 14.6 that the DepartmentIDs from the employees table and department table match in the first and last row. Only the two matching rows are returned in this query, instead of a complete cross product. In this query, the employees DepartmentID is the foreign key and the departments DepartmentID is the primary key.The resulting query shows each employee along with the department they belong to.

14.11. Joining Three Tables


You want to select related data from three tables.

Technique
Use a WHERE statement with two conditions that match the primary keys with the foreign keys.

190

Chapter 14

Structured Query Language (SQL)

<cfquery datasource=cfcookbook name=empProjects> SELECT e.Name AS EmpName, p.Name AS ProjName FROM employees AS e, projects AS p, employee_projects AS ep WHERE e.EmployeeID = ep.EmployeeID AND ep.ProjectID = p.ProjectID ORDER BY e.Name </cfquery> <cfdump var=#empProjects#>

Comments
The result of this technique is a complete list of all employees assigned to projects. Notice that it includes two join conditions because you are selecting from three tables. Here the employee_projects table holds foreign keys to the employees and projects tables.

14.12. Grouping Data with CFOUTPUT


You want to display data in groups.

Technique
Use the group attribute of the <cfoutput> tag to output data in groups.
<cfquery datasource=cfcookbook name=empProjects> SELECT e.Name AS EmpName, p.Name AS ProjName FROM employees AS e, projects AS p, employee_projects AS ep WHERE e.EmployeeID = ep.EmployeeID AND ep.ProjectID = p.ProjectID ORDER BY e.Name </cfquery> <cfoutput query=empProjects group=EmpName> #empProjects.EmpName#<br> <ul> <cfoutput> <li>#empProjects.ProjName#</li> </cfoutput> </ul> </cfoutput>

14.13.

Performing Outer Joins

191

Comments
The <cfoutput> tag provides a useful attribute, called group, that allows you to take a query like the one described in the previous section and group the data by a column in that query. For instance, you can group by the EmpName column to display each employee and the projects they are assigned to.The nested <cfoutput> tag is used to iterate over each project, and the outer <cfoutput> tag is used to iterate over each employee.

14.13. Performing Outer Joins


You want to perform a join that returns all rows even if the keys do not match.

Technique
Use the LEFT OUTER JOIN keyword to join all rows in a table.
<cfquery datasource=cfcookbook name=allEmpProjects> SELECT e.Name AS EmpName, ProjectID FROM employees AS e LEFT OUTER JOIN employee_projects AS ep ON e.EmployeeID = ep.EmployeeID </cfquery> <cfdump var=#allEmpProjects#>

Comments
The joins you have seen thus far are known as inner joins, or cross joins. In many database implementations, there exists another type of join, known as an outer join.There are three types of outer joins: left outer join, right outer join, and full outer join. This query returns all employees in the table and their project IDs, whereby an inner join would have returned only the employees that had project IDs (employees who were assigned to a project).The query returns NULL in the ProjectID field for those employees who dont have a Project ID. If you were to change the LEFT OUTER JOIN in the query to a RIGHT OUTER JOIN, the query would return all the projects, even the ones that didnt have any employees assigned to them.This is known as a right outer join.The EmpName field would be NULL for those projects that do not have an employee. A FULL OUTER JOIN would return employees who arent assigned to any projects, as well as projects that arent assigned to any employees. Here, either the ProjectID field or the EmpName field can be NULL, but never both in the same row.The code in this technique can be changed to a full join by replacing LEFT OUTER JOIN with FULL OUTER JOIN.

192

Chapter 14

Structured Query Language (SQL)

14.14. Selecting Unique Values


You want to select only distinct values for a particular column.

Technique
Use the DISTINCT keyword to select only unique values from a table.
<cfquery datasource=cfcookbook name=distinctSalaries> SELECT DISTINCT Salary FROM employees </cfquery> <cfdump var=#distinctSalaries#>

Comments
The DISTINCT operator ensures that there are no duplicate values for a specific column in a result set.This query displays a list of all the different salaries that employees currently have.

14.15. Using Aggregate Functions


You want to perform an operation (such as an average or sum) against multiple rows in the database.

Technique
Use an aggregate function such as AVG() to perform the operation on all the rows in the table.
<cfquery datasource=cfcookbook name=avgSalary> SELECT AVG(Salary) AS AvgSalary FROM employees </cfquery> <cfoutput> The average salary is #DollarFormat(avgSalary.AvgSalary)# </cfoutput>

Comments
Aggregate functions allow you to perform operations (such as averages) on all of the values in a particular column.This query shows you the average salary in the organization.

14.16.

Grouping Aggregate Data

193

The result of this query is one row with one column (called AvgSalary). A query that contains aggregate functions can contain multiple aggregate functions, but cannot select non-aggregate data (for example, average salary and maximum salary may be selected together, but you cannot add Name to the SELECT statement). In order to use nonaggregate functions, you have to use a GROUP BY clause, covered in the next section. Table 14.7 lists some common aggregate functions.
Table 14.7 Common SQL Aggregate Functions Function
AVG(column) MAX(column) MIN(column) SUM(column) COUNT(column)

Meaning Average of all values in column Maximum value in column Minimum value in column The sum of all values in column The number of values in the column

These aggregate functions can only be used with numeric columns, except for the COUNT() function, which can be used on any type of column.

14.16. Grouping Aggregate Data


You want to group together related data.

Technique
Use a GROUP BY clause to specify which columns to group by.
<cfquery datasource=cfcookbook name=avgDeptSalaryAge> SELECT d.Name, AVG(Salary) AS AvgSalary, AVG(Age) AS AvgAge FROM employees AS e, departments AS d WHERE e.DepartmentID = d.DepartmentID GROUP BY d.Name </cfquery> <cfdump var=#avgDeptSalaryAge#>

Comments
With a GROUP BY clause, you can force aggregate functions to work only on groups of data.This query shows the average salary and average age in each department.

194

Chapter 14

Structured Query Language (SQL)

14.17. Limiting Aggregate Results


You want to place a condition on aggregate functions.

Technique
Use a HAVING clause to limit the results of an aggregate function.
<cfquery datasource=cfcookbook name=deptOverThirty> SELECT d.Name AS department FROM employees AS e, departments AS d WHERE e.DepartmentID = d.DepartmentID GROUP BY d.Name HAVING AVG(e.Age) > 30 </cfquery> <cfdump var=#deptOverThirty#>

Comments
The HAVING clause is used to place a condition on aggregate functions.You can use the query shown here to determine which departments have an average age above 30. The GROUP BY clause is required when you use a HAVING clause with non-aggregate function arguments in the SELECT clause.

14.18. Using the IN Keyword


You want to select records with field values found in a predetermined list of values.

Technique
Use the IN keyword to select within a list of values.
<cfquery datasource=cfcookbook name=empAge> SELECT Name FROM employees WHERE Age IN (30, 40, 50) </cfquery> <cfdump var=#empAge#>

14.19.

Using IN with Web Forms

195

Comments
The IN keyword is a useful shortcut when you have a list of possible values for a column. This query returns the names of employees with ages 30, 40, or 50. IN does not introduce any new functionality.The same query can also be written with the OR and = operator (as shown next).
SELECT Name FROM employees WHERE Age = 30 OR Age = 40 OR Age = 50

However, it is much easier to use IN, especially when you have a list of values.

14.19. Using IN with Web Forms


You want to select data based on which check boxes are enabled.

Technique
Use the list generated from a check box in the IN statement.
<cfquery datasource=cfcookbook name=depts> SELECT DISTINCT DepartmentID, Name FROM departments </cfquery> <form method=post> <cfoutput query=depts> <input type=Checkbox name=DepartmentList value=#depts.DepartmentID#> #depts.Name#<br> </cfoutput> <input type=submit value=Get Employees> </form> <cfif IsDefined(form.DepartmentList)> <cfquery datasource=cfcookbook name=employees> SELECT e.Name AS EmpName, e.Age, e.Salary, d.Name AS DeptName FROM employees AS e, departments AS d WHERE e.DepartmentID IN (#form.DepartmentList#) AND e.DepartmentID = d.DepartmentID ORDER BY DeptName </cfquery> <cfif employees.recordcount>

196

Chapter 14

Structured Query Language (SQL)

<cfoutput query=employees group=DeptName> <b>Employees in #DeptName#</b><br> <table border=1> <tr> <td><b>Name</b></td> <td><b>Age</b></td> <td><b>Salary</b></td> </tr> <cfoutput> <tr> <td>#EmpName#</td> <td>#Age#</td> <td>#Salary#</td> </tr> </cfoutput> </table> </cfoutput> <cfelse> Sorry No Employees. </cfif> </cfif>

Comments
The IN keyword comes in handy when you are working with HTML forms, specifically with check boxes and multiple select lists.The users selection gets translated into a comma-separated list in ColdFusion; exactly what the IN keyword takes as an argument. This example page allows users to check multiple departments and view all employees in each department. IN statements can also be used with text values; however, each value needs to have single quotes wrapping them.You can use ColdFusions ListQualify function to add single quotes to the list. Lists are discussed in depth in Chapter 2, Working with Lists, and ListQualify is discussed in Section 2.8, Adding Qualifiers to a List.

14.20. Matching a Wildcard Pattern


You want to match any number of characters to a field.

Technique
Use the LIKE operator with the % character to match a wildcard pattern.
<cfquery datasource=cfcookbook name=juniors> SELECT Name FROM employees

14.21.

Matching a Single Character

197

WHERE Name LIKE %Jr. </cfquery> <cfdump var=#juniors#>

Comments
The LIKE operator in SQL allows you to match wildcard patterns against text fields in your database.The first wildcard is the % character, used to denote a string of zero or more characters.Table 14.8 explains the meaning of other wildcards available in SQL. The code in this listing will return any name that ends in Jr., which should find everyone in the database that is a junior.
Table 14.8 Wildcards for the LIKE Operator Wildcard
% _ [xyz] [a-z] [^xyz] [^a-z] * ?

Meaning Match a string of zero or more characters Match any single character Match x, y, or z once Match any character between and including a through z Match any character but x, y, or z Match any character not between a and z Microsoft Access equivalent to % Microsoft Access equivalent to _

Take note that Microsoft Access uses * and ? for % and _ respectively; however, the syntax for [] is the same.

14.21. Matching a Single Character


You want to match any single character in a field.

Technique
Use the _ character with the LIKE operator to match a single character.
<cfquery datasource=cfcookbook name=bobOrRob> SELECT Name FROM employees WHERE Name LIKE _ob </cfquery> <cfdump var=#bobOrRob#>

198

Chapter 14

Structured Query Language (SQL)

Comments
Because the _ character will match just one character, this query will match Bob or Rob. The query will not match Robert, because the pattern only accepts three character names.The pattern _ob% would match Robert.

14.22. Matching a Group of Characters


You want to match a set of possible characters in a field.

Technique
Use the [] characters with the LIKE operator to match a range of characters.
<cfquery datasource=cfcookbook name=aNames> SELECT Name FROM employees WHERE Name LIKE [Aa]% </cfquery> <cfdump var=#aNames#>

Comments
The [] bracket characters are used to hold several characters that can occur in the position that the brackets appear in the pattern.This query matches names that begin with A or a.

14.23. Matching a Range of Characters


You want to match within a range of characters in a field.

Technique
Use the [] characters with a - (dash) in the LIKE operator to match a range of characters.
<cfquery datasource=cfcookbook name=aToK> SELECT Name FROM employees WHERE Name LIKE [A-Ka-k]% </cfquery> <cfdump var=#aToK#>

14.25.

Excluding a Range of Characters

199

Comments
This query matches any name that begins with any letter, within the range A through K (in upper- or lowercase).This effectively matches the first half of the alphabet.The dash - character is used to represent the range.

14.24. Excluding a Group of Characters


You want to match any characters, except those specified, to a field.

Technique
Include the ^ character at the beginning of the bracketed text to exclude characters from the pattern.
<cfquery datasource=cfcookbook name=noBobs> SELECT Name FROM employees WHERE Name LIKE [^Bb]ob% </cfquery> <cfdump var=#noBobs#>

Comments
This query will match Rob, but will not match Bob or bob.This query will match any name that doesnt start with B or b followed by ob.

14.25. Excluding a Range of Characters


You want to match any characters, except those within a specified range, to a field.

Technique
Include the ^ character within the brackets before a range to exclude the range from the pattern.
<cfquery datasource=cfcookbook name=noNumbers> SELECT Name FROM employees WHERE Name LIKE [^0-9]% </cfquery> <cfdump var=#noNumbers#>

200

Chapter 14

Structured Query Language (SQL)

Comments
This query returns all names that do not start with a number.The ^ character is used to exclude the range from the pattern.

14.26. Using Prepared Statements


You want to improve database performance on frequently executed queries without storing code on the database.

Technique
Use the <cfqueryparam> tag to specify a bind parameter.
<cfquery datasource=cfcookbook name=myQuery> SELECT Name, Age, Salary FROM employees WHERE EmployeeID = <cfqueryparam cfsqltype=CF_SQL_INTEGER value=#EmployeeID#> </cfquery>

Comments
When a SQL statement is to be processed by the database engine, it must first parse the SQL statement to create an execution plan.The execution plan is created based on how the table is stored internally, which fields are indexed, and so on. Because this operation takes valuable milliseconds, several databases store a cache of execution plans. Most queries executed in ColdFusion applications have dynamic variables in them.
SELECT Name, Age, Salary FROM employees WHERE EmployeeID = #EmployeeID#

The variable EmployeeID can potentially represent thousands of values. Each time this query is executed with a different EmployeeID, your database server sees it as a different query. Although technically each query is in fact different, you can see that the query is the same except for the variables. Fortunately, there is a way to relay this information to the database so it can cache just one query, instead of thousands of slightly different queries. Typically, the databases that maintain a cache of execution plans also allow you to tell the database where dynamic variables are in the SQL so that it can cache just one execution plan.This is where the performance benefits come in to play. Smaller caches mean efficient retrieval, and because the cache size is finite, they allow all possible queries to be cached. SQL statements that notify the database where variables exist in

14.26.

Using Prepared Statements

201

the query are known as prepared statements.The actual syntax to bind parameters to the SQL can vary between vendors, but ColdFusion allows you to write vendor-neutral queries with the <cfqueryparam> tag. At a minimum, you should pass the <cfqueryparam> tag a CFSQLTYPE, which is used for data type validation, and a value.There are several data types that you can use, outlined in Table 14.9.
Table 14.9 CFSQLTYPES for CFQUERYPARAM CFSQLTYPE
CF_SQL_BIGINT CF_SQL_BIT CF_SQL_BLOB CF_SQL_CHAR CF_SQL_CLOB CF_SQL_DATE CF_SQL_DECIMAL CF_SQL_DOUBLE CF_SQL_FLOAT CF_SQL_IDSTAMP CF_SQL_INTEGER CF_SQL_LONGVARCHAR CF_SQL_MONEY CF_SQL_MONEY4 CF_SQL_NUMERIC CF_SQL_REAL CF_SQL_REFCURSOR CF_SQL_SMALLINT CF_SQL_TIME CF_SQL_TIMESTAMP CF_SQL_TINYINT CF_SQL_VARCHAR

Besides adding a performance boost, using <cfqueryparam> also improves security with its data type validation.The security vulnerability that it protects against is a serious one, and is covered in detail in Appendix B, Security. Even if your database doesnt support prepared statements, its still a good idea to use <cfqueryparam> in all queries for its security protection. Using <cfqueryparam> has many of the same benefits as using stored procedures; however, they allow your SQL code to be stored within the application code (CFML). Bind parameters are a simple way you can increase your query performance and security

202

Chapter 14

Structured Query Language (SQL)

without implementing stored procedures. Stored procedures do however offer much better security and performance benefits; they are discussed in Chapter 15, Stored Procedures.

15
Stored Procedures
15.0. Introduction
Beginning and intermediate developers often avoid writing stored procedures. Although they can be very complex, in most cases they dont take much more work than a SQL query.The potential benefits of using stored procedures outweigh the extra work it takes to implement them. Stored procedures allow you to encapsulate database queries and other logic into one procedure.The procedure may take input parameters, and may return a result, similar to ColdFusion user-defined functions. Procedures are stored on the database server (instead of on your application server CFM files), hence the name stored procedures. Stored procedures can be written in several languages. Microsoft SQL Server supports stored procedures written in C++ and C#; Oracle and DB2 support stored procedures written in Java. Stored procedures are typically written in a language that is specific to the database server.Transact-SQL (or TSQL) is commonly used with Microsoft SQL Server, whereas PL/SQL is frequently used with Oracle. All that flexibility is great, but youre probably thinking: Whats in it for me? It turns out that stored procedures have several benefits, including enhanced performance, better security, and code reuse. Stored procedures increase query performance in two waysfirst by caching query execution plans for the procedure and second by reducing communication between the database and your application. Stored procedures can also improve your overall application performance by including business logic that might have ended up in your ColdFusion code. The code to call a procedure is usually much smaller than the SQL it will execute; this reduces the traffic sent to the database server. Because stored procedures can manipulate multiple queries, and only return the data you need, they can eliminate multiple

204

Chapter 15

Stored Procedures

trips to the server. Streamlined communication between the database server and application server improves performance, especially in the presence of simultaneous requests. Stored procedures make it easy to enforce a tight security policy on the database server. Database users can be restricted to execute only a few stored procedures rather than execute SELECT, INSERT, UPDATE, or DELETE commands.The use of stored procedures also eradicates the possibility of URL injection hacking (covered in Appendix B, Security.). If youre using stored procedures, you will never have to write the same SQL query twice.The same stored procedure can be used across all applications accessing the database, making code reuse a benefit that makes your job easier. Because procedures are stored on the database server, changes to table or column names can easily be propagated to the procedures on the server. If your application makes use of stored procedures, you can even change database engines without changing a single line of ColdFusion code. Hopefully, by now you can see why you should start using stored procedures if your database supports them. Because stored procedures can do just about anything, this chapter keeps it simple. You will learn enough to convert current queries to stored procedures.To do more complicated tasks, you need to get a book written specifically for your database engine. The examples in this chapter use the database tables presented in the introduction of Chapter 14, Structured Query Language (SQL).

15.1. Writing a Simple Stored Procedure in Microsoft SQL Server


You want to create a stored procedure in Microsoft SQL Server.

Technique
Use a CREATE PROCEDURE statement to define a stored procedure.
CREATE PROCEDURE giveRaise AS UPDATE employees SET Salary = Salary * 1.05

Comments
The CREATE PROCEDURE statement tells the database that you are defining a new procedure, similar to how the <cffunction> tag tells ColdFusion that you are defining a new function. The name of the procedure is specified after CREATE PROCEDURE. Because the SQL will not be stored in your CFML file, stored procedure names should be descriptive enough so that you can easily tell what the procedure is doing without looking at the

15.2.

Writing a Simple Stored Procedure in Oracle

205

procedure code.This will improve the readability of your ColdFusion code. Chapter 14 talked about the AS keyword, and how it could be used to make aliases to table and column names. Here, AS is used a little differently; its used to mark the beginning of the procedure body. Inside the procedure body, you can simply write SQL statements.The SQL statement in the giveRaise stored procedure gives a five percent raise to all employees. This stored procedure only took one more line of code than its SQL counterpart.The rest of the procedures in this chapter wont be as easy as this one; however, they also wont be much more difficult. So forget all your unfounded fears of stored procedures and lets roll!

15.2. Writing a Simple Stored Procedure in Oracle


You want to create a stored procedure in Oracle.

Technique
Use the CREATE OR REPLACE along with AS, BEGIN, and END to define a stored procedure in Oracle.
CREATE OR REPLACE AS BEGIN PROCEDURE giveRaise

UPDATE employees SET Salary = Salary * 1.05; END;

Comments
The syntax for creating the giveRaise stored procedure in Oracle differs slightly from the Microsoft SQL Server equivalent.The first difference is the OR REPLACE part of the CREATE PROCEDURE statement. OR REPLACE tells the database to replace the procedure definition for giveRaise if one already exists in the database. OR REPLACE is optional; however, it is handy for testing and making changes to stored procedures. Stored procedure definitions in Oracle use the BEGIN and END keywords to define the beginning and the end of the procedure body. Oracle also uses the AS keyword just like Microsoft SQL Server. Although the code for the giveRaise procedure is fairly similar on Oracle and Microsoft SQL Server, as your procedures begin to do more, they will differ quite a bit between database engines.

206

Chapter 15

Stored Procedures

15.3. Invoking a Simple Stored Procedure


You want to call an existing stored procedure in a database.

Technique
Use the <cfstoredproc> tag to invoke a stored procedure and use the <cfstoredproc> tag to assign the result of the procedure to a query object.
<cfstoredproc datasource=cfcookbook procedure=getEmployeeNames> <cfprocresult name=employeeNames> </cfstoredproc> <b>Employees:</b> <ul> <cfoutput query=employeeNames> <li>#employeeNames.Name#</li> </cfoutput> </ul> <cfoutput>#employeeNames.recordcount#</cfoutput> Employee Names returned.

Here is the getEmployeeNames procedure:


CREATE PROCEDURE getEmployeeNames AS SELECT Name FROM employees

Comments
In this technique, you define a new stored procedure with Microsoft SQL Server syntax that returns a result set.The procedure returns the same result as the first SQL query in section 1 of Chapter 14the names of all the employees. This example executes the getEmployeeNames procedure with the <cfstoredproc> tag. It uses <cfstoredproc> in its simplest form, by simply specifying the datasource and procedure name.The <cfstoredproc> tag has many of the same attributes as the <cfquery> tag; a complete listing of attributes can be found in Table 15.1. Another way of executing stored procedures is by using T-SQLs EXECUTE command. The getEmployeeNames procedure can be called from EXECUTE with this SQL statement: EXECUTE getEmployeeNames.

15.4.

Writing a Stored Procedure with Parameters in Microsoft SQL Server

207

Table 15.1 <cfstoredproc> Attributes Attribute


procedure datasource username password blockfactor debug returncode

Notes The name of the procedure to execute. The datasource that contains the procedure. The database username. The database password. The maximum number of rows to return from the server in one block. Default is 1. Can be used to improve performance. If true, displays debugging information for the procedure. If true, sets the value of the cfstoredproc.statuscode variable to the procedures native return code.

The <cfprocresult> tag allows you to assign a variable name to the query object returned from the stored procedure.There is nothing special about the query object returned from the procedure, treat it no differently than you would treat a query object returned from the <cfquery> tag. All of the attributes for <cfprocresult> can be found in Table 15.2.
Table 15.2 <cfprocresult> attributes Attribute
name resultset maxrows

Notes The name of a variable to assign the result set query to. Used to identify a result set when more than one result sets are returned from a procedure.The default value is 1. The maximum number of rows to return from the procedure.The default value is 1, which allows an unlimited number of rows to be returned.

When you access a stored procedure from ColdFusion, the database user ColdFusion is using must have EXECUTE or EXEC permission for that stored procedure.

15.4. Writing a Stored Procedure with Parameters in Microsoft SQL Server


You want to pass input values to your procedure in SQL Server.

Technique
Use a list of @variableName datatype values to define the input parameters of your procedure.
CREATE PROCEDURE addEmployee @name varchar(50),

208

Chapter 15

Stored Procedures

@age int, @salary money, @deptID int AS INSERT INTO employees (Name, Age, Salary, DepartmentID) VALUES(@name, @age, @salary, @deptID)

Comments
Input parameters are defined similarly to how you might define arguments for a function.The variable name is prefixed with an @ symbol, and followed by a data type. Unlike ColdFusion functions, stored procedures require that you specify a data type for the input parameters. CFML is a loosely typed language, but several languages, such as Java and C#, are not.You need to be concerned about types in databases because other languages besides CFML can potentially access the same procedure. The SQL statement refers to the variable with the @ prefix, similar to how you wrap variables with # symbols inside the <cfoutput> tag. Notice that you dont need to wrap single quotes around the @name variable because the stored procedure knows its data type.

15.5. Writing a Stored Procedure with Parameters in Oracle


You want pass input values to your procedure in Oracle.

Technique
Enclose the parameters in parentheses after the procedure name to define input parameters in Oracle.
CREATE OR REPLACE PROCEDURE addEmployee (name IN varchar2, age IN number, salary IN number, deptID IN number) AS BEGIN INSERT INTO employees (Name, Age, Salary, DepartmentID) VALUES(name,age,salary, deptID); END;

15.6.

Invoking a Stored Procedure with Parameters

209

Comments
The parameters in an Oracle stored procedure are enclosed in parentheses. Unlike Microsoft SQL Server, @ signs are not used to prefix variable names. The IN keyword occurs after the variable name. Chapter 14 discussed how the IN keyword can be used to specify a list of possible values for a particular column. Here, you use IN to define the parameter as an input parameter. Note that the IN keyword, depending on its context, can be used for two completely different purposes in PL/SQL. The data type of the parameter is placed after the IN keyword. Variables in PL/SQL dont use an identifier like the @ symbol in T-SQL, so when you use parameters in this SQL, it can be confusing as to which words are variables and which words are part of the SQL. In this technique, Name is used twice in the SQL statement (they use different case, but PL/SQL is not case sensitive). Fortunately, Oracle is smart enough to know that the first instance of Name is referring to a column name, and the second to a variable name.

15.6. Invoking a Stored Procedure with Parameters


You want to pass variables into your stored procedure.

Technique
Use the <cfprocparam> tag to pass variables into the stored procedure.
<cfif IsDefined(form.name)> <!--- make sure all form variables are defined ---> <cfparam name=form.age type=numeric default=0> <cfparam name=form.salary type=numeric default=0> <cfparam name=form.departmentID type=numeric default=0> <!--- run the addEmployee procedure ---> <cfstoredproc datasource=cfcookbook procedure=addEmployee> <cfprocparam value=#form.name# cfsqltype=CF_SQL_VARCHAR type=in> <cfprocparam value=#form.age# cfsqltype=CF_SQL_INTEGER type=in> <cfprocparam value=#form.salary# cfsqltype=CF_SQL_MONEY type=in> <cfprocparam value=#form.departmentID# cfsqltype=CF_SQL_INTEGER type=in> </cfstoredproc> Employee Added. </cfif> <!--- get list of departments: Name, and DepartmentID ---> <cfstoredproc datasource=cfcookbook procedure=getDepartments> <cfprocresult name=depts> </cfstoredproc>

210

Chapter 15

Stored Procedures

<b>Add New Employee</b> <form method=post> Name: <input type=text name=name><br> Age: <input type=text name=name size=3><br> Salary: <input type=text name=name><br> Department: <select name=departmentID> <cfoutput query=depts> <option value=#depts.DepartmentID#>#depts.Name#</option> </cfoutput> </select> <br> <input type=submit value=Add Employee> </form>

Here is the getDepartments procedure:


CREATE PROCEDURE getDepartments AS SELECT DepartmentID, Name FROM departments

Comments
This example calls the addEmployee procedure defined in sections 15.4 and 15.5, which has four input parameters. Parameters are passed to stored procedures by placing <cfprocparam> tags inside the <cfstoredproc> tag.The <cfprocparam> tags should be placed in the same order as the parameters are defined in the stored procedure. The Name, Age, Salary, and DepartmentID form variables are passed into the value attribute of the <cfprocparam> tag.The cfsqltype attribute allows ColdFusion to validate the data type passed in to the value attribute. A listing of all cfsqltype data types is available in Table 14.9.The type attribute for <cfprocparam> tells ColdFusion that the parameter in this case is an input parameter.Youll learn about output parameters in the next section.Table 15.3 lists all attributes for the <cfprocparam> tag.
Table 15.3 <cfprocparam> Attributes Attribute
cfsqltype type value variable

Notes The data type of the parameter. See Table 14.9 for a list of possible values.
IN, OUT, or INOUT, depending on whether the parameter is passing in a vari-

able, setting a variable, or doing both. The value of an IN or INOUT parameter. The variable name for the result of an OUT or INOUT parameter.

15.7.

Using Output Parameters

211

Table 15.3 Continued Attribute


maxlength scale null

Notes The maximum length of a parameter. The number of decimal places to scale the parameter to. Used to pass a NULL value in an input parameter.The value attribute will be ignored if this attribute is set to true.

To return all the DepartmentIDs and department names from the departments table, the example uses another stored procedure called getDepartments.The getDepartments procedure is used in the add employee form to construct a list of all possible departments.

15.7. Using Output Parameters


You want your stored procedure to return a variable.

Technique
Use the OUTPUT keyword to declare a parameter as an output parameter, and then retrieve the value of the variable with <cfprocparam> by setting the type attribute to out.
CREATE PROCEDURE addEmployee @name varchar(50), @age int, @salary money, @deptID int, @EmployeeID int OUTPUT AS INSERT INTO employees (Name, Age, Salary, DepartmentID) VALUES(@name, @age, @salary, @deptID) SELECT @EmployeeID = @@IDENTITY

<cfif IsDefined(form.name)> <!--- make sure all form variables are defined ---> <cfparam name=form.age type=numeric default=0> <cfparam name=form.salary type=numeric default=0> <cfparam name=form.departmentID type=numeric default=0> <!--- run the addEmployee procedure ---> <cfstoredproc datasource=cfcookbook procedure=addEmployee> <cfprocparam value=#form.name# cfsqltype=CF_SQL_VARCHAR type=in>

212

Chapter 15

Stored Procedures

<cfprocparam value=#form.age# cfsqltype=CF_SQL_INTEGER type=in> <cfprocparam value=#form.salary# cfsqltype=CF_SQL_MONEY type=in> <cfprocparam value=#form.departmentID# cfsqltype=CF_SQL_INTEGER type=in> <cfprocparam variable=EmpID cfsqltype=CF_SQL_INTEGER type=OUT> </cfstoredproc> Your Employee ID is <cfoutput>#EmpID#</cfoutput>. </cfif> <!--- get list of departments: Name, and DepartmentID ---> <cfstoredproc datasource=cfcookbook procedure=getDepartments> <cfprocresult name=depts> </cfstoredproc> <b>Add New Employee</b> <form method=post> Name: <input type=text name=name><br> Age: <input type=text name=name size=3><br> Salary: <input type=text name=name><br> Department: <select name=departmentID> <cfoutput query=depts> <option value=#depts.DepartmentID#>#depts.Name#</option> </cfoutput> </select> <br> <input type=submit value=Add Employee> </form>

Comments
By adding the OUTPUT keyword to the end of a parameter declaration in the stored procedure in T-SQL, the parameters value can be read by ColdFusion after the query has executed.The new addEmployee procedure returns the employee ID of the employee who was added by the procedure.This is done by setting the output variable @EmployeeID to the built-in variable @@IDENTITY, after the INSERT statement.The @@IDENTITY variable holds the value of the employee ID generated automatically by the INSERT statement. The <cfprocparam> tag tells ColdFusion that you want to read the value of this parameter after the procedure executes by setting the type attribute to out.The variable attribute of <cfprocparam> allows you to assign a ColdFusion variable name to the parameter so you can use it later in your code. In Oracle PL/SQL, the OUT keyword is used instead of OUTPUT.The keyword is placed before the data type in the parameter definition.

15.8.

Working with Multiple Result Sets

213

15.8. Working with Multiple Result Sets


You want to use a stored procedure that returns more than one result set.

Technique
Use multiple <cfprocresult> tags to assign names to each result set.
CREATE PROCEDURE getDepartmentsAndProjects AS SELECT Name FROM departments SELECT Name FROM projects <cfstoredproc datasource=cfcookbook procedure=getDepartmentsAndProjects> <cfprocresult name=dept resultset=1> <cfprocresult name=proj resultset=2> </cfstoredproc> <cfdump var=#dept# label=Department Names> <cfdump var=#proj# label=Project Names>

Comments
Stored procedures may return more than one result set upon execution, involving fewer trips to the database server. Such a model can be more efficient than using multiple procedures to return multiple result sets. Such a procedure is defined in this section.The getDepartmentsAndProjects stored procedure is defined using SQL Server syntax to return two result sets.The first result set lists the department names and the second is a list of project names.The result sets are returned in the order that they are defined. You can apply a variable name to each result set using <cfprocresult> tags.The resultset attribute of the <cfprocresult> tag tells ColdFusion which set to assign the variable name to.
Note
Because the default value of the resultset attribute is 1, the attribute is not required on the first <cfprocresult> tag. It is however a good idea to use the resultset attribute for readability when using multiple result sets.

V
Language Extensibility Features
16 17 18 19 20 21 22 23 24 User-Defined Functions ColdFusion Custom Tags CFX API Custom Tags Components Objects WDDX XML Web Services J2EE Interoperability

16
User-Defined Functions
16.0. Introduction
User-defined functions (UDFs) were one of the most anticipated features of ColdFusion 5. It didnt take long, however, for ColdFusion developers to discover the limitations of user-defined functions in ColdFusion 5. User-defined functions in ColdFusion 5 had to be defined within <cfscript> tags.This meant that your user-defined functions couldnt use any ColdFusion tags, only functions. In ColdFusion 5, your creativity was limited to the extent of the built-in function library. With the introduction of the <cffunction> tag in ColdFusion MX, developers can leverage all the features of ColdFusion within UDFs.The <cffunction> tag allows you to use tags within your user-defined function. It also allows you to enforce data-type validation, and the existence of input arguments, as well as enforcing the data type of return values. Because of the improvements of <cffunction>, you will use its syntax exclusively in this chapter.The syntax for creating <cfscript> user-defined functions is covered in Appendix D, Converting Between Tags and Scripting. Although user-defined functions are powerful, there are certain tasks that are best left to the other language constructs, such as ColdFusion custom tags (covered in Chapter 17, ColdFusion Custom Tags) and ColdFusion components (covered in Chapter 19 Components). User-defined functions work well for manipulating a variable and returning some translation of that variable. For instance, you might create a UDF called removeSpaces that removes all spaces from a string. User-defined functions dont work as well for generating HTML content. For instance, rather than creating a user-defined function to display a copyright message, you might create a custom tag or <cfinclude> a template. Also rather than developing several related user-defined functions to perform similar operations on the same piece of data, you can create a ColdFusion component.The benefit of using a component in this case is that the shared data can persist through several function calls.

218

Chapter 16

User-Defined Functions

16.1. Declaring a Function


You want to define a simple function.

Technique
Use the <cffunction> tag to define a user-defined function. Use <cfreturn> to return a value from the function.
<cffunction name=IsIE returntype=boolean> <cfif CGI.USER_AGENT contains MSIE> <cfreturn true> <cfelse> <cfreturn false> </cfif> </cffunction> <cfif IsIE()> You Are using Microsoft Internet Explorer! <cfelse> You Are not using Microsoft Internet Explorer! </cfif>

Comments
This example created a user-defined function that returns true if the client browser is Microsoft Internet Explorer and false otherwise. The <cffunction> tag tells ColdFusion that you are declaring a function; inside the tag resides the CFML code that is executed when the function is called. <cffunction> has several attributes but only name is required.The name attribute, as you might expect, defines the name you use to call your user-defined function. Just like variable names in ColdFusion, function names are not case sensitive. Function names follow the same rules as variable names; the only limitation is that you cannot define a function with the same name as an existing function (both built-in, and user-defined functions). Although name is the only required attribute, its a good idea to use the returntype attribute. If your UDF does not return anything, you can set returntype equal to void.The return type can also be set to any of the values listed in Table 16.1.
Table 16.1 Function Data Types Return Type
string boolean

Meaning Covers all simple variables such as numbers, booleans, and lists, as well as plain text strings. A string or number that can be reduced to true or false.

16.1.

Declaring a Function

219

Table 16.1 Continued Return Type


numeric struct query variableName void array date binary any guid uuid componentName

Meaning Any string that can be evaluated as a number. A ColdFusion structure. A ColdFusion query object. Any string that matches the rules for a ColdFusion variable name (no spaces, and so on). The function does not return a value. A ColdFusion array. A ColdFusion date object. Binary data. Anything; this is the default. A globally unique identifier. A universally unique identifier. The name of any ColdFusion component.

Table 16.2 contains a complete listing of the attributes for the <cffunction> tag.
Table 16.2 <cffunction> Tag Attributes Attribute
name returntype

Meaning The name of the function; required. The data type that the function will return, see Table 16.1 for possible values. Required when implementing a Web service, and highly recommended otherwise. When set to true, the contents of the <cffunction> tag are effectively wrapped with a <cfoutput> tag.When false, <cffunction> wraps the content with a <cfsilent> tag. Note the ColdFusion documentation specifies that this attribute only works when <cffunction> is used in a ColdFusion component; however, we have found it to work for UDFs as well. A list of role names that have permission to execute the function. See Chapter 13, User Authentication and Authorization, for more information on roles. Used in components; valid values are private, public, package, and remote. See Chapter 19 for more information. A string that describes the function; used only in ColdFusion components.

output

roles

access hint

When you have a result to return from the function, you use the <cfreturn> tag. Returning variables is described in detail in section 3 of this chapter. How do you call a UDF? By its name of course. UDFs are called just like you do a built-in ColdFusion function such as IsDefined() or Len().

220

Chapter 16

User-Defined Functions

16.2.

Function Arguments

You want to pass an input value to your function.

Technique
Use the <cfargument> tag to define function arguments.
<cffunction name=Split returntype=array> <cfargument name=str type=string required=true> <cfargument name=size type=numeric required=false default=5> <cfset pos = 1> <cfset strLen = Len(arguments.str)> <cfset result = ArrayNew(1)> <cfloop condition=pos LTE strLen> <cfset subString = Mid(arguments.str, pos, arguments.size)> <cfset ArrayAppend(result, subString)> <cfset pos = pos + arguments.size> </cfloop> <cfreturn result> </cffunction> <cfdump var=#Split(1234567890)# label=Result of split function>

Comments
The <cfargument> tag allows you to define not only the name of an argument, but also its data type, default values, and an option to specify whether the argument is required. Because you cannot specify such options with UDFs in <cfscript>, this is one of the reasons its good to use the <cffunction> syntax.The attributes for the <cfargument> tag are defined in Table 16.3.
Table 16.3 <cfargument> Tag Attributes Return Type
name type required default

Meaning The name of the argument; required. The data type that the input value must be; see Table 16.2 for possible values. This value defaults to any, but is highly recommended. If true, the argument must be present when the function is called. If false, the attribute can be omitted. The value to use when the argument was not passed into the function.

16.2.

Function Arguments

221

The example Split function takes a string, and optionally a size, and returns an array of substrings that are of the size specified.The size argument is not required and the default size is 5, so when you call the split with the string 1234567890, it returns an array with two elements the first 012345 and the second 67890. As you might have assumed, the order of <cfargument> tags is important.The order that the tags show up is the same as the order that arguments are used in the UDF call. The value of an argument can be accessed using structure dot notation (arguments.argumentName), and also as an array. Using arguments as an array allows you to write functions that take in an unknown number of arguments.The following code shows the Split function illustrating this point:
<cffunction name=SuperSplit returntype=array> <cfargument name=str type=string required=true> <cfset pos = 1> <cfset result = ArrayNew(1)> <cfset argLen = ArrayLen(arguments)> <cfset strLen = Len(arguments.str)> <!--- they didnt pass in any numbers, return entire string ---> <cfif argLen LT 2> <cfset ArrayAppend(result, arguments.str)> <cfreturn result> </cfif> <cfloop from=2 to=#argLen# index=i> <cfset subStringLength = arguments[i]> <cfset subString = Mid(arguments.str, pos, subStringLength)> <cfset ArrayAppend(result, subString)> <cfset pos = pos + subStringLength> </cfloop> <!--- if there are leftovers add them to the end ---> <cfif pos LT strLen> <cfset ArrayAppend(result, Right(arguments.str, strLen-pos+1))> </cfif> <cfreturn result> </cffunction> <!--- split up a phone number into an array ---> <cfdump var=#SuperSplit(555-555-1324,3,1,3,1,4)# label=Result of SuperSplit UDF>

The SuperSplit function allows you to pass in multiple lengths at which to split the string.This example passes in a phone number, 555-555-1234, and the lengths 3, 1, 3, 1, and 4.The result is an array with elements 555, -, 555, -, and 1234.This function would allow you to easily reformat the phone number or any other data that is stored in a known format.

222

Chapter 16

User-Defined Functions

16.3. Returning Values


You want the function to return a value.

Technique
Use the <cfreturn> tag to return a result from a user-defined function.
<cffunction name=IsIE returntype=boolean> <cfreturn CGI.USER_AGENT contains MSIE> </cffunction>

<cfif IsIE()> You Are using Microsoft Internet Explorer! <cfelse> You Are not using Microsoft Internet Explorer! </cfif>

Comments
This version of the IsIE function does the same thing as the version presented in section 1 of this chapter; however, the code used to implement it is much shorter.The <cfreturn> tag can take any expression or variable and return it to the functions caller. In IsIE, you can use the expression CGI.USER_AGENT contains MSIE inside the <cfreturn> tag because its result is effectively the intended result of the IsIE UDF.

16.4. Suppressing Output


You want to suppress output in your custom function from appearing in your response.

Technique
Use the output attribute of the <cffunction> tag to control output generated by the function.
<cffunction name=noOutputAttribute returntype=void> <cfset x = Value> #x# </cffunction> <cffunction name=outputTrue returntype=void output=true> <cfset x = Value>

16.5.

Recursive Functions

223

#x# </cffunction> <cffunction name=outputFalse returntype=void output=false> <cfset x = Value> #x# </cffunction> <cfoutput> #noOutputAttribute()# <br> #outputTrue()# <br> #outputFalse()# </cfoutput>

Comments
This example defines three functions, all with the same body, yet each function outputs a different result.The difference in each function is the value or presence of the output attribute. Lets explore what each function yields. The first function, noOutputAttribute, doesnt specify a value for the output attribute in the <cffunction> tag. The body of the function is executed just as you might expect; it simply prints the string #x# because you didnt place the <cfoutput> tag around the variable. The next function, outputTrue, sets the output attribute to a value of true.The output of this function is Value.When output is true, the <cffunction> tag automatically wraps a <cfoutput> around the body of the function, thus allowing variables to be evaluated. The third function does not produce any output because output is false.When output=false, <cffunction> wraps the function body with <cfsilent> tags to suppress output. Note that all these functions specify a return type of void, meaning they dont return anything. However, they can output a value to the response. Because functions can both output a value and return a value, its best to always use output=false, and stick to custom tags or CFML templates for generating output.

16.5. Recursive Functions


You want to write a function that calls itself.

Technique
Make a call to the function from within the function.
<cffunction name=Power returntype=numeric> <cfargument name=base type=numeric required=true>

224

Chapter 16

User-Defined Functions

<cfargument name=exponent type=numeric required=true> <cfif arguments.exponent LT 0> <cfthrow type=NegativeExponentException message=You cannot pass a negative exponent to the power function.> <cfelseif arguments.exponent EQ 0> <!--- base case ---> <cfreturn 1> <cfelse> <!--- recursive step ---> <cfreturn arguments.base * Power(arguments.base, arguments.exponent-1)> </cfif> </cffunction> 2<sup>3</sup> = <cfoutput>#Power(2,3)#</cfoutput>

Comments
This example defines a function called Power that can be used to calculate the value of a base number raised to an exponent. For example, to calculate two cubed (23), two is passed in as the base, and three is passed as the exponent.The value is calculated by multiplying 2 * 2 * 2 or by multiplying 22 * 21 * 20. The first step to creating a recursive function is to find the base case.The base case is a simple sub-problem that you can solve.This problem has two potential base cases; you know that x1 is always x, and that x0 is always 1.This algorithm uses x0 as the base case. Next, you need to define the recursive step.This step keeps recursively calling itself with different values until the base case is reached. In the Power function, this is achieved by decrementing the exponent and calling the Power function again. Like many recursive problems, this problem can be solved iteratively as follows:
<cffunction name=Power returntype=numeric> <cfargument name=base type=numeric required=true> <cfargument name=exponent type=numeric required=true> <cfif arguments.exponent LT 0> <cfthrow type=NegativeExponentException message=You cannot pass a negative exponent to the power function.> <cfelseif arguments.exponent EQ 0> <cfreturn 1> <cfelse> <cfset var result = 1> <cfloop from=1 to=#arguments.exponent# index=i> <cfset result = result * arguments.base> </cfloop> <cfreturn result> </cfif> </cffunction> 2<sup>3</sup> = <cfoutput>#Power(2,3)#</cfoutput>

16.6.

Working with Function References

225

Although many recursive problems are difficult to convert to iterative ones, this one was fairly easy. Recursion is typically used because the recursive solution to a problem is easier to find than an iterative one. Iterative solutions typically perform better than recursive solutions due to the overhead of calling a function over and over.The build up of this overhead can often lead to a common problem when using recursive functions, called a stack overflow. Each time you call a function, the function call is placed on the call stack. As you might imagine, when a recursive function is calling itself thousands of times, this call stack can fill up quickly. One solution to this problem in many languages is to create a function that is tail recursive, meaning the last line in the function is the recursive step (the recursive Power function is tail recursive).Tail recursive functions can be removed from the stack after they are called, thus avoiding a stack overflow. In ColdFusion, tail recursive functions can still cause a stack overflow, so be careful. Try running the recursive Power function with a very large exponent; ColdFusion will report the stack overflow.

16.6. Working with Function References


You want to store a reference to a function in a variable.

Technique
Set a variable to the name of a user defined function.
<cffunction name=IsIE returntype=boolean> <cfreturn CGI.USER_AGENT contains MSIE> </cffunction> <cfset ie = IsIE> <cfif ie()> We are using Internet Explorer <cfelse> We are not using Internet Explorer </cfif>

Comments
When you define a function, a local variable is created using the name of the function. The variable holds a reference to the function.The function reference can be assigned to another variable. In this example, the IsIE function was defined and a reference to the IsIE function is copied to the variable ie in this line:
<cfset ie = IsIE>

226

Chapter 16

User-Defined Functions

When function references are copied to different variables, they are copied by reference, not by value.When a variable is copied by value, an entire copy of the object is copied; when a variable is copied by reference, only a reference (an indicator to the original objects location) to the object is copied.This point is illustrated in the following example:
<cffunction name=A> <cfreturn A> </cffunction> <cfset reference = A> <cfoutput>#reference()#</cfoutput><br> <cffunction name=B> <cfreturn B> </cffunction> <cfset reference = B> <cfoutput>#reference()#</cfoutput><br> <cfset B = A> <cfoutput>#reference()#</cfoutput><br> <cfoutput>#B()#</cfoutput><br>

This example outputs the following:


A B B A

The crux of this example occurs when a reference to the function A is copied to the variable B.
<cfset B = A>

The resulting output shows two things. First it shows that the variable B is simply a reference to the function B and not the function itself. Second it shows that the variables are copied by reference and not by value, because the third line of output displays a B and not an A. References to user defined functions can be stored in different scopes such as application, server, session, and so fourth. However, when a function is defined it must not have a dot (.) in its name.The following code will throw an exception:
<cffunction name=application.IsIE returntype=boolean> <cfreturn CGI.USER_AGENT contains MSIE> </cffunction>

16.6.

Working with Function References

227

The proper way to store a reference to a function in a scope other than the default (the variables scope) is to copy the reference as follows:
<cffunction name=IsIE returntype=boolean> <cfreturn CGI.USER_AGENT contains MSIE> </cffunction> <cfset application.IsIE = IsIE>

17
ColdFusion Custom Tags
17.0. Introduction
Good programming languages should encourage code isolation and encapsulation. By dissecting your application into small, highly specialized pieces, you can make it easier to maintain, extend, and debug. ColdFusion custom tags are the oldest means to encapsulate code in the CFML programming language. Prior to ColdFusion 5.0, custom tags provided the best (and, for the most part, only) way to group common logic into a reusable code component. Newer versions of ColdFusion have added other extensibility mechanisms. ColdFusion 5.0 introduced custom functions to the CFML language and ColdFusion MX includes support for ColdFusion Components (see Chapter 19, Components). Despite these advances in CFML, scripted ColdFusion custom tags are the simplest and most versatile way to create application building blocks.This chapter explores ColdFusion custom tags.Youll write some tags and look at how these tags can be used within client templates.Youll also look at topics such as data exchange and the nesting of custom tags.

17.1. Creating a Simple Custom Tag


You want to create a simple custom tag.

Technique
Create a custom tag by creating a regular ColdFusion template called helloWorldTag.cfm:
Hello World!

230

Chapter 17

ColdFusion Custom Tags

Comments
Any ColdFusion template can be treated as a custom tag.There is no limit to the functionality that your custom tag can implement.The example here shows the simplest of ColdFusion templates; the template merely outputs the string Hello World!. This template can be used as a custom tag if it is placed in the CustomTags folder in the ColdFusion installation directory or in the same directory as the calling template. The tag created in the previous example is called helloWorldTag.

17.2. Accessing a Custom Tag


You want to access an existing custom tag from your ColdFusion application.

Technique
Execute a custom tag by using the <cf_[tagname]> call syntax.
<html> <body> Calling the HelloWorld custom tag... <cf_helloWorldTag> </body> </html>

Comments
The template in this example acts as a client to the HelloWorldTag custom tag. Note that you must use the cf_ prefix when calling any custom tag.The result of invoking the HelloWorldTag custom tag is the insertion of the string Hello World into the calling template.

17.3.

Using Attributes

You want to pass CFML data structures to your custom tag.

Technique
Use the Attributes structure to pass arguments to a custom tag. greetingTag.cfm:
<cfparam name=Attributes.name default=you> <cfoutput> Hello, #Attributes.name# </cfoutput>

17.4.

Returning a Result

231

greetingClient.cfm:
<cfparam name=name default=Tom> <html> <body> Calling greeting custom tag... <cf_greetingTag name=#name#> </body> </html>

Comments
Attributes are the best way to communicate with your custom tag. In theory, a custom tag can use the Caller variable scope to look for variables.This is not good style, though, because it creates a tight coupling between the client and the custom tag and inhibits the tags reuse in other applications. Using attributes in your custom tag is extremely simple. ColdFusion creates an implicit Attributes collection that your custom tag can query to find and extract mandatory and/or optional attributes. In the file greetingTag.cfm, the custom tag uses the <cfparam> tag to initialize the optional name attribute. It is a good idea to use the <cfparam> tag to identify all of the custom tags mandatory and/or optional attributes. Declare mandatory attributes by using the <cfparam> tag without the default option to cause ColdFusion to throw an error if the user fails to specify the attribute. All optional attributes should be initialized with <cfparam> to some default value, such as the empty string. Attributes can subsequently be referenced in the custom tag template by using the Attributes prefix. Passing attributes from a client to a custom tag is even easier. All you need to do is specify the attribute name and value in the custom tag reference. For example, the template greetingClient.cfm calls the <cf_greetingTag> custom tag and passes in the name attribute with the following line of code:
<cf_greetingTag name=#name#>

17.4. Returning a Result


You want to return a result from your custom tag.

Technique
getDomainTag.cfm:
<cfparam name=Attributes.email> <cfparam name=Attributes.name>

232

Chapter 17

ColdFusion Custom Tags

<cfscript> result = GetToken(Attributes.email,2,@); SetVariable(Caller.&Attributes.name,result); </cfscript>

getDomainClient.cfm:
<cfparam name=email default=somebody@yourdomain.com> <html> <body> <cf_getDomainTag email=#email# name=domain> <cfoutput> The domain for the email address #email# is #domain#. </cfoutput> </body> </html>

Comments
Most custom tags are not meant to output content. Usually, a custom tag performs an operation and returns the result to the calling template.The calling template can choose to display the result, or not.This practice was particularly common before custom functions were released in ColdFusion 5.0. A common design pattern for returning results is the addition of a required name or variable attribute to the custom tag.This variable contains the name of a variable in the Caller scope where the custom tag should place its return result. Any ColdFusion programmer who has used the <cfquery> tag should recognize this concept.

17.5. Adding Start and End Tags


Your custom tag requires a body.

Technique
biu.cfm:
<cfif ThisTag.executionmode is end> <cfset ThisTag.GeneratedContent = <b><i><u>#ThisTag.GeneratedContent#</u></i></b>> </cfif>

biuClient.cfm:
<html> <body>

17.5.

Adding Start and End Tags

233

The following text has been formatted by the biu custom tag... <cf_biu> <cfoutput> Todays date is #DateFormat(Now(),mm/dd/yyyy)#. </cfoutput> </cf_biu> </body> </html>

Comments
ColdFusion Custom Tags can have both a start and an end tag.This feature enables you to perform operations on the content between the start and end tags. In such cases, ColdFusion will call the custom tag twiceonce when it encounters the start tag and again when it encounters the end tag. ColdFusion creates an implicit ThisTag structure that contains four variables to help the custom tag determine its context.Table 17.1 shows the contents of the ThisTag structure.
Table 17.1 ThisTag Variable Contents Key
AssocAttribs ExecutionMode GeneratedContent

Value An array of attributes contained in associated nested tags. Indicates whether the tag has been called in reference to the start tag or end tag.Values are start or end. The evaluated form of all content that has been embedded within the start and end tags.The custom tag can change this variable, affecting its own output. A Boolean that indicates whether the tag has an associated end tag. This variable is most often used for sanity checking. A custom tag that expects both start and end tags should throw an error if the value of HasEndTag is false.

HasEndTag

Custom Tags that have both start and end tags should first check the ExecutionMode variable. If the value is start, the tag should check for mandatory and optional attributes, initializing and undefined optional attributes. If the value of the variable is end, the custom tag should implement the primary algorithm. The biu.cfm custom tag represents a significant addition to the CFML language. The <cf_biu/> tag makes all embedded content bold, italicized, and underlined.The custom tag explicitly sets its GeneratedContent variable to implement this functionality.The expression <cfset ThisTag.GeneratedContent = <b><i><u>#ThisTag.GeneratedContent#</u></i></b>> wraps any content contained within the start and end tags with the HTML <b/>, <i/>, and <u/> tags.

234

Chapter 17

ColdFusion Custom Tags

17.6. Building Nested Tags


You want to use nested custom tags for increased flexibility.

Technique
mailto.cfm:
<cfparam name=Attributes.address> <cfassociate basetag=cf_massmailer dataCollection=addresses>

massmailer.cfm:
<cfparam name=Attributes.from> <cfparam name=Attributes.subject> <cfparam name=Attributes.server> <!--check for executionmode = end in order to make sure we execute the body of this tag *after* processing all nested sub-tags ---> <cfif ThisTag.executionmode is end> <cfset addressarray = ArrayNew(1)> <cfif isArray(ThisTag.addresses)> <cfloop from=1 to=#ArrayLen(ThisTag.addresses)# index=i> <cfset attr = ThisTag.addresses[i]> <cfset emailaddr = attr.address> <cfset addressarray[i] = emailaddr> </cfloop> </cfif> <cfmail to=#ArrayToList(addressarray, )# from=#Attributes.from# subject=#Attributes.subject# server=#Attributes.server#>#ThisTag.GeneratedContent#</cfmail> <cfset ThisTag.GeneratedContent = > </cfif>

massmailerClient.cfm:
<cf_massmailer from=somebody@mydomain.com subject=test server=smtp.mydomain.com> <cf_mailto address=p1@somedomain.com> <cf_mailto address=p2@somedomain.com> <cf_mailto address=p3@somedomain.com> <cf_mailto address=p4@somedomain.com> <cf_mailto address=p5@somedomain.com>

17.7.

Encoding Custom Tags

235

<cf_mailto address=p6@somedomain.com> Message Text </cf_massmailer>

Comments
Nesting custom tags can be an elegant way to pass dynamic information.This approach is often more practical than specifying a long, unwieldy attribute list. ColdFusion implements nested custom tags via the <cfassociate> CFML tag. <cfassociate> links a child tags attribute list to its parent tag. By default, the child tags attributes are placed into the parents ThisTag.AssocAttribs array of structures. However, the child tag may also specify to the parent attribute where its attributes should be placed.The mailto.cfm example uses the dataCollection attribute of the <cfassociate> tag to specify that its attribute lists are found in the variable ThisTag.addresses of the parent tag. The examples shown in this section implement a custom tag called <cf_massmailer> that accepts one or more nested <cf_mailto> sub-tags.The intent of this example is to implement a wrapper for the <cfmail> tag (or any other mail engine) that is designed to send one email to a large recipient list. By standardizing on this custom tag, you can radically change the implementation of the underlying messaging system without disturbing its call semantics. The mailto.cfm template contains the implementation of the <cf_mailto> child tag.This tag is used to specify a single email address to which the email should be sent. The user can nest one or more of these tags within the <cf_massmailer> custom tag. The logic behind the <cf_mailto> tag is straightforward; it must only associate its address attribute with the parent <cf_massmailer> tag. The massmailerClient.cfm template shows how easy it is to use the <cf_massmailer> tag and its associated <cf_mailto> tag.The message text can appear anywhere within the <cf_massmailer> start and end tags.

17.7. Encoding Custom Tags


You want to encode your tag for distribution so that others cannot see its source code.

Technique
Use the cfencode.exe command to encrypt a custom tag.
c:\CFusionMX\bin\cfencode.exe biu.cfm biu_encoded.cfm /h Bold, Italicize, Underline /v 2

236

Chapter 17

ColdFusion Custom Tags

Comments
You may have purchased or downloaded a prepackaged custom tag from the ColdFusion Developers Exchange. If so, there is a pretty good chance that the tag you downloaded is encrypted so that its source cannot be copied or modified illegally.You might wish to encode your own custom tags for distribution in a similar manner. Fortunately, ColdFusion provides a utility that you can use to encrypt your own ColdFusion templates. The cfencode.exe program, included in the bin directory of your ColdFusion distribution, is a command-line tool that reads a ColdFusion source file, encrypts it, and writes it back to disk. Once encrypted, the ColdFusion template can be deployed and executed on any ColdFusion server, but it can no longer be modified. This example uses the cfencode.exe utility to encrypt the biu.cfm custom tag. The commands syntax is as follows:
cfencode.exe infile [outfile] [/r] [/q] [/h header] /v 2

Table 17.2 provides a description of the commands arguments.


Table 17.2 cfencode.exe Arguments Argument
infile outfile /r /q /h /v

Description Mandatory. One or more source files to encode. Optional.The name of the new file(s) to be written. Can be left blank for inplace encoding. Optional. Recursively process directories. Optional. Suppress warning messages. Optional. Include a custom header message in the encoded file. Mandatory.The encryption version number to be used.

17.8. Using <cfmodule>


You want to call a custom tag regardless of its location.

Technique
<cfparam name=name default=Elizabeth> <cfset attrs = StructNew()> <cfset attrs.name = Elizabeth> <cfmodule template=./greetingTag.cfm attributeCollection=#attrs#>

17.9.

Using <cfimport>

237

Comments
The <cfmodule> tag allows you to invoke a custom tag by explicitly stating the template to use.The tag requires you to specify the template attribute, which spells out a relative template path for the custom tag. Alternatively, you can use the tags name attribute to provide a dot-delimited directory path to the custom tag template, relative to the ColdFusion Custom Tags directory. For example, the command:
<cfmodule name=com.mycompany.util.UtilTag>

would execute the following template:


<ColdFusion Install Directory>/CustomTags/com/mycompany/util/UtilTag.cfm

Attributes can be passed to a custom tag called by <cfmodule> in one of two ways. Individual attributes can be named by using the attribute_<attribute name> naming convention. For example, the command:
<cfmodule template=util.cfm attribute_name=Steve attribute_email=steve@mycompany.com>

would call the util.cfm template, passing in the attributes name and email. You can also use the attributeCollection attribute of the <cfmodule> tag to communicate a custom tags attributes.The same custom tag can be called with the following statements:
<cfset attrs = StructNew()> <cfset attrs.name = Steve> <cfset attrs.email = steve@mycompany.com> <cfmodule template=util.cfm attributeCollection=#attrs#>

The attributeCollection attribute will work with any custom tag that you call, even if you are calling the tag with the <cf_ prefix instead of with <cfmodule>.This is a useful technique when the attributes you want to set are not known until runtime.

17.9. Using <cfimport>


You want to use the Java Server Pages prefix notation to access groups of JSP or ColdFusion custom tags.

Technique
<cfparam name=name default=Alex> <cfimport prefix=utiltags taglib=taglib> <utiltags:greetingTag name=#name#>

238

Chapter 17

ColdFusion Custom Tags

Comments
<cfimport> is a new tag in ColdFusion MX that enables you to import CFML Custom Tag directories or JSP Tag Libraries and call them with a JSP-like prefix notation.The tag has one required attribute, taglib, which is a tag library URI.The URI can be one of the following: a directory containing custom tags, a URL to a JAR file in a Web application, or a path to a JSP tag library descriptor (a .TLD file).The tag also accepts an optional prefix attribute that assigns a prefix to the collection of tags in order to distinguish them from other tag namespaces. This example imports a directory called taglib.The directory contains some of the custom tag examples reviewed in this chapter.The example assigns the prefix utiltags to the custom tags found in the taglib directory.These tags can now be called directly with a utiltags: prefix. For example, the template calls the greetingTag custom tag with the following expression:
<utiltags:greetingTag name=#name#>

The big benefit to the <cfimport> tag is that it allows you to leverage any JSP tag libraries that are available to you. If you intend to import JSP tag libraries, you should give serious thought to using the <cfimport> tag to import all of your ColdFusion custom tags as well, because doing so gives your code a more consistent look.

18
CFX API Custom Tags
18.0. Introduction
CFX tags allow you to utilize APIs (Application Programming Interfaces) available to Java and C++ developers from within ColdFusion.The CFX API allows you to create your own tags that are executed on the server by ColdFusion. CFX tags can be written in either Java or C++; however, its optimal to use Java whenever possible for several reasons.The first reason is that Java CFX tags dont need to be recompiled or rewritten for each operating system on which they are executed.When Java code is compiled, it is stored in a machine-independent format that can be executed on any machine that has a Java runtime environment installed (ColdFusion MX installs a Java Runtime Environment during its installation process).When C++ is compiled, on the other hand, it is stored in a platform-specific format. Also because ColdFusion MX is written in Java, it can execute a Java CFX tag with much less overhead than it can a C++ tag. Although Java is the preferred language on ColdFusion MX, sometimes there are tasks that are much easier completed with C++ than with Java. For example, interacting with the server operating system is often much easier with C++. In the Enterprise version of ColdFusion MX, you can create JSP custom tag libraries to perform many of the tasks that Java CFX tags perform.With JSP tag libraries, you can create nested tags and tags that use the tag body content.The API for creating JSP tag libraries is more robust than the CFX API. If you do decide to use JSP custom tags, keep in mind that Java CFX tags can be used on ColdFusion 4.0 and up, whereas JSP tags only work on the Enterprise versions of ColdFusion MX. JSP custom tag libraries are covered in Chapter 24, J2EE Interoperability. This chapter examines four functionally different CFX API custom tags; each tag is implemented in both Java and C++. Before reading this chapter, you might want to review Java, C++, and object-oriented programming if you need to.

240

Chapter 18

CFX API Custom Tags

18.1. Creating a Simple Java CFX Tag


You want to create a ColdFusion custom tag using the Java programming language.

Technique
Implement the com.allaire.cfx.CustomTag interface to create a CFX custom tag. Use the com.allaire.cfx.Response.write() method to print output to the response.
<b>Memory Status:</b><br> <CFX_WriteMemoryStatus>

Heres the CFX_WriteMemoryStatus.java file:


import com.allaire.cfx.*; public class CFX_WriteMemoryStatus implements CustomTag { public void processRequest(Request request, Response response) throws Exception { Runtime thisJVM = Runtime.getRuntime(); long freeMem = thisJVM.freeMemory(); freeMem /= 1024; //convert from bytes to KB String strFreeMem = String.valueOf(freeMem); //convert to a string //write the value to the response response.write(strFreeMem); response.write(KB of free memory in the JVM.<br>); long totalMem = thisJVM.totalMemory(); totalMem /= 1024; //convert from bytes to KB String strTotalMem = String.valueOf(totalMem); //convert to a string response.write(strTotalMem); response.write(KB of memory total in the JVM.<br>); } }

Comments
What is a Java CFX tag? The answer can be explained to any experienced Java developer with one sentence: any class that implements the com.allaire.cfx.CustomTag

18.2.

Using Tag Attributes

241

interface. Interfaces are a means to define a standard for the structure of a Java class; they are essentially a list of methods that a class must define.When a class meets the standard, it is said to implement the interface.The interface itself contains no functional code, just the method declarations. For a class to implement the com.allaire.cfx.CustomTag interface, it must provide a definition for a method with the following method prototype public void
processRequest(com.allaire.cfx.Request, com.allaire.cfx.Response) throws java.lang.Exception.When ColdFusion executes the CFX tag, the processRequest method is called. The processRequest method is passed two objects by ColdFusiona request object and a response object.The request object contains methods to get information about the tag request (such as the tag attributes).The response object provides

methods to print messages or set variables. In the CFX_WriteMemoryStatus tag, the write method prints strings containing information about the memory status to the HTTP response output stream.Table 18.1 shows the methods of the Response interface. To compile the Java file, use the javac compiler that comes with the JDK (Java Developers Kit).When you compile the tag, the cfx.jar file (located in the lib directory of your ColdFusion installation) must be in the classpath.This can be done by adding the JAR file to your classpath environment variable, or by adding the classpath switch to javac. javac will create a .class file (section 9 of this chapter explains how to register the CFX tag).
Table 18.1 The com.allaire.cfx.Response Interface Method
void write(String value) void setVariable (String varName, String value) Query addQuery (String name, String[] columns)

Meaning Writes the string to the callers output stream. Sets a variable in the calling ColdFusion template. Seesection 3 of this chapter for more information. Adds a query to the calling template with a specified name and specified columns. Returns a reference to the Query. See section 4 of this chapter for more information. Writes a string to the debug output stream.

void writeDebug(String val)

18.2. Using Tag Attributes


You want to read input from the tag attributes inside the Java CFX tag.

Technique
Use the attributeExists, getAttribute, and getIntAttribute methods of the com.Allaire.cfx.Request object to read the tag attributes.

242

Chapter 18

CFX API Custom Tags

<cfset str = SubString> <CFX_SubString string=#str# start=3> <CFX_SubString string=#str# start=0 end=3>

Heres the CFX_SubString.java file:


import com.allaire.cfx.*; public class CFX_SubString implements CustomTag { public void processRequest(Request request, Response response) throws Exception { String theString; int startPos; if (!request.attributeExists(STRING)) { throw new Exception(Required attribute STRING is missing); } if (!request.attributeExists(START)) { throw new Exception(Required attribute START is missing); } theString = request.getAttribute(STRING); startPos = request.getIntAttribute(START); //END is an optional attribute if (request.attributeExists(END)) { theString = theString.substring(startPos, request.getIntAttribute(END)); } else { theString = theString.substring(startPos); } response.write(theString); } }

Comments
The CFX_SubString tag reads a string passed in through the string attribute and returns a subset of the string that starts at the position specified by the start attribute. The substring then ends either at the end of the string or at the position specified by the end attribute.

18.2.

Using Tag Attributes

243

Because this tag is useless without a string to operate on, the string attribute must exist.You can determine whether an attribute was passed by using the attributeExists method of the request object.The attributeExists method takes in a string that represents the attribute name, and returns true or false depending on whether the attribute does indeed exist. To get the value passed into the attribute, you use the getAttribute method if you want a string, or the getIntAttribute method if you want an integer. Both methods take the name of the attribute for the first argument; getIntAttribute can also take a default value if the attribute doesnt exist or if the attribute cant be converted to an integer. One big limitation of the getAttribute method is that it can return only strings. There is no way to pass a structure or array to a CFX tag without first serializing it to WDDX (See Chapter 21, WDDX, for more information about WDDX) or to another string representation. The attribute names passed into the getAttribute, getIntAttribute, or attributeExists methods are not case sensitive.Table 18.2 shows a listing of the methods in the Request interface.
Table 18.2 The com.allaire.cfx.Request Interface Method
boolean attributeExists (String name) String getAttribute (String name) int getIntAttribute (String name, int default)

Meaning Determines whether a particular attribute was passed to the tag. Returns the value of the specified attribute. Returns the integer value of an attribute.You can optionally specify a default value; used if the attribute value does not exist or cannot be converted to an int. Returns a reference to the query passed in to the QUERY attribute of the tag. Returns an array containing the names of all attributes passed into the tag. Returns true if the attribute debug is present in the tag.

Query getQuery() String[] getAttributeList() boolean debug()

Note
Exceptions thrown from within the processRequest method are propagated to the calling CFML template.

244

Chapter 18

CFX API Custom Tags

18.3. Setting ColdFusion Variables


You want to set a ColdFusion variable from within the Java CFX tag.

Technique
Use the setVariable method in the response object to set a ColdFusion variable within a Java CFX tag.
<CFX_GetMemoryStatus variable=memStruct> <cfdump var=#memStruct# label=Memory Status> <!--- convert to mega bytes ---> <cfset freeMem = (memStruct.freeMemory / 1024) / 1024> <cfoutput>#Round(freeMem)#MB of free Memory.</cfoutput>

Heres the CFX_GetMemoryStatus.java file:


import com.allaire.cfx.*; public class CFX_GetMemoryStatus implements CustomTag { public void processRequest(Request request, Response response) throws Exception { Runtime thisJVM = Runtime.getRuntime(); long freeMem = thisJVM.freeMemory(); String strFreeMem = String.valueOf(freeMem); //convert to a string long totalMem = thisJVM.totalMemory(); String strTotalMem = String.valueOf(totalMem); //convert to a string if (!request.attributeExists(VARIABLE)) { throw new Exception(Required attribute VARIABLE missing); } String structName = request.getAttribute(VARIABLE); response.setVariable(structName + .freeMemory, strFreeMem); response.setVariable(structName + .totalMemory, strTotalMem); } }

18.4.

Using Queries in Java CFX Tags

245

Comments
Although being able to write variables to the HTTP response is handy at times, its often a better practice to set a variable and let the calling CFML template display or manipulate it at will.The setVariable method allows you to do just that. In the CFX_GetMemoryStatus tag, you use an attribute called variable to represent the name of a structure that holds the memory variables that you set. When youre ready to set the variable, you use the setVariable method in the response object. setVariable takes two arguments, both of which must be strings. The first argument is the name of the variable and the second argument is the value of the variable.
Note
In versions of ColdFusion prior to MX, variable names with a . (dot) were not structures, and there was no way to create structures from a CFX tag.

18.4. Using Queries in Java CFX Tags


You want to read and create a query object from within your Java CFX tag.

Technique
Use the getQuery method to get an input query, and use addQuery to create a new query.
<CFX_ReverseQuery query=#someQuery# result=reversedQuery> <cfdump var=#reversedQuery# label=Reversed>

Heres the CFX_ReverseQuery.java file:


import com.allaire.cfx.*; public class CFX_ReverseQuery implements CustomTag { public void processRequest(Request request, Response response) throws Exception { Query inputQuery = request.getQuery(); if (inputQuery == null) { throw new Exception(Required attribute QUERY missing.); } if (!request.attributeExists(RESULT)) {

246

Chapter 18

CFX API Custom Tags

throw new Exception(Required attribute RESULT missing.); } String resultQueryName = request.getAttribute(RESULT); String[] columns = inputQuery.getColumns(); Query result = response.addQuery(resultQueryName, columns); int rowCount = inputQuery.getRowCount(); //reverse the query for(int r=rowCount;r>0;r--) { int row = result.addRow(); for (int c=1;c<=columns.length;c++) { String data = inputQuery.getData(r, c); result.setData(row, c, data); } } } }

Comments
The CFX_ReverseQuery tag is passed a query and returns a copy of the query in reverse order. Queries in Java CFX tags are represented by the com.allaire.cfx.Query interface.The Query interface provides methods to modify and add data to the query.These methods are listed in Table 18.3.
Table 18.3 The com.allaire.cfx.Query Interface Method
int addRow() String[] getColumns() int getRowCount() String getData(int row, int column) void setData (int row, int column, String data) String getName() int getColumnIndex(String name)

Meaning Adds a row to the query and returns the index of the new row. Returns an array of column names. Returns the number of rows in the query the recordcount. Gets the contents of a cell in the query specified by a row index and column index. Sets the value of a cell specified by a row index and a column index. Returns the name of the query. Returns the column index by column name.

18.5.

A Simple C++ CFX Tag

247

CFX tags allow only one query to be passed to the tag; the query should be passed with the query attribute.You can retrieve a reference to the query by calling the request objects getQuery() method. To add a new query to the calling CFML template, use the addQuery method in the response object. addQuery takes two argumentsthe query name and an array of column names. A reference to the new query is returned from addQuery. Once you have a reference to the query, you can find some information about it.To get an array of columns, call the getColumns() method.The number of rows in the query is returned by the getRowCount() method.With this information, you can loop through the query and copy the data in to the new query in reverse order. To add a row to the query, call the addRow() method. It returns the index of the row that was added. Next, you copy the data to the new row, and then call the getData() method to extract the contents in the original query object. By passing getData() a row and a column, youll get a string representing the data in that cell. You then set the cell in the new query with the setData() method, which takes a row, a column, and a string.

18.5. A Simple C++ CFX Tag


You want to create a simple CFX custom tag using the C++ programming language.

Technique
Write a ProcessRequest function that takes a pointer to a CCFXRequest object.
#include stdafx.h #include windows.h #include cfx.h // Standard MFC libraries // Windows Functions, GlobalMemoryStatus // CFX Custom Tag API

void ProcessTagRequest( CCFXRequest* pRequest ) { try { CString buffer; MEMORYSTATUS stat; //get memory statistics GlobalMemoryStatus(&stat); //free physical memory unsigned long freeMem = stat.dwAvailPhys; freeMem /= 1024; //convert from bytes to KB //convert long to string ltoa(freeMem, buffer.GetBuffer(16), 10);

248

Chapter 18

CFX API Custom Tags

pRequest->Write(buffer); pRequest->Write(KB of physical memory is free.<br>); //free physical memory unsigned long totalMem = stat.dwTotalPhys; totalMem /= 1024; //convert from bytes to KB //convert long to string ltoa(totalMem, buffer.GetBuffer(16), 10); pRequest->Write(buffer); pRequest->Write(KB of physical memory total.<br>); } // Catch Cold Fusion exceptions & re-raise them catch( CCFXException* e ) { pRequest->ReThrowException( e ) ; } // Catch ALL other exceptions and throw them as // Cold Fusion exceptions (DO NOT REMOVE! -// this prevents the server from crashing in // case of an unexpected exception) catch( ... ) { pRequest->ThrowException( Error occurred in tag CFX_WRITEMEMORYSTATUS, Unexpected error occurred while processing tag. ) ; } }

Comments
The easiest way to create a CFX tag in C++ is by using the project wizard in Microsoft Visual C++.The wizard will set up the required compiler settings and create the needed files. As mentioned in the introduction, C++ CFX tags on UNIX need to be recompiled on the platform on which they will run.To compile a tag for UNIX, look at the makefiles included in the directory list CFX tag (found in the cfx directory of your ColdFusion installation). C++ code tends to be platform-specific. CFX_WriteMemoryStatus happens to only work on Windows because it is calling Windows-specific functions.The C++ implementation of CFX_GetMemoryStatus (discussed in section 7) uses the same functions and also will not work on UNIX.The remaining C++ CFX tags should work on UNIX with very little modification.

18.5.

A Simple C++ CFX Tag

249

All language differences aside, the API for creating C++ CFX tags is very similar to the Java CFX tag API.The main difference is that there is no Response object, only a Request object that has all the methods that the Response object does in the Java API. A pointer to a CCFXRequest object is passed in to the ProcessTagRequest function.With the pointer, you invoke the Write method to output to the HTTP response. Notice that in the C++ implementation of the CFX_WriteMemoryStatus, you can determine the total amount of physical memory, but the Java implementation reports the amount of memory in the JVM only. You can use the CFML code from the technique discussed in section 1 to test the output of CFX_WriteMemoryStatus in the C++ implementation. The methods in the CCFXRequest class are outlined in Table 18.4.
Table 18.4 The CCFXRequest Abstract Class Method
void Write(LPCSTR value) void SetVariable (LPCSTR varName, LPCSTR value)

Meaning

Writes the string to the callers output stream. Sets a variable in the calling ColdFusion template. CCFXQuery* AddQuery(LPCSTR Adds a query to the calling template with a name, CCFXStringSet* columns) specified name and specified columns. Returns a pointer to the query. void WriteDebug(LPCSTR val) Writes a string to the debug output stream. BOOL AttributeExists(LPCSTR name) Determines whether an attribute was passed into the tag. LPCSTR GetAttribute(LPCSTR name) Returns the value of the specified attribute. CCFXQuery* GetQuery() Returns a pointer to the query passed in to the QUERY attribute of the tag. CCFXStringSet* GetAttributeList() Returns a pointer to CCFXStringSet containing the names of all attributes passed into the tag. BOOL Debug() Returns true if the attribute debug is present in the tag. void ThrowException(LPCSTR error, ) Throws a ColdFusion exception.
LPCSTR diagnostics void ReThrowException ( CCFXException* e) CCFXStringSet* CreateStringSet()

Re-throws a caught exception. Allocates and returns a pointer to a string set; memory will be automatically cleaned up by ColdFusion.

250

Chapter 18

CFX API Custom Tags

18.6. Using Tag Attributes


You want to read input from the tag attributes inside the C++ CFX tag.

Technique
Use the AttributeExists and GetAttribute methods on the CCFXRequest object.
#include stdafx.h #include cfx.h #include <string> // Standard MFC libraries, remove on unix // CFX Custom Tag API // Use STL string for unix compatibility

void ProcessTagRequest( CCFXRequest* pRequest ) { try { if (!pRequest->AttributeExists(STRING)) { pRequest->ThrowException(Error occurred in tag CFX_SUBSTRING, Required attribute STRING missing ) ; } if (!pRequest->AttributeExists(START)) { pRequest->ThrowException(Error occurred in tag CFX_SUBSTRING, Required attribute START missing ) ; } std::string str = pRequest->GetAttribute(STRING); std::string strStart = pRequest->GetAttribute(START); int iStart = atoi(strStart.c_str()); if (pRequest->AttributeExists(END)) { std::string strEnd = pRequest->GetAttribute(END); int iEnd = atoi(strEnd.c_str()); str = str.substr(iStart, iEnd); } else { str = str.substr(iStart); } pRequest->Write(str.c_str()); } // Catch Cold Fusion exceptions & re-raise them catch( CCFXException* e ) {

18.7.

Setting ColdFusion Variables

251

pRequest->ReThrowException( e ) ; } // Catch ALL other exceptions and throw them as // Cold Fusion exceptions (DO NOT REMOVE! -// this prevents the server from crashing in // case of an unexpected exception) catch( ... ) { pRequest->ThrowException( Error occurred in tag CFX_SUBSTRING, Unexpected error occurred while processing tag. ) ; } }

Comments
The AttributeExists method checks for the existence of attributes.The GetAttribute method is used to return a LPCSTR (a pointer to a character array) representation of the attribute value.The C++ API does not have a GetIntAttribute method like the Java API does, so you have to handle the string-to-integer conversion yourself. This example uses the Standard Template Library (STL) string class to represent strings, because CString is part of the Microsoft Foundation Classes (MFC) and not very UNIX-friendly. STL strings typically work well across different platforms.

18.7. Setting ColdFusion Variables


You want to set a ColdFusion variable from within in the C++ CFX tag.

Technique
Use the SetVariable method to set a ColdFusion variable.
#include stdafx.h #include cfx.h #include windows.h // Standard MFC libraries // CFX Custom Tag API // Windows Functions, GlobalMemoryStatus

void ProcessTagRequest( CCFXRequest* pRequest ) { try { CString buffer; MEMORYSTATUS stat;

252

Chapter 18

CFX API Custom Tags

if (!pRequest->AttributeExists(VARIABLE)) { pRequest->ThrowException( Error occurred in tag CFX_GETMEMORYSTATUS, Required attribute VARIABLE missing. ) ; } CString var = pRequest->GetAttribute(VARIABLE); //get memory statistics GlobalMemoryStatus(&stat); //free physical memory unsigned long freeMem = stat.dwAvailPhys; //convert long to string ltoa(freeMem, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .freeMemory, buffer); //total physical memory unsigned long totalMem = stat.dwTotalPhys; //convert long to string ltoa(totalMem, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .totalMemory, buffer); //more windows specific memory info unsigned long availablePageFile = stat.dwAvailPageFile; ltoa(availablePageFile, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .availablePageFile, buffer); unsigned long availableVirtual = stat.dwAvailVirtual; ltoa(availableVirtual, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .availableVirtual, buffer); unsigned long memoryLoad = stat.dwMemoryLoad; ltoa(memoryLoad, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .memoryLoad, buffer); unsigned long totalPageFile = stat.dwTotalPageFile; ltoa(totalPageFile, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .totalPageFile, buffer); unsigned long totalVirtual = stat.dwTotalVirtual; ltoa(totalVirtual, buffer.GetBuffer(16), 10); pRequest->SetVariable(var + .totalVirtual, buffer); } // Catch Cold Fusion exceptions & re-raise them

18.8.

Using Queries in C++ CFX Tags

253

catch( CCFXException* e ) { pRequest->ReThrowException( e ) ; } // Catch ALL other exceptions and throw them as // Cold Fusion exceptions (DO NOT REMOVE! -// this prevents the server from crashing in // case of an unexpected exception) catch( ... ) { pRequest->ThrowException( Error occurred in tag CFX_GETMEMORYSTATUS, Unexpected error occurred while processing tag. ) ; } }

Comments
The SetVariable method can be used to set a variable in the calling ColdFusion page. It takes two arguments, both of type LPCSTR. The first argument is the variable name and the second is the value. In the C++ implementation of CFX_GetMemoryStatus, you can get much more information about the memory, including virtual memory and page file information. This is due to the close relationship between C++ and the operating system.

18.8. Using Queries in C++ CFX Tags


You want to read and create a Query object from within your C++ CFX tag.

Technique
Use GetQuery and AddQuery to get a CCFXQuery object, and then retrieve, add, or edit information about the query with methods in the CCFXQuery object.
#include stdafx.h #include cfx.h #include <string> // Standard MFC libraries, remove for unix // CFX Custom Tag API // Use STL String for unix

void ProcessTagRequest( CCFXRequest* pRequest ) { try { CCFXQuery * pInputQuery = pRequest->GetQuery(); if (pInputQuery == NULL) { pRequest->ThrowException(

254

Chapter 18

CFX API Custom Tags

Error occurred in tag CFX_REVERSEQUERY, Required attribute QUERY missing. ) ; } if (!pRequest->AttributeExists(RESULT)) { pRequest->ThrowException( Error occurred in tag CFX_REVERSEQUERY, Required attribute RESULT missing. ) ; } std::string strResult = pRequest->GetAttribute(RESULT); CCFXStringSet * pColumns = pInputQuery->GetColumns(); int iColumns = pColumns->GetCount(); int iRows = pInputQuery->GetRowCount(); CCFXQuery * pResultQuery = pRequest->AddQuery(strResult.c_str(), pColumns);

//reverse the query for(int r=iRows;r>0;r--) { int row = pResultQuery->AddRow(); for (int c=1;c<=iColumns;c++) { std::string strData = pInputQuery->GetData(r, c); pResultQuery->SetData(row, c, strData.c_str()); } } } // Catch Cold Fusion exceptions & re-raise them catch( CCFXException* e ) { pRequest->ReThrowException( e ) ; } // Catch ALL other exceptions and throw them as // Cold Fusion exceptions (DO NOT REMOVE! -// this prevents the server from crashing in // case of an unexpected exception) catch( ... ) { pRequest->ThrowException(

18.8.

Using Queries in C++ CFX Tags

255

Error occurred in tag CFX_REVERSEQUERY, Unexpected error occurred while processing tag. ) ; } }

Comments
A pointer to the query passed into the QUERY attribute of the tag is retrieved by the GetQuery method. If a query is not passed to the tag, GetQuery will return NULL; otherwise, it returns a pointer to a CCFXQuery object.The methods of the CCFXQuery abstract class are explained in Table 18.5. One difference between the CCFXQuery abstract class and the com.allaire.cfx. Query interface (in Java) is the GetColumns method. In Java, getColumns returns an array of strings; in C++, it returns a pointer to a CCFXStringSet object.The CCFXStringSet abstract class is similar to a list in ColdFusion.
Table 18.5 The CCFXQuery Abstract Class Method
int AddRow() CCFXStringSet* GetColumns() int GetRowCount() LPCSTR GetData (int row, int column) void SetData(int row, int column, LPCSTR data) LPCSTR getName() int getColumnIndex (LPCSTR name)

Meaning Adds a row to the query and returns the index of the added row. Returns a pointer to a collection of column names. Returns the number of rows in the query; the recordcount. Gets the contents of a cell in the query specified by a row index and column index. Sets the value of a cell specified by a row index and a column index. Returns the name of the query. Gets the column index by column name.

256

Chapter 18

CFX API Custom Tags

18.9. Installing a Java CFX Tag


You want to register a Java CFX tag with ColdFusion.

Technique
Use ColdFusion administrators CFX Tags link to register CFX tags. Java classes must be in the classpath.

Comments
So now that you have written a CFX tag, you would like to use it in your ColdFusion application, or at least test it to see if its working. In order to use a CFX tag in CFML, the ColdFusion server needs to know where your compiled code resides and which piece of code contains the CFX tag. The process for registering CFX tags is slightly different for C++ and Java tags.This section looks at eachboth boil down to mapping a tag name to a procedure or class. In order for ColdFusion to execute your compiled class file, the class file must be in the classpath.The classpath is not actually a feature of ColdFusion, but rather a feature of the Java Virtual Machine.When the Java Virtual Machine is started, it is given a list of directories and JAR files (the classpath).When the JVM has a request to instantiate a new class, it searches the directories and JAR files in the list to find the .CLASS file corresponding to the class name. The first step to installing a Java CFX tag is adding your CFX class file in your classpath.The directory <coldfusionroot>/wwwroot/WEB-INF/classes is already part of ColdFusions classpath.You can add additional directories or JAR files to the classpath in ColdFusion Administrator with the Java and JVM link. Be sure not to put your class files in subdirectories under your classpath. Java treats directories as package names, so if you put MyCFX.class in a subdirectory called cfx, Java can only access the class by the name cfx.MyCFX, and MyCFX must be compiled with package cfx; as the first line in the code.
Note
The classpath setting in the ColdFusion MX Administrator only allows a classpath of a limited number of characters. You can manually edit your classpath by editing the java.class.path property in the <coldfusionroot>/runtime/bin/jvm.config file.

Once the JVM can find your class, you have to let ColdFusion know what tag name you want to map to your class.To do this, click the CFX Tags link in ColdFusion Administrator, and then click the Register Java CFX button. Simply enter a tag name (CFX_MyTag) and a class name (MyCFX). Class names are case sensitive, but tag names are not.You can optionally enter a description of the tag.The page for registering CFX tags is shown in Figure 18.1.

18.10.

Installing a C++ CFX Tag

257

Figure 18.1 Registering a Java CFX tag.

18.10. Installing a C++ CFX Tag


You want to register a C++ CFX tag with ColdFusion.

Technique
Use ColdFusion administrators CFX Tags link to register CFX tags.

Comments
Registering C++ CFX tags is actually a little bit easier than registering Java CFX tags because you dont have to worry about classpaths or packages; you simply point to a DLL (on Windows) or so (on UNIX) file. In the ColdFusion Administrator, click CFX Tags and then click the Register C++ CFX button. Enter the tag name (CFX_MyTag) and the path to the DLL or the file (c:\cfx\MyTag.dll). Although the ColdFusion Administrator allows you to name your procedure something other than ProcessTagRequest, you should stick to the standard.

258

Chapter 18

CFX API Custom Tags

The next option is the Keep Library Loaded check box.When this setting is checked, your CFX code will be retained in RAM, allowing for fast access. Chances are your procedure wont take up much RAM, so you should keep this option checked. Finally, you can optionally enter a description of the CFX tag.The form for adding a C++ CFX tag is shown in Figure 18.2.

Figure 18.2 Registering a C++ CFX tag.

19
Components
19.0. Introduction
ColdFusion components, new in ColdFusion MX, enable developers to create object-like structures in ColdFusion. ColdFusion components, or CFCs, add many of the benefits of object-oriented programming in ColdFusion, without requiring you to learn much new CFML syntax. Some of the benefits of using object-oriented programming include code reuse, a flexible and extensible design, and simplified maintenance. ColdFusion components also have these benefits. ColdFusion components can also be easily used as Web services, thus allowing a high level of interoperability between several platforms.This is covered in Chapter 23, Web Services. ColdFusion components rely heavily on the <cffunction> tag, which is covered in Chapter 16, User-Defined Functions. It would be worthwhile to review the syntax of <cffunction> before delving into this chapter.

19.1. Defining a Component


You want to create a new ColdFusion component (a CFC).

Technique
Use the <cfcomponent> tags with the <cffunction> tags to create a CFC. Here is the component light.cfc:
<cfcomponent> <cfset lightIsOn = false> <cffunction name=switch returntype=void>

260

Chapter 19

Components

<!--- if light is on turn it off, if off turn it on ---> <cfset lightIsOn = NOT lightIsOn> </cffunction> <cffunction name=isLightOn returntype=boolean> <cfreturn lightIsOn> </cffunction> </cfcomponent>

Comments
Before you get too far into the syntax, its important to examine some aspects of objectoriented programming, and understand how it relates to ColdFusion components.The best way to think about objects is to consider a physical object, for instance, a light. A light can be on or off (its property is to emit light).There also needs to be a way to turn the light on and off, a light switchspeaking programmatically, the switch is a function you can call. In object-oriented programming, you can bundle all of the functions and properties (variables) of an object into an entity. Once youve defined the structure of your light, in order to use it you have to instantiate it, or create an instance of it.You can create multiple light objects by using just one definition. Each light instance has its own storage for variables, so each instance can be at its own state. The Light component is created in its own file with the extension .CFC (light.cfc). The <cfcomponent> tags act as containers for defining the component. Local variables defined within the <cfcomponent> tags (such as lightIsOn) are accessible only to functions within the component; they are sometimes referred to as instance variables (because each instance of a component can have a different value stored in the variable). ColdFusion components dont provide all of the features that most object-oriented languages provide. However, you can easily apply object-oriented design principles and patterns when designing ColdFusion components.This is why studying object-oriented design is a worthwhile task for ColdFusion developers.
Note
You can create variables that are accessible outside of the component by using a scope such as application or session. You can also use a special scope called this inside a CFC to make the variable accessible (for example, componentInstance.variableName). This practice might not always lead to the best design.

The fact that only functions within the component can access the local variables is a good thing.You can validate the values to ensure, for example, that a number is always above zero. Additionally, you can have instance variables that developers calling your

19.1.

Defining a Component

261

component cannot access.This concept is often called encapsulation, or data hiding. Encapsulation allows you to change code within the object without changing the code used to call functions on the object. For example, consider light.cfc again, now with a light bulb that dies after a few uses:
<cfcomponent> <cfset switchIsOn = false> <cfset lightBulbIsGood = true> <cfset counter = 0> <cffunction name=switch returntype=void> <cfset counter = counter + 1> <cfif counter GT 3> <cfset lightBulbIsGood = false> </cfif> <cfset switchIsOn = NOT switchIsOn> </cffunction> <cffunction name=isLightOn returntype=boolean> <cfreturn switchIsOn AND lightBulbIsGood> </cffunction> </cfcomponent>

This is an example whereby the internal logic changed, but the external structure remained the same, making it very easy to deploy changes to the internal logic. The <cfcomponent> tag has some optional attributeshint and displayname that can be used to describe your component.The hint attribute provides a hint as to what the component does, and the displayname attribute is used to give the component a friendly name (you can use spaces).These attributes are used by the CFC explorer, the GetMetaData function, and <cfdump>.The GetMetaData function is covered later in section 19.6.The CFC explorer displays the structure of your component in an HTML format.The CFC explorer is invoked by simply entering the URL of the CFC in your Web browser. For security reasons, you might be asked for your RDS or ColdFusion administrator username and password before you can view the component. The <cffunction> tag also has the hint and displayname attributes.These attributes have the same meaning in this context, and show up in the meta data.
Note
Using the hint and displayname attributes in <cfcomponent> and <cffunction> can greatly improve the readability of your code. These attributes also generate useful documentation when components are viewed through the CFC Explorer. For these reasons, it is highly recommended that you take advantage of these optional attributes.

262

Chapter 19

Components

19.2 Instantiating a Component


You want to create an instance of a CFC.

Technique
Use CreateObject or the <cfobject> tag to create an instance of a CFC.
<cfobject component=Light name=lightInstance>

OR
<cfset lightInstance = CreateObject(component, Light)>

Comments
There are two ways to create an instance of a CFCwith the <cfobject> tag or with the CreateObject function. CreateObject and <cfobject> are both functionally equivalent.The only difference is that CreateObject can be used from within the <cfscript> tag. The component attribute of <cfobject> tag takes the name of the CFC (the filename minus .CFC).The name attribute is the name of the variable that holds a reference to the CFC.With a reference to the CFC, you can invoke functions on it. To instantiate a component with the CreateObject function, you pass in the string component as the type of object in the first argument (you can use CreateObject and <cfobject> to create several types of objects such as Java, COM, and CORBA). In the second attribute, name, you pass the name of the CFC. The technique in this section uses the light.cfc file created in the previous technique.The code in this technique should reside in the same directory as the light.cfc file.You can call CFCs that are stored in other directories, but doing so requires a slightly different technique (see section 19.5 of this chapter for an explanation). Once you have a component instance, you can pass the instance to different functions or CFCs.You can also store a reference to a CFC instance in several scopes. The following example uses an application scoped variable to allow the CFC to persist between page requests.When a user clicks the Click Switch button, the switch method is invoked on the light component (method invocation is covered in the next section).
<cfif NOT IsDefined(application.light)> <cfset application.light = CreateObject(component, Light)> </cfif> <cfif IsDefined(form.switch)> <cfset application.light.switch()> </cfif>

19.3.

Invoking a Component Method

263

<cfoutput> The light is currently: <cfif application.light.isLightOn()> On <cfelse> Off </cfif> </cfoutput> <form method=post> <input type=submit name=switch value=Click Switch> </form>

19.3. Invoking a Component Method


You want to invoke methods on a CFC.

Technique
Use dot notation or the <cfinvoke> tag to call a method on a CFC.
<cfobject component=Counter name=counterInstance> <cfoutput> #counterInstance.increment()#<br> #counterInstance.increment()#<br> </cfoutput> <cfinvoke component=#counterInstance# method=increment returnvariable=result> <cfoutput>#result#<br></cfoutput> <!--- creates a new instance ---> <cfinvoke component=Counter method=increment returnvariable=result> <cfoutput>#result#<br></cfoutput>

Heres counter.cfc:
<cfcomponent> <cfset theCounter = 0> <cffunction name=increment returntype=numeric> <cfset theCounter = theCounter + 1> <cfreturn theCounter> </cffunction> </cfcomponent>

264

Chapter 19

Components

Comments
There are four ways to invoke a method (functions in an object are often called methods) in a CFC.The first way requires an instance of a CFC, the other three ways involve using an instance or creating a new instance. The first approach for invoking a method is using dot notation on an instance of a CFC.You use the syntax cfcInstanceName.methodName(arguments).The advantages to this approach are that it takes the least amount of code and works within <cfscript> tags. The next two approaches use the <cfinvoke> tag to invoke the method. Depending on whether you pass in CFC instance or a component name to the component attribute of the <cfinvoke> tag, the result can be different. If you pass in a CFC instance, the values stored in the instance variables (theCounter in this example) are retained. If you pass in a component name to the component attribute, a new instance of the CFC is created.When a new instance of a CFC is created, the instance variable theCounter is reset to zero.The output of the code in this technique is as follows:
1 2 3 1

Notice that the last line is reset to one, because you created a new instance of the component. If a component method has arguments that must be passed, there are two ways to achieve this with <cfinvoke>.The first way is to simply pass attributes with the same name as the arguments in to the <cfinvoke> tag. For example:
<cfinvoke component=Math method=divide numerator=5 denominator=10>

The second way to pass arguments to a component method involves a child tag of <cfinvoke> called <cfinvokeargument>.The <cfinvokeargument> tag has two attributes; name and value.The name attribute is used to identify the name of the argument, and the value attribute is used to pass the variable value to the function.
<cfinvoke component=Math method=divide> <cfinvokeargument name=numerator value=5> <cfinvokeargument name=denominator value=10> </cfinvoke>

Another way to pass arguments in to the <cfinvoke> tag is to create a structure of arguments and then pass the structure in to the argumentcollection attribute.This fourth technique is shown here.
<cfset args = StructNew()> <cfset args.numerator = 5> <cfset args.denominator = 10> <cfinvoke component=Math method=divide argumentcollection=#args#>

19.4.

Component Inheritance

265

19.4. Component Inheritance


You want to create a new component that extends the functionality of an existing component.

Technique
Use the extends attribute of the <cfcomponent> tag to create a component that inherits functionality from another component.
<cfobject component=Manager name=bob> <cfobject component=Employee name=fred> <cfset fred.init(1, Fred, fred@company.com)> <cfset bob.init(2, Bob, bob@company.com, 1)> <cfoutput> Freds Email: #fred.getEmail()#<br> Bobs Email: #bob.getEmail()#<br> Is Bob Freds Manager? #YesNoFormat( bob.manages( fred.getEmployeeID() ) )# </cfoutput>

Heres employee.cfc:
<cfcomponent> <cfset employeeID = 0> <cfset name = > <cfset email = > <cffunction name=init returntype=void> <cfargument name=empID type=numeric required=true> <cfargument name=empName type=string required=true> <cfargument name=empEmail type=string required=true> <cfset employeeID = arguments.empID> <cfset name = arguments.empName> <cfset email = arguments.empEmail> </cffunction> <cffunction name=getEmployeeID returntype=numeric> <cfreturn employeeID> </cffunction> <cffunction name=getName returntype=string> <cfreturn name> </cffunction> <cffunction name=getEmail returntype=string> <cfreturn email>

266

Chapter 19

Components

</cffunction> </cfcomponent>

Heres manager.cfc:
<cfcomponent extends=Employee> <!--- a list of employee ids the manager manages ---> <cfset manageList = > <cffunction name=init returntype=void> <cfargument name=empID type=numeric required=true> <cfargument name=empName type=string required=true> <cfargument name=empEmail type=string required=true> <cfargument name=empList type=string required=false> <cfset employeeID = arguments.empID> <cfset name = arguments.empName> <cfset email = arguments.empEmail> <cfset manageList = arguments.empList> </cffunction> <cffunction name=manages returntype=boolean> <cfargument name=empID required=true type=numeric> <cfreturn ListFind(manageList, arguments.empID)> </cffunction> </cfcomponent>

Comments
Objects that have an is a relationship (a tree is a plant) are prime candidates for inheritance.This technique uses the example: a Manager is an Employee.The Manager possesses all of the properties that a regular Employee does, but also has a list of employees that he or she manages as an additional property. The Manager CFC does not need to redefine the getEmployeeID, getName, or getEmail functions; these function definitions are inherited from the Employee CFC. This is facilitated by adding extends=Employee to the <cfcomponent> tag in the Manager CFC. Using inheritance often reduces the amount of code you need to write, because you can inherit code from other components. Because inheritance usually saves you quite a bit of code, it is tempting to use inheritance on relationships that dont make sense. For instance, you might try to inherit Cat from Dog because they both have four legs. Dont use inheritance unless you can say object A is an object B. You may have noticed that the example defined the init function in both the Employee and Manager CFCs.The implementation of the init function in the Manager CFC is said to override that of the Employee CFC.The Manager CFC includes an argument for the list of employees that the manager manages.

19.5.

Accessing a Component

267

Another tempting practice with inheritance is to use multiple inheritance; that is object A inherits from object B and object C, but objects B and C do not inherit from each other. Multiple inheritance exists in some languages such as C++, but is not allowed in ColdFusion or Java. You can, however, inherit from multiple components one at a time. For example, object A inherits from object B, which inherits from object C. In fact, by default, all components inherit from a component named WEB-INF.cftags.component, which is the base of all components. One of the advantages of inheritance is that it allows developers to create APIs and frameworks that can be extended by other developers.The extended components then work seamlessly with the APIs. After all, when you use inheritance, your extended component is a form of its parent component. In other words, you can write a function that expects to receive an Employee and pass it a Manager.The function can also handle any component that inherits from Employee.This phenomenon is known as polymorphism in object-oriented programming circles.
Note
Because ColdFusion components support function overriding, but do not support function overloading (multiple function definitions with different arguments), its possible to override a function and change the arguments so that the child class breaks polymorphic code. The init function in the Manager CFC adds an argument, but because it designates the argument as not required, the function is still safe.

19.5. Accessing a Component


You want to access CFCs from multiple directories.

Technique
<cfobject component=cfcookbook.19.Light name=light> <cfoutput>#light.isLightOn()#</cfoutput>

Comments
If you try to run a component in a directory other than the one it was defined in, you will find that a NoSuchTemplateException is thrown. Components can be accessed from any ColdFusion template on your server as long as you fully qualify the component name. The code in this technique instantiates a component named cfcookbook.19.light.When ColdFusion is given this component name, it looks under the Web root for the following file /cfcookbook/19/light.cfc. ColdFusion barrows some naming conventions here from Java; the directory /cfcookbook/19/

268

Chapter 19

Components

translates to a package named cfcookbook.19. Each directory under the Web root is its own package. ColdFusion components also allow you to enable access to functions with the access attribute of the <cffunction> tag. Functions defined within a component with the attribute access=package can only be accessed by CFCs or templates in the same package (directory). In addition, you can use access=private to write convenience functions that are available only to the CFC that defined them.

19.6. Using Component Meta Data


You want to retrieve information about a components methods and properties.

Technique
Pass a component reference into the GetMetaData function and manipulate the resulting structure.
<cfobject component=Light name=light> <cfset metaData = GetMetaData(light)> <cfdump var=#metaData# label=MetaData For Light CFC>

Comments
The GetMetaData function can be useful for debugging or for building applications that dynamically choose which methods to execute on a CFC by using the meta data. This process is often referred to as introspection. GetMetaData takes one argument, a reference to a component.You can obtain a reference to a component by using one of the methods mentioned in section 19.2 of this chapter, or by using the this keyword inside a CFC.The this keyword is a reference to the current component instance (it is also a scope that can be used to make public variables). The GetMetaData function, at a minimum, returns a structure with the following keys: extends, functions, name, path, and type.The name key stores the name of the CFC.The path key stores the path to the CFC on your server, and the type key should be set to component. The extends key contains the meta data for the base component. All components intrinsically inherit from WEB-INF.cftags.component. If you used the extends attribute to inherit from another component, it will appear in the extends key of the structure. In the functions key of the meta data structure is an array of structures, each structure has information about a function in the component.The function structure mini-

19.6.

Using Component Meta Data

269

mally has the keys name and parameters.The name key corresponds to the name of the function, and the parameters key holds an array of structures for each function argument.The parameters structure has keys equivalent to the possible attributes for the <cfargument> tag (name, returntype, default, and type).The structure in the functions array can also contain some additional keys depending the attributes present in the <cffunction> tag, including access, displayname, hint, roles, and returntype. The best way to work with meta data is to output the meta data structure with <cfdump> and then extract the pieces you need. Keep in mind that the presence of structure keys depends on which attributes were used with the <cfcomponent>, <cffunction>, and <cfargument> tags.

20
Objects
20.0. Introduction
ColdFusions object support is perhaps one of the most underused features of ColdFusion. Using the <cfobject> tag, or its equivalent function CreateObject, allows you to have direct interaction with ColdFusion Components,Web services, Java, COM, or CORBA objects. Because ColdFusion Components (or CFCs) are implemented in the CFML language, an entire chapter is devoted to them (Chapter 19, Components).Web services are also covered in a separate chapter (Chapter 23, Web Services).This chapter covers the processes of creating and invoking Java, COM, and CORBA objects. Using objects liberates your ColdFusion code from performing complex business logic.The logic is instead written and optimized in a separate class. Using objects also can increase the performance and scalability of an application. For example, if the object can be distributed across multiple servers, you increase scalability. Several third-party objects and components are available that can reduce the amount of time needed to develop an application. In addition, most third-party objects undergo more testing than you would normally administer, providing better reliability and stability.

20.1. Creating a Java Object Instance


You want to create an instance of a Java object within ColdFusion.

Technique
Use the <cfobject> tag or the CreateObject function to create an instance of a Java object:
<cfobject name=stack class=java.util.Stack type=java action=create>

272

Chapter 20

Objects

or
<cfset stack = CreateObject(java, java.util.Stack)>

Comments
There are two ways to create an instance of any arbitrary Java object in ColdFusion <cfobject> and CreateObject. In the <cfobject> tag, four attributes are required to instantiate the objectname, class, type, and action. The value of the name attribute corresponds to the name of the ColdFusion variable that holds a reference to the instantiated Java object. Using one of the persistent scopes (application, session, server, or client) allows the object instance to persist between multiple page requests. The class attribute of the <cfobject> tag defines the name of the Java class to be instantiated.The class must be in the servers classpath in order for ColdFusion to find the code for the class. Several classes and packages are available natively as part of Java. Such classes can be instantiated without concern as to whether they have been added to the classpath, as they are intrinsically part of the Java platform. Some of the most useful classes that are part of the Java platform can be found in the java.util package.The java.util package contains several data structures such as List, Hash Table, Set,Tree, and a Stack (this technique instantiates a Stack object). Because the <cfobject> tag can also be used to instantiate ColdFusion Components,Web services, CORBA, and COM objects, the type attribute is required. The action attribute is also required.To create an instance of a Java object, set type=java and action=create.Table 20.1 lists all of the attributes of the <cfobject> tag.
Table 20.1 <cfobject> Attributes Attribute
type

Type Java, CORBA, and COM

Meaning The type of object to create: Java, CORBA, or COM. Required if the component attribute is not present. The name of a variable that will hold a reference to the object instance. Required. An identifier to the class to be instantiated. Required if the component attribute is not present. Java and CORBA always use the value create. With COM you can specify create or connect. Connect is used to connect to an existing object. Required if the component attribute is not present.

name class

Java, CORBA, COM, Component,Web Service Java, CORBA, and COM

action

Java, CORBA, and COM

20.2.

Invoking Methods on an Object

273

Table 20.1 Continued Attribute


context

Type CORBA and COM

server locale

COM only CORBA only

component Components only

webservice Web Services only

Meaning Specifies the context in which the object will be created. Possible values in CORBA are IOR or nameservice.When type=COM is used, the values are InProc, local, or remote. Required for CORBA; COM objects use registry settings as a default. A DNS or UNC path. Required if context=remote. The name of the CORBA ORB connection to use. Required if there are more than one registered connectors in the ColdFusion Administrator. The name of a ColdFusion Component to create an instance of. See Chapter 19 for more information. A URL to a WSDL file, which describes the service.

The CreateObject function can also be used to instantiate a Java object with slightly less code.The first argument of the CreateObject function corresponds to the type attribute in <cfobject>.The second argument represents the name of the Java class to instantiate (corresponds to the class attribute of <cfobject>). CreateObject returns a reference to an instance to the Java object.This reference can then be stored in a ColdFusion variable. Although CreateObject and <cfobject> yield the same result, CreateObject can be used within <cfscript> blocks, and can be invoked with less code.

20.2. Invoking Methods on an Object


You want to invoke static or instance methods on an existing object.

Technique
Use the syntax instanceName.methodName(arguments) to invoke a method on an object.
<cfset stack = CreateObject(java, java.util.Stack)> <cfset stack.push(A)> <cfset stack.push(B)> <cfoutput> pop: #stack.pop()#<br> pop: #stack.pop()#<br> </cfoutput>

274

Chapter 20

Objects

Comments
The technique to invoke a method on an object is identical for each type of object that ColdFusion supports (Components, Java, CORBA, COM, and Web services). This technique invokes a method on a Java instancenamely methods on the java.util.Stack class.The stack data structure allows you to push objects onto the stack, and then pop them off the top.The first item on the stack will be the last item popped off (this is known formally as Last In First Out, or LIFO). Items are placed on the stack with the push method.The push method takes one argument of type java.lang.Object. Because all methods in Java inherit from java.lang.Object, any variable in ColdFusion MX also inherits from java.lang.Object behind the scenes.Thus you can pass any variable into the push method; however, this technique used the literal strings A and B for simplicity. Items are popped off the top of the stack with a call to the pop method. Execution of this technique outputs B A (LIFO ordering). You can also invoke static methods (methods that do not use instance variables) with ColdFusion MX. However, in ColdFusion you must still create an instance of the object before you can call a method on it, which defeats the performance benefits of using static methods.The code to invoke a static method is the equivalent to an instance method.
<cfset system = CreateObject(java, java.lang.System)> <cfoutput>#system.currentTimeMillis()#</cfoutput>

Because ColdFusion MX is a loosely typed language, all numbers are represented as strings.When a ColdFusion number is passed to a Java method that expects an int as an argument, ColdFusion will convert the string representation of the number into an integer.These conversions will degrade performance. If you need to perform several Java method invocations, especially those using data types other than String, you might be better off writing a high-level wrapper class in Java. If a Java method is overloaded (it can be passed different data types), you may need to use the JavaCast function to explicitly cast a ColdFusion variable to a primitive data type.The JavaCast function takes two arguments; the first argument is the type to cast to. Possible values for this argument include int, string, boolean, long, and double.The second argument of the JavaCast function is the value you are casting.
<cfset x = someObject.overloadedMethod(JavaCast(int, 5))> <cfset y = someObject.overloadedMethod(JavaCast(string, Hello))>

20.4.

Working with Java Objects

275

20.3. Using Properties on an Object


You want to access public properties on an object.

Technique
Use instanceName.propertyName to read or set the value of a public property.
<cfset math = CreateObject(java, java.lang.Math)> <cfset pi = Math.PI> <cfoutput>#pi#</cfoutput>

Comments
Accessing a property in an object is identical to accessing a key within a structure using dot notation.The syntax is the same for all types of objects. The property in this example happens to be the value of PIa static value.This property is defined as static final, which in Java means that its value cannot be changed. Because PI is read-only, you cannot write to it; however, some properties can be written to.The syntax for writing to a property is simply instanceName.propertyName = value.

20.4. Working with Java Objects


You want to work with Java objects.

Technique
Create an object instance with <cfobject> or CreateObject with type=java, and then call methods or properties on that object.
<cfset random = CreateObject(java, java.util.Random)> <cfset randomSeed = GetTickCount()> <!--- call the constructor ---> <cfset random.init(randomSeed)> <cfset randomBool = random.nextBoolean()> <cfoutput> Are you feeling lucky? #YesNoFormat(randomBool)# </cfoutput>

Comments
In ColdFusion MX, short of Components, Java objects are the natural choice for implementing business logic and utility functions. Due to its Java backbone, ColdFusion MX handles Java objects much more gracefully than it does CORBA or COM objects. Some of the shortcomings of using Java objects, or any type of object, is due to the way CreateObject and <cfobject> work.There is no way to call the constructor

276

Chapter 20

Objects

when the object is created in CreateObject or <cfobject>.The constructor can be called by using the init function.When ColdFusion receives a call to the init function, it calls the constructor. Although this works, it isnt optimal because two objects are created (one in CreateObject/<cfobject>, and one in init).

20.5. Working with COM Objects


You want to work with COM/DCOM objects.

Technique
Use the <cfobject> tag to connect to or create an instance of a COM or DCOM object.
<cftry> <cfobject type=COM action=connect class=Scripting.FileSystemObject name=fileSystem> <cfcatch type=Object> <cfobject type=COM action=create class=Scripting.FileSystemObject name=fileSystem> </cfcatch> </cftry> <cfset drive = fileSystem.GetDrive(C)> <cfoutput> C:\ [#drive.VolumeName#]<br> Total Size: #Round((drive.TotalSize / 1024) / 1024)# MB<br> Free Space: #Round((drive.FreeSpace / 1024) / 1024)# MB<br> </cfoutput>

Comments
COM (Component Object Model) object support is found only in the Windows versions of ColdFusion MX. Being a Windows-centric technology, COM often makes it easy to interact with Windows components, such as the file system illustrated in this technique. In order for ColdFusion to get an instance of a COM object, it must first be registered.To register a DLL (Dynamic Link Library), use the regsvr32 program. For example:
regsvr32 c:\file.dll

Several COM objects are installed as part of Windows, or as part of service packs such as the Scripting.FileSystemObject.These objects are already registered, and therefore should work in a ColdFusion environment without any extra steps. This technique attempts to connect to an instance of the Scripting.FileSystemObject that is already running on the server. If no instance is running, the <cfcatch> block executes and creates a new instance of the object. With an instance to the Scripting.FileSystemObject, you can call methods or

20.6.

Working with CORBA Objects

277

access properties exposed by the object. In this case, you call the GetDrive method to retrieve an object with information about the specified drive. COM object performance has degraded significantly in ColdFusion MX. Because ColdFusion MX is written in Java, it requires a Java-to-COM bridge to facilitate the communication. ColdFusion MX currently uses a third-party Java-to-COM bridge called Jintegra. One of Macromedias recommendations to improve performance is to create a Java stub for your COM object.The Java stub is effectively a Java wrapper for the COM object. A tool that is part of Jintegra, called com2java.exe, automatically generates the Java stub.You can find out more information about this technique in Macromedias Technote #22922. Rather than waiting for patches and performance tweaks, a good long-term solution is to rewrite your COM objects in Java. Besides increased performance, you will also have an operating system-independent application. You can connect to objects on your network using DCOM (Distributed Component Object Model) by specifying the server attribute in <cfobject>.The server attribute must contain a DNS name (such as www.server.com), an IP address, or a UNC name (such as \\computer). The CreateObject function can also be used to connect to or create a COM object instance, as in this example code:
<cfset fileSystem = CreateObject(COM, Scripting.FileSystemObject)>

20.6. Working with CORBA Objects


You want to work with CORBA objects.

Technique
Use <cfobject> or CreateObject to create an instance of a CORBA object.
<cfobject name=person context=IOR class=c:\ior\person.ior action=create type=CORBA> <cfset person.setName(Pete)> <cfoutput>#person.getName()#</cfoutput>

Person.idl:
module PersonApp { interface Person { string getName(); void setName(in string name); }; };

278

Chapter 20

Objects

Comments
Common Object Request Broker Architecture, or CORBA, is a mechanism for developing language-independent distributed applications.The specifications for CORBA are managed by the OMG (Object Management Group). CORBA implementations exist for nearly every object-oriented language. CORBA achieves its language independence by using its own language to describe the object interface.The language is called IDL (Interface Definition Language). Using the IDL for verification, all object invocations are passed through an ORB (Object Request Broker).The ORB is responsible for finding and managing a connection to the Servant object (the implementation of the object). Servant objects are classes written in a CORBA supported language, and typically bound to CORBA through stub classes. Because all requests go through the ORB, the server and the ORB can reside on different computers, allowing for the construction of distributed applications. ColdFusion MX is integrated with Borland VisiBroker 4.5.1 for Java ORB; however, you must download and install VisiBroker from Borland if you want to use CORBA with ColdFusion MX. Once installed, you have to set up a CORBA connector in the ColdFusion Administrator.The ColdFusion MX documentation details the necessary installation steps.
Note
Other ORBs can also be used with ColdFusion MX; however, you must write a connector for the ORB vendor. Contact Macromedia if you need to use an ORB other than VisiBroker.

Once a connection to the ORB is present, the ORB must be able to find the Servant objects you want to invoke.There are two supported methods for facilitating this Naming Services and Interoperable Object References (IOR).The previous code example uses a file that contains the IOR; the filename is passed through the class attribute of <cfobject>.The context attribute is used to distinguish between using an IOR and a CORBA naming service.The following example shows using a naming service:
<cfobject context=NameService class=Organization/Person name=person action=create type=CORBA>

The class attribute when context=NameService contains the name of the object; forward slashes (/) are used as delimiters. An additional attribute of <cfobject> is provided to specify which CORBA connector you are using. If you have multiple ORBs registered in the ColdFusion administrator, you need to pass the name of the connection into the locale attribute of <cfobject>. For example, to connect to an ORB called JavaIDL, you use the following code:

20.6.

Working with CORBA Objects

279

<cfobject locale=JavaIDL name=obj context=IOR class=c:\x.ior action=create type=CORBA>

The CreateObject function can also be used to retrieve object handles from the ORB.
<cfset obj = CreateObject(CORBA, IOR, c:\x.ior, corbaConnection)>

The CreateObject function expects the arguments type, context, class, and optionally locale. The mapping of IDL to ColdFusion is defined in Table 20.2.
Table 20.2 CORBA IDL to ColdFusion Type Mappings IDL Type
any array attribute boolean char constant couble enum float interface long longlong module octet sequence short string struct typedef union unsigned long unsigned longlong unsigned short

ColdFusion Type N/A


Array

Supported

No Yes Object reference Yes Boolean Yes String Yes N/A No Numeric Yes Numeric (zero is the first number in the enumeration) Yes Numeric Yes Object reference Yes Numeric Yes N/A No N/A Yes String Yes Array Yes Numeric Yes String Yes Structure Yes N/A Yes N/A No Number Yes N/A No Number Yes

280

Chapter 20

Objects

Table 20.2 Continued IDL Type


void wchar wstring

ColdFusion Type N/A


String String

Supported Yes Yes Yes

Some of the goals of CORBA are very similar to the goals of Web services, such as distributed language-independent objects.Web services are much easier to use in ColdFusion MX than CORBA, and may be a better choice. See Chapter 23, Web Services, for more information about Web services.

21
WDDX
21.0. Introduction
Web Distributed Data Exchange or WDDX is an XML format for serializing data structures (such as structures, arrays, and queries/recordsets) and data types (such as strings, numbers, booleans, and binary).WDDX was introduced in ColdFusion 4.0 with hopes to promote language-independent data sharing. Several toolkits have been developed for WDDX in languages other than ColdFusion, such as ASP, Java, JavaScript, Perl, PHP, and C#. However,WDDX has yet to reach widespread adoption outside of the ColdFusion community. WDDX is a good shortcut for converting ColdFusion variables into XML documents.The <cfwddx> tag also makes it easy to represent ColdFusion variables as JavaScript variables.

21.1. CFML to WDDX


You want to convert a CFML data structure into a WDDX XML document.

Technique
Use the<cfwddx> tag with action=CFML2WDDX to convert a ColdFusion structure to WDDX.
<cfset slopes = ArrayNew(1)> <cfset slopes[1] = WhiteFace> <cfset slopes[2] = Killington> <cfwddx action=CFML2WDDX input=#slopes# output=skiWDDX> <cfoutput>#HTMLEditFormat(skiWDDX)#</cfoutput>

282

Chapter 21

WDDX

Comments
The code in this technique creates a simple array with two elements, and represents the array in a standard XML format,WDDX.The following code shows the generated XML, or WDDX packet, stored in the skiWDDX variable.
<wddxPacket version=1.0> <header/> <data> <array length=2> <string>WhiteFace</string> <string>Killington</string> </array> </data> </wddxPacket>

The generated XML produces enough information for any language to reconstruct this ColdFusion array in its native array. The key element of this example is the <cfwddx> tag, as it does the work of converting the ColdFusion variable into XML.The action attribute of the <cfwddx> tag specifies the type of translation to be performed.The action in this case is CFML2WDDX. The other three possible values for action are discussed in subsequent sections of this chapter. When you specify action=CFML2WDDX, the tag will naturally convert the input variable (identified by the input attribute) into a WDDX packet.The result of the <cfwddx> operation is stored in the variable name designated by the value of the output attribute.Table 21.1 shows some common tag attributes.
Table 21.1 <cfwddx> Tag Attributes Attribute
action input output

Meaning The type of WDDX translation to perform. Possible values are CFML2WDDX, WDDX2CFML, CFML2JS, and WDDX2JS. Input value to serialize or deserialize. Name of a variable to store the result of the action. If the action generates JavaScript (if action is CFML2JS or WDDX2JS), and this attribute is not present, the result will print to the page output stream. When true, <cfwddx> validates input WDDX against the WDDX DTD. Used with the actions WDDX2CFML and WDDX2JS. The JavaScript variable name for the resulting JavaScript object. Only used when action is CFML2JS or WDDX2JS. When set to true, <cfwddx> represents times in the ISO8601 format.When its false, local times are used.

validate toplevelvariable usetimezoneinfo

21.2.

CFML to JavaScript

283

This example uses the HTMLEditFormat function to format the XML so that it will display in your browser (it escapes the < and > characters).This is handy for displaying WDDX. Some Web browsers will format XML in an easy-to-read format.You can let the browser know that your returning XML by setting the HTTP response header Content-Type to text/xml. HTTP response headers can be issued with the <cfheader> tag.
<cfheader name=Content-Type value=text/xml> <cfoutput>#skiWDDX#</cfoutput>

In Internet Explorer, the XML is presented in a color-coded format with collapsible nodes instead of in HTML format.

21.2. CFML to JavaScript


You want to convert a CFML data structure into a JavaScript object.

Technique
To convert a ColdFusion data structure to a JavaScript object, you use the <cfwddx> tag with the action attribute set to CFML2JS.
<cfset <cfset <cfset <cfset trail = StructNew()> trail.isOpen = false> trail.name = BarkEater> trail.opens = CreateDate(2002,11,1)>

<script language=JavaScript> <cfwddx action=CFML2JS input=#trail# toplevelvariable=jsTrail> if (jsTrail[isopen] == true) { alert(Trail: + jsTrail[name] + is open.); } else { alert (Trail: + jsTrail[name] + opens on + jsTrail[opens]); } </script>

Comments
Converting from CFML to JavaScript doesnt actually involve WDDX.The ColdFusion variable is converted to a JavaScript variable with the <cfwddx> tag. In this technique, the <cfwddx> tag produces the following JavaScript code:

284

Chapter 21

WDDX

jsTrail = new Object(); jsTrail[opens] = new Date(2002, 10, 1, 0, 0, 0); jsTrail[isopen] = false; jsTrail[name] = BarkEater;

There are a few things to note about the generated JavaScript code.The isOpen key in the trail structure is converted to lowercase; this is important because JavaScript variables are case-sensitive, but ColdFusion variables are not. Attempting to read the JavaScript variable jsTrail[isOpen] will return a null value. The ColdFusion variable trail.isOpen is set to a boolean value, false. However the corresponding JavaScript variable jsTrail[open] is set to the string false, not a boolean. Because of this, you cant treat the JavaScript variable as a boolean. For example if (jsTrail[isopen]) will never evaluate to true, even if trail.isOpen is set to true. One feature of the generated JavaScript is that ColdFusion date objects are converted into JavaScript date objects, as the value of jsTrail[opens] illustrates. Different ColdFusion data types generate different JavaScript objects.Table 21.2 shows some common JavaScript conversions.
Table 21.2 CFML2JS Conversions Data Type CFML Array Date Query
<cfset array = ArrayNew(1)> <cfset array[1] = One> <cfset date = CreateDateTime (2000,1,1, 12,00,00)> <cfset query = QueryNew (column)> <cfset row = QueryAddRow(query)> <cfset QuerySetCell(query, column, value, row)>

JavaScript
array = new Array(); array[0] = One; date = new Date(2000, 0, 1, 0, 0, 0); query = new WddxRecordset(); col0 = new Array(); col0[0] = value; query[column] = col0; col0 = null; string = Value; struct = new Object(); struct[key] = Value; number = 8; bool = true;

String Structure Number Boolean

<cfset string = Value> <cfset struct = StructNew()> <cfset struct.key = Value> <cfset number = 8> <cfset bool = true>

Note
If you want to work with query objects in JavaScript, you need to include the WDDX JavaScript library. The definition for the WddxRecordset JavaScript object is defined in this library, along with a JavaScript to WDDX serializer. Binary data also requires the JavaScript WddxBinary object to be defined. To include the library, use the src attribute of the <script> tag<script src=/CFIDE/scripts/ wddx.js></script>.

21.3.

WDDX to CFML

285

21.3. WDDX to CFML


You want to convert a valid WDDX XML document into a CFML data structure.

Technique
Use action=WDDX2CFML in the <cfwddx> tag to convert WDDX to a ColdFusion variable.
<cfsavecontent variable=wddx> <wddxPacket version=1.0> <header/> <data> <array length=2> <string>WhiteFace</string> <string>Killington</string> </array> </data> </wddxPacket> </cfsavecontent> <cfwddx action=WDDX2CFML input=#wddx# output=slopes> <cfoutput> #slopes[1]# </cfoutput>

Comments
The process of converting WDDX into ColdFusion data structures is known as deserialization.This code is essentially the opposite of the serialization example shown in section 21.1.The input attribute receives a WDDX string, and the output attribute holds the name of the resulting ColdFusion variable. Table 21.3 shows some common data types that are supported, and their WDDX representations.
Table 21.3 WDDX Representations of Data Types Data Type Array WDDX Representation
<array length=1> <string>One</string> </array>

Date Query

<dateTime>2000-1-1T12:0:0-5:0</dateTime> <recordset rowCount=1 fieldNames=column

286

Chapter 21

WDDX

Table 21.3 Continued Data Type WDDX Representation


type=coldfusion.sql.QueryTable> <field name=column> <string>value</string> </field> </recordset>

String Structure

<string>some string</string> <struct> <var name=KEY> <string>Value</string> </var> </struct>

Number Boolean Binary

<number>8</number> <boolean value=true/> <binary length=6>YmluYXJ5</binary>

Note
Binary data in WDDX is encoded in Base64 encoding.

21.4. WDDX to JavaScript


You want to convert WDDX into a JavaScript object.

Technique
Use WDDX2JS as the value for the action attribute in <cfwddx> to convert a WDDX string into JavaScript variables.
<cfsavecontent variable=wddx> <wddxPacket version=1.0> <header/> <data> <array length=2> <string>WhiteFace</string> <string>Killington</string> </array> </data> </wddxPacket> </cfsavecontent>

21.5.

Syndicating Content with WDDX

287

<script language=JavaScript> <cfwddx action=WDDX2JS input=#wddx# toplevelvariable=slopes> for (var i=0;i<slopes.length;i++) { alert(slopes[i]); } </script>

Comments
Converting WDDX to JavaScript works just like converting CFML to JavaScript (read section 21.2). Simply use WDDX2JS as the action, and use the WDDX string in the input attribute of <cfwddx>.The toplevelvariable attribute stores the name of the JavaScript variable. You can also store the generated JavaScript in a ColdFusion variable using the output attribute in <cfwddx>. For example:
<cfwddx action=WDDX2JS input=#wddx# output=slopesJS toplevelvariable=slopes> <script language=JavaScript> <cfoutput>#slopesJS#</cfoutput> </script>

21.5. Syndicating Content with WDDX


You want to distribute content using WDDX.

Technique
Publish a page that serializes your content with <cfwddx>, and output the resulting WDDX packet.
skiConditions.cfm:
<cfsetting showdebugoutput=false enablecfoutputonly=true> <cfset skiConditions = StructNew()> <cfset skiConditions.currentTemperature = 24> <cfset skiConditions.weatherForecast = Snow, and lots of it.> <cfset skiConditions.newSnow = 8> <cfset skiConditions.averageSnowBase = 75> <cfset skiConditions.trails = QueryNew(name, isOpen, skillLevel)> <cfset row = QueryAddRow(skiConditions.trails)> <cfset QuerySetCell(skiConditions.trails, name, Jeremys Run, row)> <cfset QuerySetCell(skiConditions.trails, isOpen, Yes, row)>

288

Chapter 21

WDDX

<cfset QuerySetCell(skiConditions.trails, skillLevel, Expert, row)> <cfset row = QueryAddRow(skiConditions.trails)> <cfset QuerySetCell(skiConditions.trails, name, Avalanche Cliff, row)> <cfset QuerySetCell(skiConditions.trails, isOpen, Yes, row)> <cfset QuerySetCell(skiConditions.trails, skillLevel, Expert, row)> <cfheader name=Content-Type value=text/xml> <cfwddx action=CFML2WDDX input=#skiConditions# output=wddx> <cfoutput> <?xml version=1.0?> #wddx# </cfoutput>

Comments
Syndicating content with WDDX involves creating a Web page that outputs WDDX. The example publishes ski conditions for a factitious ski resort called ColdFusion Mountain. On the first line of the example is a <cfsetting> tag. It ensures that the only thing you output is XML by disabling debugging output, and output that isnt inside the <cfoutput> tags.
Note
White space is ignored in WDDX, so its not necessary to use the <cfsetting> tag to limit output to the body of <cfoutput> tags. However, any characters that are output accidentally may cause problems with parsers deserializing the content.

Next, you create a structure that holds the information you are to syndicate.The trails key in the skiConditions structure holds a query of trial information.This query is populated manually with the QueryNew, QueryAddRow, and QuerySetCell functions but normally all the information youre syndicating would come from a database or some other dynamic source. The <cfheader> tag sets the page Content-Type to text/xml in order to allow browsers that format XML to do so. Finally, you serialize the structure into WDDX with <cfwddx> and output it. An XML document declaration <?xml version=1.0?> is prepended to the WDDX. You might also consider adding the WDDX Document Type Declaration (DTD) to your XML document to enable XML validation.

21.5.

Syndicating Content with WDDX

289

Note
The WDDX DTD, along with more information about WDDX, can be found at www.openwddx.org. The OpenWDDX Web site is an effort to promote WDDX as an open interface for exchanging data on the Web.

Once the WDDX is published it can be consumed by anyone with access to it, on any server.WDDX is typically consumed with a HTTP client such as <cfhttp>.
<cfif NOT IsDefined(application.skiConditions) OR IsDefined(url.update)> <!--- retrieve the WDDX feed ---> <cfset skiConditionsURL = http://skicoldfusion.com/skiConditions.cfm> <cfhttp url=#skiConditionsURL#></cfhttp> <cfwddx action=WDDX2CFML input=#CFHTTP.FileContent# output=application.skiConditions> </cfif> <b>Current Ski Conditions at ColdFusion Mountain</b><p> <cfoutput> Current Temperature: #application.skiConditions.currentTemperature#<br> Weather Forecast: #application.skiConditions.weatherForecast#<br> New Snow: #application.skiConditions.newSnow# inches<br> Average Snow Base: #application.skiConditions.averageSnowBase# inches<br> </cfoutput> </p> <b>Trails</b><p> <cfoutput query=application.skiConditions.trails> #application.skiConditions.trails.name# (#application.skiConditions.trails.skillLevel#): <cfif application.skiConditions.trails.isOpen>Open <cfelse>Closed </cfif><br> </cfoutput> </p>

The output of this example is as follows:


Current Ski Conditions at ColdFusion Mountain Current Temperature: 24 Weather Forecast: Snow, and lots of it. New Snow: 8 inches Average Snow Base: 75 inches Trails Jeremys Run (Expert): Open Avalanche Cliff (Expert): Open

290

Chapter 21

WDDX

When the WDDX is deserialized back into a ColdFusion variable, you can manipulate it as you want. In this case, the example shows an HTML page with a summary of the ski conditions. The preceding code example caches the skiConditions structure in an application scoped variable so the WDDX doesnt need to be retrieved every time the ski conditions are requested. Caching the feed benefits both the content provider and the content consumer with less bandwidth consumption. Web Services are another good way to share data between servers; they are discussed in Chapter 23, Web Services.

21.6. Converting a Java Object into WDDX


You want to serialize a Java data structure into WDDX XML.

Technique
Use the WddxSerializer class to convert a Java object to WDDX.
import com.allaire.wddx.WddxSerializer; import java.util.Hashtable; import java.util.Vector; import java.util.Date; import java.io.StringWriter; public class WDDXSerializerExample { public static final void main(String[] args) throws java.io.IOException { Hashtable hash = new Hashtable(); hash.put(key, value); Vector v = new Vector(); v.add(new Date()); v.add(new Integer(8)); hash.put(array, v); WddxSerializer serializer = new WddxSerializer(); StringWriter writer = new StringWriter(); serializer.serialize(hash, writer);

21.6.

Converting a Java Object into WDDX

291

System.out.println(writer.toString()); } }

Comments
The WddxSerializer class is included in a Java archive file called wddx.jar that is included in the WDDX SDK from www.openwddx.org.The WddxSerializer class, when given an object, will serialize the object in the WDDX format.Table 21.4 shows the mapping of Java data structures to their respective WDDX representation.
Table 21.4 WDDX Representations of Java Data Structure WDDX Type
array dateTime recordSet string struct number boolean number number number number string array struct

Java Type
Java.util.Vector Java.util.Date com.allaire.util.RecordSet Java.lang.String Java.util.Dictionary Java.lang.Integer Java.lang.Boolean Java.lang.Byte Java.lang.Float Java.lang.Double Java.lang.Short Java.lang.Character Java.util.List Java.util.Map

The serialize method of the WddxSerializer class takes two arguments.The first argument is an object of type java.lang.Object that you would like to serialize into WDDX.The second argument is used to pass a java.io.Writer to the serializer.The serialized content is then written to the java.io.Writer. Because the WddxSerializer can only serialize a java.lang.Object, you cannot directly serialize a primitive data type such as int, float, or an array. Instead primitive data types must be converted to their java.lang counterparts, such as java.lang.Integer and java.lang.Float. Arrays must be converted to a java.util.List or a java.util.Vector. Due to polymorphism in Java, the WddxSerializer can serialize any object that inherits from any of the Java classes listed in Table 21.4. For example, the java.util.Hashtable inherits from java.util.Dictionary; a hashtable was used in the previous example.

292

Chapter 21

WDDX

The WDDX API contained within wddx.jar is written in a manor that allows you to create your own serializer for your own data structures.To create your own WDDX object serializer, you must create a class that implements the com.allaire.wddx. WddxObjectSerializer interface.The WddxObjectSerializer interface has one method that you must implement. Its called writeObject and takes a com.allaire. wddx.WddxOutputStream and a java.lang.Object as inputs and has a void return type. Once your WDDX object serializer is implemented, you can add it to the instance of the com.allaire.wddx.WddxObjectSerializerFactory class that your WddxSerializer instance is using.This is done using the getSerializerFactory or the static getDefaultSerializerFactory method in the WddxSerializer class.

21.7. Deserializing WDDX in Java


You want to create java objects from a WDDX packet.

Technique
Use the WddxDeserializer class to convert WDDX to java objects.
import import import import import import import import java.io.StringReader; java.io.IOException; org.xml.sax.InputSource; com.allaire.wddx.WddxDeserializer; com.allaire.wddx.WddxDeserializationException; com.allaire.util.RecordSet; java.io.FileInputStream; java.util.Dictionary;

public class WDDXDeserializerExample { public static final void main(String[] args) throws Exception { FileInputStream fin = new FileInputStream(c:/skiConditions.wddx); InputSource in = new InputSource(fin); Object obj = deserializeWDDX(in); if (!(obj instanceof Dictionary)) { throw new Exception(obj not a dictionary); } Dictionary ski = (Dictionary)obj; System.out.println(Temp:

21.7.

Deserializing WDDX in Java

293

+ ski.get(CURRENTTEMPERATURE)); System.out.println(New Snow: + ski.get(NEWSNOW)); System.out.println(Forecast: + ski.get(WEATHERFORECAST)); System.out.println(Avg Base: + ski.get(AVERAGESNOWBASE)); Object trailObj = ski.get(TRAILS); if (! (trailObj instanceof RecordSet) ) { throw new Exception(trails is not a query); } RecordSet trails = (RecordSet)trailObj; int rows = trails.getRowCount(); int name = trails.findColumn(name); int isOpen = trails.findColumn(isOpen); int skill = trails.findColumn(skillLevel); System.out.println(Trails:); StringBuffer sb = new StringBuffer(32); for (int row=0;row<rows;row++) { sb.setLength(0); sb.append(trails.getField(row, name)); sb.append( - open: ); sb.append(trails.getField(row, isOpen)); sb.append( - skill level: ); sb.append(trails.getField(row, skill)); System.out.println(sb.toString()); } }

public static Object deserializeWDDX(String wddxPacket) throws WddxDeserializationException, IOException { InputSource in = new InputSource(new StringReader(wddxPacket)); return deserializeWDDX(in); } public static Object deserializeWDDX(InputSource in) throws WddxDeserializationException, IOException { WddxDeserializer deserializer =

294

Chapter 21

WDDX

new WddxDeserializer(org.apache.xerces.parsers.SAXParser); // Deserialize the WDDX packet return deserializer.deserialize(in); } }

Comments
This example outputs the ski conditions WDDX feed presented in section 21.5.The WddxDeserializer class is used to convert the WDDX packet into Java objects, and is found in wddx.jar from www.openwddx.org.The WddxDeserializer constructor takes the name of a Sax Parser (a class that implements the org.xml.sax.Parser interface). In this case, we specified org.apache.xerses.parsers.SAXParser because we are using Xerces from the Apache Foundation. To deserialize the WDDX packet, the deserialize method is invoked in the WddxDeserializer class.The deserialize method takes an org.xml.sax. InputSource object as an input.The org.xml.sax.InputSource class constructor takes a java.io.InputStream as an argument. In this example we pass an instance of a java.io.FileInputStream to create the input source. The deserialize method returns a java.lang.Object that represents the contents of the WDDX packet.The next step is typically to cast the object into a type that you can use.This example does just that, but before the object is cast the type of the object is verified by determining whether it is an instance of a java.util.Dictionary. Data is extracted from the recordSet object (the trails query) using the com. allaire.util.RecordSet class.The methods for the com.allaire.util. RecordSet class are defined in Table 21.5.
Table 21.5 The com.allaire.util.RecordSet Interface Method
int addRows(int rows) int addColumn(String name) int findColumn(String name) int getColumnCount() String[] getColumnNames() Object getField(int row, int column) int getRowCount() void setField(int row, int column, Object value)

Description Adds rows to the record set and returns the row index. Adds a column with the specified name and returns the column index. Returns the column index given the column name. Returns the number of columns in the record set. Returns an array holding the names of each column in the record set. Returns the value of a cell in the record set. Returns the number of rows in the record set. Sets the value of a cell in the record set.

22
XML
22.0. Introduction
The Xtensible Markup Language (XML) has quickly become the data interchange format of choice. Software systems can now use XML as a communications vehicle with other systems, regardless of their platform or implementation.WDDX and SOAP are two excellent examples of how XML can be used to serialize and transmit complex data objects. Prior to the ColdFusion MX release, CF developers who wanted to use XML were forced to do so manually. Fortunately, the ColdFusion MX release includes a robust programming library for creating, modifying, and interacting with XML documents.

22.1. Creating an XML Object with the <cfxml> Tag


You want to create an XML object manually.

Technique
Use the <cfxml> tag to create and initialize a ColdFusion XML structure.
<cfscript> person = structNew(); person.firstname = Albert; person.lastname = Einstein; person.city = Princeton; person.state = New Jersey; person.country = USA; </cfscript>

296

Chapter 22

XML

<cfxml variable=personXML> <person> <cfoutput> <firstName>#person.firstName#</firstName> <lastName>#person.lastName#</lastName> <city>#person.city#</city> <state>#person.state#</state> <country>#person.country#</country> </cfoutput> </person> </cfxml> <cfdump var=#personXML#>

Comments
The <cfxml> tag is an excellent way to create and initialize an XML document object quickly. If you have ever used the XML DOM API to create an XML document object, you know that this method can be a laborious process.You have to first create a new object for each element, attribute, and text value in the XML tree and then piece them together.You can bypass this process by using the <cfxml> tag to initialize an XML object with a string representing the XML document you want to create. The previous example uses this technique to create an XML representation of a person structure.The example uses text to fill out the structure of the XML document within the body of the <cfxml> tag and uses ColdFusion expressions to extract the values from a CFML structure to be used as text nodes.

22.2. Creating an XML Object with the XmlNew() Function


You want to create an XML object programmatically.

Technique
Use the XMLNew() and XMLElemNew() functions to build an XML object piece by piece.
<cfscript> person = structNew(); person.firstname = Albert; person.lastname = Einstein; person.city = Princeton; person.state = New Jersey;

22.2.

Creating an XML Object with the XmlNew() Function

297

person.country = USA; </cfscript> <cfscript> personXML = XmlNew(); personXML.xmlRoot = XmlElemNew(personXML, person); personXML.xmlRoot.XmlChildren[1] = XmlElemNew(personXML, firstName); personXML.xmlRoot.XmlChildren[1].XmlText = person.firstName; personXML.xmlRoot.XmlChildren[2] = XmlElemNew(personXML, lastName); personXML.xmlRoot.XmlChildren[2].XmlText = person.lastName; personXML.xmlRoot.XmlChildren[3] = XmlElemNew(personXML, city); personXML.xmlRoot.XmlChildren[3].XmlText = person.city; personXML.xmlRoot.XmlChildren[4] = XmlElemNew(personXML, state); personXML.xmlRoot.XmlChildren[4].XmlText = person.state; personXML.xmlRoot.XmlChildren[5] = XmlElemNew(personXML, country); personXML.xmlRoot.XmlChildren[5].XmlText = person.country; </cfscript> <cfdump var=#personXML#>

Comments
There are two types of XML objects in ColdFusion MX: elements and documents. An element object represents a single element node within an XML tree. Generally, an element contains a name, a list of attributes, a text value, and/or a collection of child elements. Element objects belonging to a single XML document are assembled into a document object.The document objects job is to maintain a reference to the root node in the XML document tree. To support the creation of these objects, ColdFusion MX includes two additions to its function library: XmlNew() and XmlElemNew(). XmlNew() creates and returns a new ColdFusion XML document object, whereas XmlElemNew() creates and returns a child element that can be inserted anywhere within the XML document. XmlElemNew() accepts two arguments.The first, xmlObj, is a valid CFML XML object (such as the one created with XmlNew()).The second, childName, is the name of the child element to be created. CFML XML document and element objects have a number of properties that you should be aware of.These are listed in Tables 22.1 and 22.2.
Table 22.1 XML Document Properties Key
XmlComment XmlRoot

Type String Element

Value A comment to be associated with this XML document object. The root element in the XML document.The root element corresponds to the documents root tag.

298

Chapter 22

XML

Table 22.2 XML Element Properties Key


XmlAttributes XmlChildren XmlComment XmlName XmlNsPrefix XmlNsURI XmlText

Type Structure Array String String String String String

Value A structure of name/value pairs representing an elements attribute list. An array of child elements.This array can be manipulated directly to maintain the elements children. A string representing a comment to be associated with this element. The name of this element. The namespace prefix for this element. The namespace URL for this element. The text value for this element.

22.3. Creating an XML Object from Another XML Object


You want to create an XML object using another object as a starting point.

Technique
Use the Duplicate() function to copy an XML document to another XML document.
<cfset personXML2 = Duplicate(personXML)> <cfset personXML2.xmlRoot.XmlChildren[1].XmlText = Johnathon> <cfdump var=#personXML#> <cfdump var=#personXML2#>

Comments
The Duplicate() function is a standard function that lets you copy the contents of any CFML variable.The Duplicate() function ensures that the copied value contains no references to the original variable.This is shown in the technique above, in which the text value of a node is altered in the copied XML document. Dumping the contents of both structures reveals that the change did not affect the original document.

22.5.

Accessing Attributes

299

22.4. Parsing an XML Document


You want to parse an XML document into a CFML XML document object.

Technique
Use the XmlParse() function to parse an XML text string into a ColdFusion XML document object.
<cfset filename = GetDirectoryFromPath(GetCurrentTemplatePath())&person.xml> <cffile action=read file=#filename# variable=xmltext> <cfset personXML = XmlParse(xmltext)> <cfdump var=#personXML#>

Comments
The XmlParse() function lets you convert an XML document in text format into an XML document object.The XML text can come from any source available to ColdFusion, such as an HTTP request result, a database query, or a file.The XmlParse() function accepts the XML string as its only argument and returns a CFML XML document object as its result.

22.5. Accessing Attributes


You want to add or modify an XML nodes attributes.

Technique
Modify an XML elements attributes collection by modifying the XmlAttributes CFML structure.
<cfparam name=person> <cfxml variable=personXML> <person> <cfoutput> <firstName>#person.firstName#</firstName> <lastName>#person.lastName#</lastName> <city>#person.city#</city> <state>#person.state#</state> <country>#person.country#</country> </cfoutput> </person> </cfxml> <cfset personXML.xmlRoot.xmlAttributes.id = 10012> <cfdump var=#personXML#>

300

Chapter 22

XML

Comments
All ColdFusion MX element objects contain an implicit structure called XmlAttributes.This CFML structure contains the name/value pairs of the nodes attributes collection. Adding and modifying the elements attributes is as easy as modifying the CFML structure. In the previous technique, an ID attribute is added to the <person> element by setting an id property in the xmlAttributes structure.

22.6. Accessing Children


You want to add or modify one or more children of an XML node.

Technique
Use an elements XmlChildren array to modify its list of children.
<cfscript> myXML = XmlNew(); myXML.xmlRoot = XmlElemNew(myXML, myRoot); myXML.xmlRoot.XmlChildren[1] = XmlElemNew(myXML, childNode); myXML.xmlRoot.XmlChildren[1].XmlText = Child Node Text; </cfscript> <cfdump var=#myXML#>

Comments
All element objects contain an XmlChildren property, which is an array of its children element objects.This array can be accessed and modified directly. In the previous technique, a child is added to the root node with the following statement:
myXML.xmlRoot.XmlChildren[1] = XmlElemNew(myXML, childNode);

22.7. Accessing Inner Text


You want to access an XML nodes inner text value.

Technique
Use an XML elements XmlText property to set its text value.
<cfscript> myXML = XmlNew(); myXML.xmlRoot = XmlElemNew(myXML, myRoot); myXML.xmlRoot.XmlText = This is the inner text; </cfscript> <cfdump var=#myXML#>

22.8.

Searching XML with XPath

301

Comments
The XmlText property of an XML element represents the text that can be found in the tag body. All XML elements contain this property and you can set it directly in your code.The previous technique sets the root nodes inner text value to the string This is the inner text.

22.8. Searching XML with XPath


You want to search an XML document.

Technique
Use the XmlSearch() CFML function and the XPath query language to search an XML document.
<cfxml variable=capitalsXML> <capitals> <capital> <name>Raleigh</name> <state>NC</state> </capital> <capital> <name>Augusta</name> <state>GA</state> </capital> <capital> <name>Columbia</name> <state>SC</state> </capital> </capitals> </cfxml> <cfset results = XmlSearch(capitalsXML, /capitals/capital[state=NC])> <cfdump var=#results#>

Comments
The XPath query language is a powerful tool for dissecting and querying XML documents. Because a full discussion of this language is beyond the scope of this book, more information about XPath can be found at the W3C Web site at http://www.w3.org/TR/xpath. ColdFusion MX supports XPath queries via the new XmlSearch() function.The function takes two arguments: the XML document object to search and the XPath expression to search for.The function returns a CFML array of XML element objects.

302

Chapter 22

XML

The previous technique searches a list of state capitals for the capital city of North Carolina. The XPath expression /capitals/capital[state=NC] searches the children of the capitals root node for all capital elements that contain a state child node with the text value of NC.The result of this query is an array with a single value that is the XML element object of the capital element corresponding to Raleigh, NC.

22.9. XSL Transformations


You want to transform one XML document into another XML document using an XSL transformation.

Technique
Use the XmlTransform() function and the XSLT transformation language to transform XML documents.
<cfxml variable=people> <people> <person> <firstName>John</firstName> <lastName>Smith</lastName> <city>New York</city> <state>NY</state> <country>USA</country> </person> <person> <firstName>Steve</firstName> <lastName>Jones</lastName> <city>Boston</city> <state>MA</state> <country>USA</country> </person> </people> </cfxml> <cfsavecontent variable=xslt> <xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform version=1.0> <xsl:output method=html/> <xsl:template match=/> <html> <body>

22.9.

XSL Transformations

303

<xsl:apply-templates /> </body> </html> </xsl:template> <xsl:template match=people> <table> <tr> <th>First Name</th> <th>Last Name</th> <th>City</th> <th>State</th> <th>Country</th> </tr> <xsl:apply-templates /> </table> </xsl:template> <xsl:template match=person> <tr> <xsl:choose> <xsl:when test=position() mod 4 > 0> <xsl:attribute name=bgcolor>ffffff</xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name=bgcolor>a1a1a1</xsl:attribute> </xsl:otherwise> </xsl:choose> <td><xsl:value-of select=firstName/></td> <td><xsl:value-of select=lastName/></td> <td><xsl:value-of select=city/></td> <td><xsl:value-of select=state/></td> <td><xsl:value-of select=country/></td> </tr> </xsl:template> </xsl:stylesheet> </cfsavecontent> <cfset result = XmlTransform(people,xslt)> <cfoutput>#result#</cfoutput>

Comments
The XSL Transformation language (XSLT) is an XML-based language that lets you convert one XML document into another.This may sound trivial, but its implications are

304

Chapter 22

XML

far-reaching. Like XPath, the XSLT specification is maintained by the W3C and can be found at http://www.w3.org/TR/xslt. XSLT uses a combination of its own tag syntax and the XPath expression language to enable the recursive or iterative construction of a new XML document. One of the most compelling uses for XML transformations in your ColdFusion applications is for separating presentation from content. By using XML to model the content in your Web pages, you can reduce the pages to their barest essentials, thereby removing all HTML formatting.The HTML presentation rules can then be relocated into one or more XSLT transformation pages.Thus, you divide the job of page creation neatly into its natural components: page creation and aesthetic formatting. The previous technique provides a complete example of transforming a generic XML document into an HTML document suitable for display in a Web browser.The first section of the technique uses the <cfxml> function to create an XML document that contains a list of names and addresses.The second section of the example creates an XSLT stylesheet and saves it in a variable called xslt. Finally, the following expression
XmlTransform(people,xslt)

applies the rules defined in the XSLT transformation to the people XML document and returns the result, which is subsequently inserted in the page.

23
Web Services
23.0. Introduction
ColdFusion MX provides one of the simplest platforms for invoking and deploying Web services. Not to mistake simplicity with weakness, the ColdFusion MX Web Services platform is also robust, and based on the Apache Axis SOAP (Simple Object Access Protocol) implementation. Discussions of Web services often include the acronyms WSDL and SOAP. SOAP is an XML messaging protocol used most frequently when the messages are sent over the HTTP or HTTPS protocols (however, other transport protocols, such as SMTP, can be used). SOAP, in and of itself, is not a Web service; it is simply one way to leverage them by providing message encoding and remote procedure calls (RPC).WSDL, the Web Services Description Language, is used to describe the capabilities of the Web service and the formats for exchanging data. From the WSDL, a client has all the information needed to communicate with the Web service. The key advantage to using Web services is interoperability.Web services are exposed through standard protocols such as HTTP, and work with standard data formats such as XML. Any platform that can send a message to the service and interpret its response can interoperate. Most languages have toolkits, or APIs, for serving or consuming Web Services.This includes Java, C#/.NET, JavaScript, Perl, PHP, and several more. By leveraging XML,Web services provide a standards-based wrapper around proprietary platforms.With broad support from all major software vendors, its clear that Web services will be around for a few years.

306

CHAPTER 23

Web Services

23.1. Invoking a Web Service with CFINVOKE


You want to invoke a method on a Web service.

Technique
Use the <cfinvoke> tag to invoke a single method on a Web service.
<cfinvoke webservice= http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl method=getQuote symbol=MACR returnvariable=quote> <cfoutput>Current Stock Price for MACR is: #quote#</cfoutput>

Comments
The <cfinvoke> tag is used to call a method on an object.When used with the webservice attribute, <cfinvoke> can be used to call methods on Web services.The webservice attribute in this technique holds a URL to a Web Services Description Language (WSDL) XML document. As its name suggests, a WSDL file describes all aspects of a Web service, including object definitions, the location of the remote object, and a means, or protocol, to reach the object. This example makes use of a public Web service to retrieve a stock quote. Because a Web service can have multiple methods that you can invoke, the method name to invoke is specified with the method attribute. Passing arguments to methods can be accomplished in three ways.This technique passes the argument as an attribute in the <cfinvoke> tag, in this case the symbol attribute.The attribute must have the same name as the argument defined in the WSDL file. An alternative method to pass arguments to a <cfinvoke> request involves passing a structure to the argumentcollection attribute.The third way to pass arguments to <cfinvoke> uses the <cfinvokeargument> tag as the following code sample illustrates.
<cfinvoke webservice= http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl method=getQuote returnvariable=quote> <cfinvokeargument name=symbol value=MACR> </cfinvoke>

The returnvariable attribute in <cfinvoke> defines the name of a variable holding the result of the method call.

23.2.

Creating an Instance of a Web Service

307

Not all Web services are intended to be public. If you need to invoke a Web service that requires a username and password, you can use the username and password attributes of <cfinvoke> to do so.You can manage usernames and passwords for Web services in the ColdFusion Administrator by creating Web services mappings (map an arbitrary name to WSDL file), and entering a username and password. If a username and password are supplied in both the ColdFusion Administrator and the <cfinvoke> tag, the <cfinvoke> tag takes precedence. The getQuote method returns a value of type xsd:float; this value is automatically converted to a string or numeric by ColdFusion. See Table 23.1 for conversion between ColdFusion and WSDL types. Any complex type received by a Web service is converted into a ColdFusion structure.
Note
The <cfdump> tag is an easy way to inspect what a Web service returned. Its especially handy when dealing with complex types.

23.2. Creating an Instance of a Web Service


You want to store an instance of a Web service in order to perform multiple method calls on it.

Technique
Use the <cfobject> tag or CreateObject function to create an instance of an object.
<cfobject webservice=http://www.xmethods.net/sd/2001/TemperatureService.wsdl name=tempService> <cfoutput> Temperatures<br> Sauquoit, NY: #tempService.getTemp(13456)#<br> Anchorage, AK: #tempService.getTemp(99501)#<br> </cfoutput>

Comments
If you want to invoke multiple methods on a Web service or invoke the same method multiple times, it is a good idea to store an instance of the Web service in a ColdFusion variable. An instance of a Web service can be created with the <cfobject> tag via the webservice attribute.The webservice attribute is used to pass either a URL to a WSDL file, or the name of a Web service alias defined in the ColdFusion Administrator.

308

CHAPTER 23

Web Services

You can also use the CreateObject function to create an instance of a Web service, as shown here:
<cfset service = CreateObject(webservice, http://site.com/wsdl)>

The first argument of CreateObject specifies the type of object to create. For a Web service, you use the string webservice.The second argument of the CreateObject function is the WSDL URL or Web Service name. Once a handle to the Web service is obtained, you can call methods on the Web service using the instance.methodName(arguments) notation (shown in the previous code example), or using the <cfinvoke> tag shown here:
<cfinvoke webservice=#tempService# method=getTemp zipcode=13456 returnvariable=temp>

To invoke an instance method with <cfinvoke>, you pass the variable holding the instance (in this case, the variable tempService).

23.3. Writing a Web Service


You want to create a Web service with ColdFusion.

Technique
To write a Web service, create a CFC method with access type remote.
<cfcomponent> <cffunction access=remote name=getDogYears output=false returntype=numeric> <cfargument name=humanYears type=numeric required=true> <cfreturn arguments.humanYears * 7> </cffunction> </cfcomponent>

Comments
Writing a Web service is surprisingly simple if youre already familiar with ColdFusion Components (covered in Chapter 19, Components) and user-defined functions

23.4.

Using Properties

309

(covered in Chapter 16, User-Defined Functions). By using access=remote in the <cffunction> tag within a CFC, the method is exposed as a Web service. There are a few rules for Web services that differ from CFCs and UDFs. First, the returntype attribute must be present in the <cffunction> tag. Because most languages, unlike ColdFusion, are strongly typed, this is a requirement for Web services. If the Web service does not return a value, use the return type void.Web services also do not allow optional function arguments.
Note
The ColdFusion compiler does not warn you when you omit the returntype, or attempt to use an optional argument within a remote method. You will, however, receive errors when you attempt to invoke the Web service.

23.4. Using Properties


You want to define your own data types for your Web service.

Technique
Use the <cfproperty> tag to create your own complex types for your Web services.
Flight.cfc:
<cfcomponent> <cfproperty <cfproperty <cfproperty <cfproperty <cfproperty <cfproperty </cfcomponent> name=departs type=date> name=arrives type=date> name=departsFrom type=string> name=destination type=string> name=price type=numeric> name=flightNumber type=numeric>

TravelService.cfc:
<cfcomponent> <cffunction access=remote name=findFlight returntype=Flight output=false> <cfargument name=departs type=date required=true> <cfargument name=arrives type=date required=true> <cfargument name=departsFrom type=string required=true> <cfargument name=destination type=string required=true> <!--- lookup matching flights in db, then populate flight info ---> <cfset flight = CreateObject(component, Flight)> <cfset flight.departs = CreateDateTime(2003,3,6,18,0,0)>

310

CHAPTER 23

Web Services

<cfset flight.arrives = CreateDateTime(2003,3,6,22,0,0)> <cfset flight.departsFrom = SYR> <cfset flight.destination = DEN> <cfset flight.price = 200> <cfset flight.flightNumber = 123> </cffunction> </cfcomponent>

Comments
Web services built with ColdFusion can return custom data types, represented by a CFC using <cfproperty> tags.The <cfproperty> tag is used to describe property names and data types. By specifying multiple properties within a component, you can create your own data type.This technique creates a data type called Flight that stores information about a flight. Properties can be accessed with dot notation, as shown in the findFlight method of the TravelService CFC. Although you can simply return a structure of values, using properties is a better approach. Properties allow you to return a data structure with documented elements and data types. Structures can represent different data types and a variable number of elements. The findFlight method in the TravelService Web service returns a Flight object. In WSDL, the structure of the Flight Component is defined in XML schema as follows.
<complexType name=flight> <sequence> <element name=Destination nillable=true type=xsd:string /> <element name=Departs nillable=true type=xsd:dateTime /> <element name=Price nillable=true type=SOAP-ENC:double /> <element name=DepartsFrom nillable=true type=xsd:string /> <element name=Arrives nillable=true type=xsd:dateTime /> <element name=FlightNumber nillable=true type=SOAP-ENC:double /> </sequence> </complexType>

When other platforms receive the Flight object, they use this description to map data types to property names.The struct and query ColdFusion data types map to their own complexType depending on the contents of the variable.The data type mappings are defined in Table 23.1.
Table 23.1 ColdFusion WSDL Type Mappings ColdFusion Type
string numeric

WSDL Type
xsd:string or SOAP-ENC:string SOAP-ENC:double

23.6.

Deploying a Web Service

311

Table 23.1 Continued ColdFusion Type


date boolean array binary struct query

WSDL Type
xsd:dateTime SOAP-ENC:boolean SOAP-ENC:array xsd:base64Binary complexType complexType

23.5. Deploying a Web Service


You want to deploy a Web service on a ColdFusion application server.

Technique
Add ?wsdl to the end of the URL pointing to a CFC to generate a WSDL file.
<cfobject webservice=http://localhost/DogService.cfc?wsdl name=dog> <cfoutput>#dog.getDogYears(8)#</cfoutput>

Comments
Deploying Web services in ColdFusion is very simple.The WSDL can be automatically generated by appending ?wsdl to the end of a URL pointing to the Web service CFC. Sometimes it makes sense to modify the WSDL generated by ColdFusion to use stronger typing. For instance, you might want to change SOAP-ENC:double to SOAP-ENC:integer if your Web service doesnt use decimal points.You might also want to add typing to an array; ColdFusion arrays do not require that all elements be of the same type, yet many languages do. Changes to the WSDL can increase the performance, and possibly the interoperability, when communicating with other languages.
Note
If you modify the WSDL generated by ColdFusion, it is up to your application to ensure that the types returned from your application meet the specifications defined in the WSDL. WSDL is a contract that the Web service must adhere to.

312

CHAPTER 23

Web Services

23.6. Invoking a Web Service with .NET


You want to call a Web service written in ColdFusion with the .NET platform.

Technique
Use Visual Studio .NET. Here is getDogYears.aspx:
<%@ Page language=c# Codebehind=getDogYears.aspx.cs AutoEventWireup=false Inherits=DogServiceClient.getDogYears %> <!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.0 Transitional//EN > <HTML> <HEAD> <title>Dog Service C# Client</title> </HEAD> <body> <h1>Dog Service C# Client</h1> <form id=Form1 method=post runat=server> Enter a value in person years: <asp:TextBox id=TextBox1 runat=server/> <INPUT id=Submit1 type=submit value=Submit name=Submit1 runat=server/> </form> <%if (dogYears > 0) {%> <%=personYears%> person years equals <%=dogYears%> dog years <% }%> </body> </HTML>

Here is getDogYears.aspx.cs:
using System; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace DogServiceClient { /// <summary> /// consumes the getDogYears Web Service /// </summary> public class getDogYears : System.Web.UI.Page { protected HtmlInputButton Submit1; protected TextBox TextBox1; protected double dogYears = 0; protected double personYears = 0; private void Page_Load(object sender, System.EventArgs e) {}

23.6.

Invoking a Web Service with .NET

313

#region Web Form Designer generated code override protected void OnInit(EventArgs e) { InitializeComponent(); base.OnInit(e); } /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.Submit1.ServerClick += new System.EventHandler(this.Submit1_ServerClick); this.Load += new System.EventHandler(this.Page_Load); } #endregion private void Submit1_ServerClick(object sender, System.EventArgs e) { personYears = double.Parse(TextBox1.Text); localhost.DogServiceService svc = new localhost.DogServiceService(); dogYears = svc.getDogYears(personYears); } } }

Comments
Invoking a ColdFusion Web service in the .NET environment is a relatively simple process when using Microsoft Visual Studio .NET.This example invokes the DogService.cfc Web service (defined in section 3) from a .NET Web application. The first step in accessing a Web service is to add a Web Reference to your Visual Studio project. Do this by right-clicking References in the Solution Explorer, and selecting Add Web Reference. A dialog box prompts you for the address to the Web service. In this case, you use http://localhost:8500/DogService.cfc?wsdl, which is the location of the WSDL file describing the DogService.Visual Studio generates a stub object from the WSDL called DogServiceService, in the DogServiceClient. localhost namespace shown next. Here is Web References\localhost\Reference.cs:
namespace DogServiceClient.localhost { using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel;

314

CHAPTER 23

Web Services

using System.Web.Services; [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute(code)] [System.Web.Services.WebServiceBindingAttribute( Name=DogService.cfcSoapBinding, Namespace=http://DefaultNamespace)] public class DogServiceService : System.Web.Services.Protocols.SoapHttpClientProtocol { public DogServiceService() { this.Url = http://localhost:8500/DogService.cfc; } [System.Web.Services.Protocols.SoapRpcMethodAttribute(, RequestNamespace=http://DefaultNamespace, ResponseNamespace=http://DefaultNamespace)] [return: System.Xml.Serialization.SoapElementAttribute(return)] public System.Double getDogYears(System.Double years) { object[] results = this.Invoke(getDogYears, new object[] {years}); return ((System.Double)(results[0])); } public System.IAsyncResult BegingetDogYears(System.Double years, System.AsyncCallback callback, object asyncState) { return this.BeginInvoke(getDogYears, new object[] {years}, callback, asyncState); } public System.Double EndgetDogYears(System.IAsyncResult asyncResult) { object[] results = this.EndInvoke(asyncResult); return ((System.Double)(results[0])); } } }

In the codebehind file, getDogYears.aspx.cs, the DogServiceService class is instantiated and the getDogYears method is called using the instance referencethe variable svc.The result of the getDogYears method is stored in the variable dogYears.The value of dogYears, if greater than zero, is displayed in the getDogYears.aspx page.

23.7.

Invoking a Web Service with Java

315

23.7. Invoking a Web Service with Java


You want to call a Web service written in ColdFusion with Java.

Technique
Use the Apache AXIS WSDL2Java tool to create a Java stub, and then use the stub to invoke the remote method.
import dogservice.*; public class DogServiceClient { public static final void main(String[] args) throws Exception { Double humanYears = new Double(8); Double dogYears = null; dogservice.DogService binding; binding = new dogservice.DogServiceServiceLocator().getDogServiceCfc(); dogYears = binding.getDogYears(humanYears); System.out.println(Response from DogService = + dogYears); } }

Comments
Apache Axis is an open source project for implementing and invoking Web services using SOAP with Java. Macromedia is a contributor to the Axis project, and ColdFusion MXs Web services infrastructure is built with Axis. This example uses DogService.cfc defined in section 3 and requires Apache Axis. You can download Axis at xml.apache.org/axis/; these examples were created with Axis version 1.0. The easiest way to invoke a Web service in Java using Axis is with the WSDL2Java utility that comes with Axis.You can run the WSDL2Java tool from your command line as follows:
java org.apache.axis.wsdl.Wsdl2Java http://x/y.wsdl

316

CHAPTER 23

Web Services

The tool can also run via an Apache Ant script. Ant is a build tool for Java (download it at ant.apache.org).The following segment of code runs WSDL2Java within an Ant target:
<taskdef name=wsdl2java classname=org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask/> <wsdl2java url=http://localhost:8500/DogService.cfc?wsdl output=${src} deployscope=session noimports=no verbose=no testcase=no> <mapping namespace=http://DefaultNamespace package=dogservice/> </wsdl2java>

Both methods of running the WSDL2Java tool require the contents of the Axiss lib directory, and a JAXP compliant XML parser to be included in the Java classpath. When WSDL2Java executes, it generates several Java source files that can be used to invoke the Web service.The two files you will use directly are the DogService interface and the DogServiceLocator class.The DogService interface simply defines the structure of the public methods in your Web service.The interface that Axis generates is as follows:
public interface DogService extends java.rmi.Remote { public java.lang.Double getDogYears(java.lang.Double years) throws java.rmi.RemoteException, dogservice.CFCInvocationException; }

The getDogServiceCfc method in the DogServiceLocator class instantiates a stub class that implements the DogService interface.

24
J2EE Interoperability
24.0. Introduction
For years, ColdFusion thrived as a Web application server, despite the momentum that Microsofts ASP and Suns Servlet/JSP competitive technologies were gaining. ColdFusions tag-based syntax made it a favorite among less technical Web designers. In order to secure ColdFusions long-term future, Macromedia has embraced Suns J2EE suite of standards. Macromedias JRun Server is a J2EE application server and, with the MX release, the ColdFusion Server is now a J2EE application that can run on a variety of J2EE-certified application servers. The ColdFusion interpreter has been reengineered as a collection of Java Servlets. Thus, all ColdFusion MX applications are J2EE applications, albeit indirectly.This architectural decision opens entirely new Java integration possibilities to ColdFusion programmers. ColdFusion MX provides for end-to-end integration with J2EE JSPs and Servlets. Servlets and JSPs can access ColdFusion session and application state and vice versa. In addition, ColdFusion pages can include JSPs and Servlets or forward processing control to them. In addition, ColdFusion MX offers tighter integration with businesstier components, such as Enterprise Java Beans. This chapter reviews the different techniques you can use to utilize J2EE components within your ColdFusion application. It covers how to access JSPs and Servlets, how to share application state with J2EE components, and how to access Enterprise Java Beans.

318

Chapter 24 J2EE Interoperability

24.1. Redirecting to a Servlet or JSP


You want to forward the ColdFusion request to a Java Servlet or JSP.

Technique
Use the following CFML code fragment to perform a server-side forward to a JSP or Servlet.
<cfscript> GetPageContext().forward(date.jsp); </cfscript>

Comments
The GetPageContext() function is a new addition to the CFML language. It provides access to the ColdFusion MX PageContext object, allowing you to see and manipulate page attributes and the request and response Servlet objects. The MX PageContext object wraps the Java PageContext object and exposes two functions for you to use: forward() and include(). Use the forward() function (shown in the previous technique) to forward the processing control to a Servlet or JSP deployed on the underlying J2EE application server. You can add JSPs to the standalone version of ColdFusion MX by simply adding them to your ColdFusion project. Servlets must be declared in the web.xml file under the WEB-INF folder in the ColdFusion MX wwwroot folder. Unpacked Servlet classes must be added to the CFusionMX/wwwroot/WEB-INF/classes folder. Alternatively, JARed Servlet libraries can be placed in the CFusionMX/wwwroot/WEB-INF/lib folder.You must use one of the previous methods to deploy your Servlet classes before you can access them in your ColdFusion application.

24.2. Including a Servlet or JSP


You want to include a Java Servlet or JSP in the context of your ColdFusion page.

Technique
Use the following CFML code fragment to perform a server-side include of a JSP or Servlet.
Including a jsp... <cfscript> GetPageContext().include(date.jsp); </cfscript>

24.3. Accessing ColdFusion Pages from Servlets and JSPs

319

Comments
Like the <cfinclude> tag, the MX PageContext include() function can be used to include the output of a Servlet or JSP in a CFML page.This technique uses the function to include the output of the date.jsp JSP page.

24.3. Accessing ColdFusion Pages from Servlets and JSPs


You want to access a ColdFusion page from a Servlet or JSP.

Technique
factorial.cfm:
<cfparam name=num> <cfset result = 1> <cfloop from=2 to=#num# index=i> <cfset result = result * i> </cfloop> <cfoutput>#num#! = #result#</cfoutput>

include.jsp:
<jsp:include page=factorial.cfm> <jsp:param name=num value=10/> </jsp:include>

Comments
ColdFusions Servlet/JSP integration is bi-directional.This means that you can include (or forward to) a ColdFusion page directly from a Servlet or JSP.The technique used to access a CFML page from a Servlet or JSP is identical to the one used to access another Servlet or JSP.This technique uses the standard <jsp:include/> JSP tag to include the results of a ColdFusion page called factorial.cfm. Note that you can use the <jsp:param/> tag to set any parameters the ColdFusion page needs in order to operate.

320

Chapter 24 J2EE Interoperability

24.4. Sharing Data Between ColdFusion and Servlets/JSPs


You want to share common application data with JSPs and Servlets.

Technique
Setvars.cfm:
<cfapplication name=cfsession sessionmanagement=yes> <cfset Request.requestvar = Request Variable> <cfset Session.sessionvar = Session Variable> <cfset Application.applicationvar = Application Variable> <cfscript> GetPageContext().include(/PrintVars); </cfscript>

PrintVars.java:
package org.acme; import import import import import import import java.io.IOException; java.io.PrintWriter; java.util.*; javax.Servlet.ServletException; javax.Servlet.http.HttpServlet; javax.Servlet.http.HttpServletRequest; javax.Servlet.http.HttpServletResponse;

public class PrintVars extends HttpServlet { protected void processRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Get POST and GET parameters PrintWriter out = response.getWriter(); out.print(<b>Page Parameters:</b><br>); Enumeration params = request.getParameterNames(); while (params.hasMoreElements()) { String name = (String)params.nextElement(); String value = (String)request.getParameter(name); out.print(name+ = +value+<br/>); } // Get Request Attributes out.print(<p>);

24.4.

Sharing Data Between ColdFusion and Servlets/JSPs

321

out.print(<b>Page Attributes:</b><br>); Enumeration attrs = request.getAttributeNames(); while (attrs.hasMoreElements()) { String name = (String)attrs.nextElement(); String value = (String)request.getAttribute(name); out.print(name+ = +value+<br/>); } // Get Session Variables out.print(<p>); out.print(<b>CF Session Variables:</b><br>); Map sessionVars = (Map)request.getSession().getAttribute(cfsession); Set sessionAttrs = sessionVars.keySet(); for (Iterator i = sessionAttrs.iterator(); i.hasNext();) { String name = (String)i.next(); String value = sessionVars.get(name).toString(); out.print(name+ = +value+<br/>); } } protected void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request,response); } protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request,response); } }

Comments
An exciting new feature in ColdFusion MX is the capability to share application and session state among ColdFusion pages and J2EE Servlets and JSPs.The storage mechanism for ColdFusion application state has been redesigned in MX to utilize the standard J2EE request parameters, request attributes, and HTTP session objects. This allows you to mix ColdFusion pages with Servlets and JSPs in a single application while maintaining consistent session state.Thus, you can draw upon each of the Web page development technologies when the need arises. For example, you might use ColdFusion to rapidly assemble a front-end that gathers information to be passed to a Servlet-driven back-end business method.

322

Chapter 24 J2EE Interoperability

In the technique shown here, ColdFusion is used to populate an assortment of session and request variables, which is subsequently inspected by a Java Servlet.The example uses the expression GetPageContext().include(/PrintVars) to include the PrintVars Servlet that prints the application state.The PrintVars Servlet logic is divided into three sections that handle printing the request parameters, request attributes, and session variables. Request parameters, such as URL and form variables, are available in the Parameter scope of the request object. Individual parameters can be accessed by calling the request.getParameter(parameterName) function. Likewise, you can extract ColdFusion request variables (such as the one created with the CFML statement <cfset request.requestVar = Request Variable>) with the Request.getAttribute(attributeName) function. ColdFusion session variables, however, are handled slightly differently.The ColdFusion runtime engine keeps all session state in a single Servlet session variable matching the name of the ColdFusion application.This example assigned a name to the CFML page and created a session scope for it by using the <cfapplication> tag: <cfapplication name=cfsession sessionmanagement=yes>. Behind the scenes, ColdFusion created a Java map to store session state and added it to the Servlet session scope under the name cfsession. This Servlet retrieves the ColdFusion session state block with the expression: Map sessionVars = (Map)request.getSession().getAttribute(cfsession). Individual CFML session variables can thenceforth be accessed by their names. For example, the Servlet can get the value of the sessionvar ColdFusion session variable by using the expression sessionVars.get(sessionvar).

24.5. Accessing Enterprise Java Beans


You want to access an Enterprise Java Bean from your ColdFusion template.

Technique
Math.java:
package org.acme.beans.math; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface Math extends EJBObject { public int add(int a, int b) throws RemoteException; public double add(double a, double b) throws RemoteException; public int factorial(int number) throws RemoteException; }

24.5.

Accessing Enterprise Java Beans

323

MathHome.java:
package org.acme.beans.math; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface MathHome extends EJBHome { public Math create() throws CreateException, RemoteException; }

MathBean.java:
package org.acme.beans.math; import javax.ejb.CreateException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; public class MathBean implements SessionBean { transient protected SessionContext ctx; public public public public public void void void void void ejbCreate() throws CreateException {} ejbRemove() {} ejbActivate() {} ejbPassivate() {} setSessionContext(SessionContext ctx) {this.ctx = ctx;}

public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } public int factorial(int number) { int result = 1; for (int i = 2; i <= number; i++) { result *= i;

324

Chapter 24 J2EE Interoperability

} return result; } }

CallEJB.cfm:
<cfscript> ctx = CreateObject(java,javax.naming.Context); props = CreateObject(java,java.util.Properties); props.init(); props.put(ctx.INITIAL_CONTEXT_FACTORY, jrun.naming.JRunContextFactory); props.put(ctx.PROVIDER_URL, localhost:2908); initContext = CreateObject(java,javax.naming.InitialContext); initContext.init(props); mathHome = initContext.lookup(MathBean); mathBean = mathHome.create(); result = mathBean.factorial(10); WriteOutput(Result = &result); </cfscript>

Comments
Suns Enterprise Java Beans specification has matured over the years and has seen a corresponding growth in its adoption.The technology, which has undergone two major releases, is an attractive tool to model complex business processes. A J2EE application server, such as Macromedias JRun Server, hosts your EJB business components and provides a wealth of infrastructure that you, the component author, can draw upon. Support for transactions, clustering, security, and database persistence are just a few of the powerful features mandated by the EJB specification. EJB-based components are accessible to a wide range of clients, from rich desktop applications to thin Web-based clients, including ColdFusion. ColdFusion template integration with EJB components is a more efficient process now that the ColdFusion interpreter is, itself, a Java process. One benefit of this implementation is that a ColdFusion client can access an EJBs local interface. EJB local interfaces are optimized for in-process calls and avoid the overhead imposed by their RMI-based remote counterparts. This example creates a small stateless session EJB that exposes methods for adding different types of numbers and finding the factorial of an integer. Although a detailed explanation of the mechanics of an EJB is beyond the scope of this chapter, a brief overview of the classes involved is in order. Math.java declares the interface that the application server will expose to remote clients once this EJB has been deployed. MathHome.java is also an interface that declares a single method, create(), that clients can use to obtain an instance of a Math EJB. Finally, the MathBean.java

24.5.

Accessing Enterprise Java Beans

325

abstract class implements the beans business logic.The class also contains the required EJB house-keeping methods (for example, ejbCreate()).These three files are placed into a single JAR file along with an XML deployment descriptor that describes the component to the application server. Once deployed, the MathBean EJB is available to its clients.This technique creates a ColdFusion template that looks up the bean and calls one of its methods.The lookup process requires several steps and a handful of classes, so it uses a <cfscript> block to isolate this logic. First, EJB clients must use the Java Naming Directory Interface (JNDI) to locate an instance of the beans home interface.The code is as follows:
ctx = CreateObject(java,javax.naming.Context); props = CreateObject(java,java.util.Properties); props.init(); props.put(ctx.INITIAL_CONTEXT_FACTORY, jrun.naming.JRunContextFactory); props.put(ctx.PROVIDER_URL, localhost:2908); initContext = CreateObject(java,javax.naming.InitialContext);

This code creates an instance of a JNDI InitialContext and initializes it with a Properties object.The context object must be initialized with a CONTEXT_FACTORY (usually application server-dependent) and a PROVIDER_URL (the Web address and port of the application servers Java Naming Provider [JNP] service). Once initialized, you can use the JNDI context object to look up your beans home interface.The beans name is specified in its deployment descriptor.The statement mathHome = initContext.lookup(MathBean) looks up the bean and returns an instance of its home interface.
Note
EJB clients never talk directly to a beans business logic. The Home and Remote objects that an EJB client uses are generated by the EJBs container. This level of indirection is necessary for the application server to implement the caching, transactions, and other services required by the specification.

Finally, you use the beans home interface to create an actual instance of the bean to call its factorial() method:
mathBean = mathHome.create(); result = mathBean.factorial(10); WriteOutput(Result = &result);

When run, the example should output the following message:


Result = 3628800

This seems like a lot of work for finding a simple factorial and it is. If you plan on using EJBs in your ColdFusion applications, you should create a custom tag to automate the

326

Chapter 24 J2EE Interoperability

process of looking up the beans home interface and calling the create() method.This will save you time in debugging error-prone code and will also make your application more flexible if you ever change the application servers implementation or the host IP address.

VI
Appendixes
A Online ColdFusion Resources B D Security Converting Between Tags and Scripting C Migrating from ColdFusion 5 to CFMX

A
Online ColdFusion Resources

nline resources are a great way to enhance your skills.This appendix includes Web sites that provide resources in the form of articles, discussion forums, mailing lists, tutorials, code sharing, and other forms.

ColdFusion Web Sites


CF Bug Hunt
http://www.cfbughunt.org/

Online public bug database for ColdFusion. CF Bookmark


http://www.cfbookmark.com/

A listing of links related to ColdFusion. CFCustomTags.com


http://www.cfcustomtags.com/

Includes a tag gallery, forums, and tutorials. CFCZone


http://www.cfczone.org/

Includes a CFC gallery, mailing list, and frequently asked questions (FAQs). CFDEV
http://www.cfdev.com/

Includes code samples, articles, tags, links, and products. CFDEVX


http://www.cfdevx.com/

A directory of ColdFusion links. CF FAQ


http://www.cffaq.com/

Frequently asked questions related to ColdFusion.

330

Appendix A

Online ColdFusion Resources

CFHub.com
http://www.cfhub.com/

Includes articles, tutorials, forums, and other resources. CFMCentral


http://www.cfmcentral.com/

Includes forums, tags, jobs, tips, resources, products, services, and events. ColdFusion Developers Journal
http://www.sys-con.com/coldfusion/

Includes articles, a mailing list, and forums. ColdFusion FAQ


http://www.thenetprofits.co.uk/coldfusion/faq/

Frequently asked questions and answers about ColdFusion. CFLib.org


http://www.cflib.org/

A library of free user-defined functions and resources. Defusion


http://www.defusion.com/

Includes news, articles, resources, and forums. DEVMX


http://www.devmx.com/

Includes tutorials, forums, articles, mailing list, and other resources related to Macromedia MX products. EasyCFM.com
http://www.easycfm.com/

Includes tutorials, references, forums, and links. Full as a Goog


http://www.fullasagoog.com/

Aggregator for Macromedia-related blogs. Fusion Authority


http://www.fusionauthority.com/

Includes weekly ColdFusion news, tips, and resources. House of Fusion


http://www.houseoffusion.com/

Includes several mailing lists, articles, and community resources. Live Documentation
http://livedocs.macromedia.com/

Online ColdFusion documentation that allows developers to post comments, clarifications, and corrections. Macromedia Designer and Developer Center
http://www.macromedia.com/desdev/

Includes articles for designers and developers using Macromedia products.

XML Web Sites

331

Macromedia Online Forums


http://webforums.macromedia.com/

Includes discussion forums for Macromedia products. Macromedia ColdFusion Support Center
http://www.macromedia.com/support/coldfusion/

Search technotes and knowledge base articles. Open Directory ColdFusion Category
http://dmoz.org/Computers/Programming/Internet/ColdFusion/

The Open Directory Projects listing of ColdFusion links. Secret Agents


http://www.secretagents.com/

Includes tutorials, free code, and articles.

Java Web Sites


The Apache Jakarta Project
http://jakarta.apache.org

Open Source Java software. Includes a JSP custom tag libraries, JSP and Servlet engine, and several other projects. JSP Tags
http://www.jsptags.com/

A listing of JSP custom tag libraries. JSP Resource Index


http://www.jspin.com/

A categorized directory of JSP resources. Open Directory Server-Side Java Category


http://dmoz.org/Computers/Programming/Languages/Java/Server-Side/

A directory of server-side Java Web sites, including JSP and Servlets. Sun Microsystems Java Site
http://java.sun.com/

Includes tutorials and articles for learning Java, as well as the tools needed to develop and run Java applications.

XML Web Sites


The Apache XML Project
http://xml.apache.org/

Open Source software relating to XML. Includes the AXIS project, which is the underbelly of the Web services implementation in ColdFusion MX. Open WDDX
http://www.openwddx.org/

The home of the WDDX project. Includes WDDX development kits for several languages.

332

Appendix A

Online ColdFusion Resources

Xmethods
http://www.xmethods.com

A listing of Web services, many of which are free. XML.com


http://www.xml.com/

Articles and resources for working with XML technologies.

Other Useful Web Sites


Evolt
http://www.evolt.org/

Includes articles, mailing lists, tips, and links. Jakob Nielsens Web Site
http://www.useit.com/

Includes usability and Web design articles. Section 508 Accessibility Information
http://www.section508.gov/

Information for building Web sites accessible to disabled visitors.

B
Security

n ever increasing amount of sensitive and private information is accessible to the public over the Internet. Awareness of best practices, as well as the common routes attackers take are necessities for Internet developers.The cost of applying security measures is orders of magnitude smaller than the potential cost of a security breach. This appendix covers some common security problems, but is by no means a complete list. Security is a constantly changing beast; to keep up-to-date, visit the Web sites in the Security Resources section of this appendix.

Session Hijacking
When you use session or client variables in ColdFusion, two variables are setCFID and CFTOKEN.The variables are stored as cookies on the clients computer, or passed in the URL. CFID and CFTOKEN are often passed through the URL to allow clients with cookies disabled to maintain state. Security concerns arise when someone has access to the values of CFID and CFTOKEN. By providing a CFID and CFTOKEN that matches an active session, you can hijack or spoof the session (bypassing the Web sites login mechanism). Unfortunately, there are several ways an attacker can obtain session identification numbers.The attacker can use a packet sniffer to gather URLs and session identifiers from the traffic on the network.The session IDs can also be obtained by looking in the browser history (if the user didnt log out). If the session IDs arent large and complex enough, hackers can even guess them. By following some best practices, as follows, you can minimize the potential possibility of session hijacking: Use SSL (Secure Socket Layer, HTTPS) for session-enabled sites. SSL encrypts all transmission of cookies and URLs, thus making it nearly impossible for an attacker to sniff your session identifiers. Use CFID and CFTOKEN in the URL only as a last resort. Because URLs are often cached, or stored by the browser, an attacker can possibly retrieve those values. Use
n n

334

Appendix B

Security

CFIDs and CFTOKENs only when youre required to use a session when cookies are not enabled.You can use the URLSessionFormat function with J2EE sessions to
n

print the session ID only when cookies are disabled. Use a UUID for CFTOKEN.You enable this setting in ColdFusion Administrator. By using a UUID, it is much more difficult to guess the CFTOKEN (it uses a much larger number). UUIDs are also guaranteed to be unique on your server. ColdFusion uses a random number, which could in some cases cause session crossover.You can alternatively use J2EE sessions to solve the same problems.

Another approach that isnt necessarily a best practice because it can cause problems with some ISPs is to store the clients IP address in a session variable.Then, at each page request, you verify that the clients IP address is the same as the address stored in the session variable. If the addresses do not match, you force another login.This can cause problems for ISPs that change IP addresses frequently.This approach also doesnt protect users on a shared IP address (proxies).

URL Injection Hacking


By appending special characters to a URL, attackers can run SQL commands of their choice on some database platforms.The most common (and most dangerous) form of this attack takes place on databases that allow multiple statements to be executed in one query (typically by separating SQL statements with a semicolon). Consider the following query:
<cfquery ...> SELECT column FROM table WHERE id = #url.id# </cfquery>

Under normal circumstances, the value of url.id would be some integer. However, because the value of the variable is in the hands of the attackers, they could pass a value like id=123;delete FROM table, which would delete all records from your table. The best defense to URL injection hacking is to use the <cfqueryparam> tag.The following example is immune from such an attack:
<cfquery ...> SELECT column FROM table WHERE id = <cfqueryparam cfsqltype=CF_SQL_INTEGER value=#url.id#> </cfquery>

The <cfqueryparam> tag performs data-type checking, and also escapes special characters in the value passed. More information about <cfqueryparam> is available in Chapter 14, Structured Query Language (SQL). Besides using the <cfqueryparam> tag as a general best practice, you should always ensure that ColdFusion doesnt have any excess permission to the database. Be restrictive

Denial of Service Attacks

335

with the DELETE and DROP permissions. Also ensure that the database system stored procedures are locked down. Another solution to this problem is to use stored procedures. Stored procedures are covered in detail in Chapter 15, Stored Procedures. Not all databases allow multiple SQL statements to execute. Microsoft SQL Server and Postgres are two that do allow multiple statements. In addition to multiple statement execution, it is sometimes possible to perform other malicious operations when unchecked variables exist in the SQL query. Attackers using this technique cant do any harm without knowing, or guessing, the database structure. Be sure that debugging information and error message details are turned off on production servers. Although this is a best practice, it doesnt provide any real security from attacks. It is very easy to guess monikers like database table names (products, users, orders, and so on).

File Uploading
Giving users the capability to upload files can be very dangerous without properly securing the code and the upload directory. The <cffile> tag, used to upload files from a form, has an attribute called accept. This attribute is used to specify allowable MIME types.The accept attribute should always be used, but it may provide a false sense of security.The MIME type it uses is sent from the client. Hackers can easily send a .CFM file claiming to have a MIME type of image/gif (or any other MIME type). Because of this dangerous fact, you should check the file extension of the uploaded file to ensure that it is expected.
<cfif cffile.ServerFileExt IS NOT gif> <cffile action=delete file=#cffile.ServerDirectory#\#cffile.serverfile#> Sorry incorrect file type. <cfabort> </cfif>

You can use the cffile.ServerFileExt attribute to determine whether the file uploaded has the correct extension, and if not, delete it. Its best to ensure that the directory that files are uploaded to is not under the Web root (if possible). If files must be uploaded to the Web root, the Web server should not allow execution of scripts (such as .CFM files).

Denial of Service Attacks


Denial of service, or DOS, attacks are categorized by attempts to overload and crash a server by utilizing as many resources as possible. Distributed DOSs involve a multiple attack by several machines, all trying to overload a more powerful server. Such attacks are very difficult, and sometimes impossible, to prevent.

336

Appendix B

Security

The key preventive measure for such attacks is to minimize the relationship of input to page loading time. An exaggerated example of a scenario to avoid follows:
<cfloop from=1 to=#url.loop# index=i> <!--- do something ---> </cfloop>

In this example, anyone can control the number of loops the application performs. A more practical example might include a page that performs several manipulations and transformations on an input string.To prevent problems, check the size of the string against its upper limit before performing the costly manipulations.

Cross Site Scripting


Cross site scripting (or sometimes referred to as XSS, or CSS, but not to be confused with cascading style sheets) attacks involve injecting client side code (HTML, JavaScript, VBScript, ActiveX, Flash, and so on) through a variable passed by the client.The variable is typically from a form submission or a URL query string. For example, the following code has a cross site scripting vulnerability. Consider the following form:
<form method=post action=formActionPage.cfm> <textarea name=variable></textarea> <input type=submit value=submit> </form>

And the following form action page:


<cfif IsDefined(form.variable)> <cfoutput> #form.variable# </cfoutput> </cfif>

The form action page is basically a blank slate for a malicious attacker to use. Consider an attacker posting the following:
<script> alert(document.cookie); </script>

This code will place the values for all cookies into a Java script alert box.This code isnt dangerous, but if instead it submitted the cookie information to a malicious Web site, you could be in trouble. Cookies arent the only risk faced by cross site scripting attacks. An attacker might also spoof a Web page and get you to enter personal information, thinking that youre using a valid Web site. It is very difficult for the average user to know whether they are

Security Resources

337

under such an attack, because the URL will appear to be valid, and SSL certificates will still validate. Many other possible scenarios exist as well. The remedy for CSS scripting attacks is fortunately very simple.The HTMLEditFormat function will automatically convert < and > characters into &lt; and &gt;, respectively. So instead of the code executing on the client, the code is simply displayed.To fix the previous form action page, you simply apply the HTMLEditFormat function to form.variable before displaying it.
<cfif IsDefined(form.variable)> <cfoutput> #HTMLEditFormat(form.variable)# </cfoutput> </cfif>

Sandbox Security
Sandbox security in ColdFusion MX allows a server administrator to disable tags, datasources, functions, files, and directories. Sandbox security also lets you disable access to servers and ports.These features can be disabled based on a file pattern such as (c:\inetpub\wwwroot\), called a sandbox. To set up sandbox security, go to the ColdFusion MX Administrator, and click the Sandbox Security link. Many developers think that the sandbox security setting in ColdFusion MX exists only for Internet Service Providers (ISP) offering shared hosting environments. However, it can be useful as a last line of defense on any ColdFusion server. It is a good idea to disable any tags or functions that your application isnt using, especially those that can harm the system such as <cffile>, <cfdirectory>, <cfexecute>, and <cfregistry>, to name a few. If your ColdFusion application server is behind a firewall with other servers, its also a good idea to use the sandbox server IP restrictions. For instance, if your intranet server is behind the same firewall as your public Web server, an attacker that has gained access to your public Web server can use <cfhttp> to access your intranet. For obvious reasons, sandbox security is also useful on servers that host multiple applications by multiple users. In order to ensure that users dont tamper with other users applications and files, a sandbox security policy must be implemented.

Security Resources
Microsoft Security Bulletin
https://www.microsoft.com/technet/security/bulletin/notify.asp

Sign up for the security newsletter to be notified about potential security flaws in Microsoft products.

338

Appendix B

Security

Macromedia Security Zone


http://www.macromedia.com/security

Any security patches or security bulletins are posted here.You can also sign up for email alerts. Windows Update
http://windowsupdate.microsoft.com/

Download the latest patches, updates, and security fixes for Windows servers. The CERT Coordination Center
http://www.cert.org/

Includes security best practices, vulnerabilities, and fixes. SecurityFocus Vulnerability Database
http://www.securityfocus.com/corporate/products/vdb

A searchable database containing security vulnerabilities.

C
Migrating from ColdFusion 5 to ColdFusion MX
oldFusion MX represents a drastic change in ColdFusions underlying technology. The ColdFusion engine was redesigned from the ground-up as an assembly of Java server-side components, replacing its older native C++ executable architecture.This kind of change in architecture from one release to another can often lead to incompatibilities. Fortunately, the developer team at Macromedia did a good job of maintaining backwards compatibility for the vast majority of functionality.There are some tags and functions, however, that have changed or are no longer supported in ColdFusion MX. For this reason, Macromedia included the Compatibility Analyzer and the Settings Migration Wizard to help developers convert their legacy applications to use CFMX. ColdFusion MX uses a stricter syntax checker than earlier versions of ColdFusion. Pages that once worked correctly, despite incorrect syntax, may now break under the MX parser.

ColdFusion Code Compatibility Analyzer


The ColdFusion Compatibility Analyzer will assess your CFML application for compatibility with ColdFusion MX.The analyzer will let you know if your application relies on features in the ColdFusion 5.0 CFML language that have been changed, deprecated, or removed in ColdFusion MX.The Compatibility Analyzer will also check your application for syntax errors. To access the Compatibility Analyzer, log into the ColdFusion administrator and click the Code Analyzer link under the Debugging and Logging header.

ColdFusion Settings Migration Wizard


Another significant change in the architecture of ColdFusion MX is how it stores its registry settings. After installing MX, you should run the Settings Migration Wizard to convert your ColdFusion 5 settings into MX.

340

Appendix C

Migrating from ColdFusion 5 to ColdFusion MX

To run the wizard, you must have MX running on the same machine as the instance of ColdFusion that you are upgrading. Depending on your platform, ColdFusion will prompt you to run the Migration Wizard upon completion of the installation (Windows) or the first time you log into the ColdFusion administrator (UNIX).

Changes to CFML Tags and Functions


There are numerous changes to the CFML language in MX.The majority of these are small changes in behavior of some of the tags. A small number of tags and functions, however, have been deprecated or removed entirely.The ColdFusion online documentation includes a comprehensive list of all the changes that were made to the CFML language.

Database Operations
ColdFusion MX uses a different database communications API than its predecessors. Older versions of ColdFusion used ODBC and OLE-DB drivers to provide database access. ColdFusion MX is a Java-based application and uses the Java Database Connectivity (JDBC) API to connect to databases. Because of this, you will need to obtain JDBC drivers from your database vendor or else use the Merant Type 4 driver or the SQL Link Type 3 driver to connect to ODBC data sources. ColdFusion MX Query objects now store query columns using their JDBC data types, as opposed to earlier versions of ColdFusion, which represented all columns as strings. ColdFusion MX converts non-string entities to strings by calling the Java toString() method on the object.This may lead to differences in the display of dates, numbers, and monetary units from database queries.

Localization
ColdFusion MX includes new internationalization support. MX supports new currency formats and formatting functions.The ColdFusion MX online documentation contains detailed information on these new internationalization features.

References
You should always check your ColdFusion release notes before upgrading for versionspecific information. Migration notes for ColdFusion MX can be found in the ColdFusion documentation at the following address:
/cfdocs/Migrating_ColdFusion_5_Applications/contents.htm

Macromedia has posted a detailed article on migrating to ColdFusion MX on its Web site. It can be found at:
http://www.macromedia.com/desdev/mx/coldfusion/articles/migrating.html

D
Converting Between Tags and Scripting

lthough the CFML tag syntax is most useful for adding dynamic content to otherwise static HTML pages, it can become a bit unwieldy when authoring a large, contiguous block of application logic.To this end, CFML scripting was introduced in ColdFusion version 4.0. CFML scripting allows you to embed a series of Java-like statements and expressions within your ColdFusion template. CFML scripts have the same access to application state as their tag-based counterparts.

The <cfscript> Tag


CFML scripts are always enclosed within the body of a <cfscript> tag.The <cfscript> tag may contain a valid series of CFScript statements.The values of CFScript statements are not displayed to the page.You must use the WriteOutput() function to output any information to the resulting Web page. Although scripts are not so great for handling display and formatting, they are the ideal choice for implementing large sections of application logic. For example, consider the following switch statement implemented using both tags and scripts: Using tags:
<cfset choice = form.choice> <cfswitch expression=#choice#> <cfcase value=0> <cfset color=red> </cfcase> <cfcase value=1> <cfset color=blue> </cfcase> <cfcase value=2> <cfset color=yellow>

342

Appendix D

Converting Between Tags and Scripting

</cfcase> <cfcase value=3> <cfset color=green> </cfcase> <cfdefaultcase> <cfset color=purple> </cfdefaultcase> </cfswitch>

Using a script:
<cfscript> choice = form.choice; switch(choice) { case 0: color=red; break; case 1: color=blue; break; case 2: color=yellow; break; case 3: color=green; break; default: color=purple; break } </cfscript>

Scripts make your application logic easier to follow and to maintain. Programmers should be familiar with script flow-control statements (such as the switch statement), as they are based on similar statements in the C and Java programming language. CFML scripts are composed of semicolon-delimited statements. CFML supports the following types of statements: assignment, function call, if-else, switch-case, for, for-in, while, do-while, break, continue, try-catch, function, var, and return.The last three statements are used within user-defined functions (covered in Chapter 16, User-Defined Functions). Assignment statements are used to assign a value to a variable. For example, the statement a = 3; assigns the value 3 to the variable a. A variable may be assigned either a literal value (such as the number 3) or the result of an expression. An expression is a group of functions, operators, and literals that, when evaluated, produce a value. In other words, an expression is anything that you can use on the right side of a <cfset> tag. For example, the statement a = Rand() * 10; assigns the value of the expression Rand() * 10 to the variable a.

Loops

343

Function-call statements are used to call a CFML or user-defined function that has no return value and, thus, cannot be assigned to a variable. For example, the statement WriteOutput(Hello World); writes the string Hello World to the page result.

If-Then Statements
If-Then-Else statements are very easy to implement in CFScript.The if statement

uses the following syntax:


if (expression) statement

where expression is a true/false, or Boolean, expression and statement is either a single statement or a group of statements enclosed in braces ({}). For example, the following if statement sets the value of color to blue if the variable choice is equal to the number 1:
if (choice eq 1) { color = blue; }

Note that the braces in this example are optional because the if statement includes only one line. An if statement may also contain an else statement that executes if the expression evaluates to false. In such cases, the syntax is:
if (expression) statement else statement

The following example uses the else statement to assign the value red to the variable color if the value of choice is anything but 1:
if (choice eq 1) { color = blue; } else { color = red; }

The CFML script language does not contain an elseif keyword.You must use the else and if keywords instead.

Loops
You should consider using CFML scripts to implement looping logic in your CFML application, particularly if your loop does not control any display logic. For example, scripts are an ideal means to initialize a large array with a set of values.

344

Appendix D

Converting Between Tags and Scripting

The CFML scripting language includes support for the following types of loops: for, for-in, while, and do-while. The for loop is useful for iterating over a fixed number of items (such as the contents of an array).The following for loop prints the contents of an array:
for (i = 1; i <= ArrayLen(myarray); i = i + 1) { value = myarray[i]; WriteOutput(myarray[&i&] = &value&<br/>); }

The for expression contains three separate statements within parentheses.The first statement initializes a variable to be used in the loop (such as i = 1;).The second statement is a true/false expression that determines whether the body of the loop should be executed (such as i <= ArrayLen(myarray)).The body of the loop will execute as long as the expression evaluates to true.The third statement contains an expression that is evaluated after each iteration of the loop. It typically contains logic to increment a counter. For example, the expression i = i + 1 increments the counter i by one each time through the loop. The for-in loop is similar to a for loop except that it is explicitly designed to iterate over the members of a ColdFusion structure.The syntax is as follows:
for(keyname in structure) statement

For example, the following loop iterates over all of the fields contained in a CFML structure called personstruct:
for (fieldname in personstruct) { fieldvalue = personstruct[fieldname]; WriteOutput(personstruct.&fieldname& = &fieldvalue&<br/>); }

While and Do-While loops are simple to use. Both loops simply execute until their true/false expression evaluates to false.The only difference is that While loops evaluate the true/false expression before each iteration, whereas Do-While loops check it afterwards. See if you can determine the value of cnt after the execution of both of

these loops.
cnt = 1; while (cnt < 10) { cnt = cnt + 2; } cnt = 1; do

Switch Statements

345

{ cnt = cnt + 2; } while (cnt < 10)

Sometimes you encounter conditions that require you to bail out of a loop entirely or else to skip directly to the next iteration of the loop.The break and continue statements let you do this. Break will quit the loop entirely, whereas continue will jump straight to the top of the loop.The following loop breaks if an array element contains the value -1.
for (i = 1; i <= ArrayLen(myarray); i = i + 1) { value = myarray[i]; if (value eq -1) break; WriteOutput(myarray[&i&] = &value&<br/>); }

This loop divides the number 10 by each value in an array unless the value is 0:
for (i = 1; i <= ArrayLen(myarray); i = i + 1) { divisor = myarray[i]; if (divisor eq 0) continue; WriteOutput(10 / divisor); }

Switch Statements
Switch statements in CFScript behave much like the <cfswitch> and <cfcase> tags. Switch statements essentially implement a series of if-then-else statements with an optional default case.The syntax of the switch statement is as follows:
switch(expression) { case constant: ... [case constant:] ... [default:] ... }

Just like with the <cfswitch> and <cfcase> tags, the CFML interpreter evaluates the switch expression and jumps to the case statement whose constant is equal to the value of the switch expression or to the default case if a matching case cannot be found.The CFML interpreter begins execution at this case statement and does not stop

346

Appendix D

Converting Between Tags and Scripting

until it reaches a break statement or the end of the switch statement. Make sure that you put break statements at the end of each case unless you intend for subsequent cases to execute as well. For example, the following switch statement sets the value of the variable color depending on the value of the choice variable:
<cfscript> choice = form.choice; switch(choice) { case 0: color=red; break; case 1: color=blue; break; case 2: color=yellow; break; case 3: color=green; break; default: color=purple; break } </cfscript>

Exception Handling
ColdFusion scripts should also check for and catch any exceptions that might occur (exception handling concepts are covered in Chapter 8, Exception Handling). Scripts use the try and catch statements to handle exceptions.The syntax is as follows:
try { // Exception-prone logic } catch(ExceptionType ExceptionVariable) { // Exception-handling logic } [catch(ExceptionType ExceptionVariable) {}]

You can supply unique catch blocks for different types of expressions by specifying the exception type in the catch block. For example, the following code catches all

347

exceptions and provides specialized catch blocks for expression and application exceptions:
try { // Risky logic omitted } catch (Expression exprException) { WriteOutput(An expression exception occurred); } catch (Application appException) { WriteOutput(An application exception occurred); } catch (Any exception) { WriteOutput(A generic exception occurred); }

You should use this technique to provide fine-grained exception handlers that can respond differently to different types of exceptions.Your exception handling code will usually do more than simply display an error message. For example, an attempt to open a log file might raise an exception if the file does not exist. Rather than print an error, you might want to handle this exception by creating and opening an empty log file.

Index

SYMBOLS
* (asterisk), 19 @ (at symbol), 208 \ (backslash), 21-22 [] (brackets), 20-21, 198 ^ (caret), 20, 199-200 & (concatenation) operator, 7-8 - (dash), 198-199 $ (dollar sign), 20, 122 % (percent symbol), 196-197 . (period), 20 | (pipe character), 21 + (plus sign), 19 ? (question mark), 19 _ (underscore), 197-198 ?wdsl suffix, 311

A
accept attribute (<cffile> tag), 130, 335 access attribute (<cffunction> tag), 219 accessing components, 267-268 custom tags, 230 EJBs (Enterprise JavaBeans), 322-326 acmeIntranet.cfm file, 172-174 action attribute <cfcollection> tag, 152 <cfdirectory> tag, 135 <cffile> tag, 126-130, 133 <cfftp> tag, 148

<cfindex> tag, 153 <cfldap> tag add value, 143-144 delete value, 147 modify value, 144-145 modifyDN value, 146 <cfobject> tag, 272 <cfpop> tag, 141 <cfwddx> tag, 282 actions CFML2JS, 283-284 CFML2WDDX, 281-283 WDDX2CFML, 285-286 WDDX2JS, 286-287 addColumn() method, 294 addEmployee stored procedure, 211-212 addNewLine attribute (<cffile> tag), 127-128 addQuery() method, 241, 245-249, 253 addRow() method, 246-247, 255 addRows() method, 294 administration of charts, 123 Administrator, 123 aggregate data, grouping, 193 aggregate functions, 46-47 conditions, 194 tables, 192-193 aliases, 187-188 :alnum: character class, 22 :alpha: character class, 22 ampersand (&), 7-8

350

analyzing code

analyzing code, 339 AND operator, 186-187 any data type, 219 Any exception, 105 Apache AXIS WSDL2Java tool, 315-316 Apache Jakarta Project Web site, 331 Apache XML Project Web site, 331 app column (CDATA table), 99 appending to files, 127-128 Application exceptions, 104 Application.cfm file, 91, 165 applications application scope, 92-94 authentication example acmeIntranet.cfm, 172-174 application.cfm, 165 Authenticator.cfm, 167-168 DatabaseAuthenticator.cfm, 168-169 LDAPAuthenticator.cfm, 169-172 loginForm.cfm, 166-167 loginLogic.cfm, 165-166 role table, 176 SimpleAuthenticator.cfm, 168 user table, 175 user.cfm, 167 user_roles table, 176 Client Management, 96-97 client state information storing in database, 99-100 storing in Registry, 97-98 storing with cookies, 98-99 creating, 91-93 Session Management, 95-96 applicationTimeout attribute (<cfapplication> tag), 92-93 argumentcollection attribute (<cfobject> tag), 264

arguments cfencode.exe, 236 functions, 220-221 StructSort() function, 64 array data type, 219, 284-285 ArrayAppend() function, 38-39, 47 ArrayAvg() function, 46 ArrayBoundException, 40 ArrayClear() function, 47 ArrayDelete() function, 47 ArrayIsEmpty() function, 49 ArrayLen() function, 40, 49 ArrayMax() function, 46 ArrayMin() function, 46 ArrayNew() function, 37-38, 44-47 ArrayPrepend() function, 38-39, 47 ArrayResize() function, 38, 48 arrays converting lists to, 25-26, 48 converting to lists, 48 creating, 37-38 defined, 37 displaying values in, 39-40 dynamically sized arrays, 38 elements adding, 38-39 counting, 40 defined, 37 index, 37 manipulating, 41 sorting, 41-44 functions aggregate functions, 46-47 ArrayAppend(), 38-39, 47 ArrayAvg(), 46 ArrayClear(), 47 ArrayDelete(), 47 ArrayIsEmpty(), 49 ArrayLen(), 40, 49 ArrayMax(), 46

attributes

351

ArrayMin(), 46 ArrayNew(), 37-38, 44-47 ArrayPrepend(), 38-39, 47 ArrayResize(), 38, 48 ArraySet(), 48 ArraySort(), 41-43, 48 ArraySum(), 46 ArraySwap(), 48 ArrayToList(), 48 information functions, 48-49 IsArray(), 49 ListToArray(), 48 utility functions, 47-48 looping through, 40 manipulating values in, 41 multidimensional arrays, 44-45 printing contents of, 344 returning information about, 48-49 sizing, 38 swapping values in, 48 when to use, 37 ArraySet() function, 48 ArraySort() function, 41-43, 48 ArraySum() function, 46 ArraySwap() function, 48 ArrayToList() function, 48 AS statement, 187-188, 205 Asc() function, 9 ASCII character codes, 9 assignment statements, 342 AssocAttribs key (ThisTag variable), 233 associative arrays. See structures asterisk (*), 19 at symbol (@), 208 attacks (security) cross-site scripting, 336-337 DOS (denial of service) attacks, 335-336 file uploading, 335

sandbox security, 337 security Web sites, 337-338 session hijacking, 333-334 URL injection hacking, 334-335 attemptedServerFile key (cffile structure), 131 attributeExists() method, 241-243, 249-251 attributes C++ CFX tags, 250-251 <cfapplication> tag applicationTimeout, 92-93 clientManagement, 92-93 clientStorage, 92, 97-99 name, 92 sessionManagement, 92-93 sessionTimeout, 92 setClientCookies, 92 setDomainCookies, 92 <cfargument> tag, 220 <cfchart> tag, 115-116, 122-123 <cfchartseries> tag, 118 <cfcollection> tag, 152 <cfcomponent> tag displayname, 261 extends, 265-267 hint, 261 <cfdirectory> tag, 135 <cffile> tag accept, 130, 335 action, 126-130, 133 addNewLine, 127-128 attributes, 127-130, 133 charset, 126-128, 133 destination, 130, 133 file, 126-128 filefield, 130 mode, 127-130, 133 nameConflict, 130 output, 127-128

How can we make this index more useful? Email us at indexes@samspublishing.com

352

attributes

ServerFileExt, 335 source, 133 variable, 126 <cfftp> tag, 148 <cffunction> tag, 219, 222-223 <cfhttp> tag, 138-139 <cfindex> tag, 153 <cfloop> tag, 31-33 <cfmail> tag, 140 <cfobject> tag action, 272 argumentcollection, 264 class, 272 component, 262-264, 273 context, 273 locale, 273 name, 272 server, 273 type, 272 webservice, 273 <cfpop> tag, 141 <cfprocparam> tag, 210-211 <cfprocresult> tag, 207 <cfquery> tag blockFactor, 68 cachedAfter, 68, 76-77 cachedWithin, 68, 76-77 Datasource, 68, 73-74 dbType, 68, 73-74 Debug, 68 maxRows, 68 Name, 68 Password, 68 Timeout, 68 Username, 68 <cfsearch> tag, 156 <cfstoredproc> tag, 207 <cfthrow> tag, 107 <custom> tags, 230-231

Java CFX tags, 241-243 LDAP directory entries, 145-146 stored procedures defining in Microsoft SQL Server, 207-208 defining in Oracle, 208-209 output parameters, 211-212 passing, 209-211 XML documents, 297 XML elements, 298-300 attributes attribute (<cffile> tag), 127-130, 133 Attributes structure, 230-231 authentication and authorization, 159 authentication example acmeIntranet.cfm, 172-174 application.cfm, 165 Authenticator.cfm, 167-168 DatabaseAuthenticator.cfm, 168-169 LDAPAuthenticator.cfm, 169-172 loginForm.cfm, 166-167 loginLogic.cfm, 165-166 role table, 176 SimpleAuthenticator.cfm, 168 user table, 175 user.cfm, 167 user_roles table, 176 authentication realm, 159 blocks of code, 162-163 functions, 164 j_password field, 161-162 j_username field, 161-162 logins, 160 logouts, 160-161 user IDs, 162 user roles, 163 Authenticator.cfm file, 167-168 AVG() function, 192-193

CFCs (components)

353

B
backgroundColor attribute (<cfchart> tag), 115 backslash (\), 21-22 bcc attribute (<cfmail> tag), 140 beans, accessing, 322-326 binary data type, 219, 286 biu.cfm custom tag, 232 biuClient.cfm template, 232 :blank: character class, 22 blockfactor attribute (<cfstoredproc> tag), 207 blockFactor parameter (<cfquery> tag), 68 blocks of code, authorizing, 162-163 body attribute (<cfindex> tag), 153 boolean data type, 218, 284-286 brackets ([]), 20-21, 198 break statements, 345 breaking loops, 345

C
C++ CFX tags, 239 attributes, 250-251 <CFX_GetMemoryStatus>, 251-253 <CFX_ReverseQuery>, 253-255 <CFX_SubString>, 250-251 <CFX_WriteMemoryStatus>, 247-248 ColdFusion variables, 251-253 creating, 247-249 installing, 257-258 queries, 253-255 registering, 257-258 cachedAfter parameter (<cfquery> tag), 68, 76-77 cachedWithin parameter (<cfquery> tag), 68, 76-77 caching queries, 76-77

CallEJB.cfm listing, 324 calling methods, 263-264, 273-274 .NET, 312-314 stored procedures, 206-207 Web services <cfinvoke> tag, 306-307 Java, 315-316 cardinal numbering, 25 caret (^), 20, 199-200 catch statements, 346-347 catching exceptions multiple exceptions, 104-105 single exceptions, 103 cc attribute (<cfmail> tag), 140 CCFXQuery class, 255 CCFXRequest class, 249-250 CDATA table, 99 CERT Coordination Center, 338 CF Bookmark Web site, 329 CF Bug Hunt Web site, 329 CF FAQ Web site, 329 <cfapplication> tag, 91-93, 97-99 <cfargument> tag, 220-221 <cfcatch> tag, 102-105 cfcatch variable, 106 <cfchart> tag, 113-116, 122-123 <cfchartdata> tag, 113-114, 121 <cfchartseries> tag, 113-114, 117-118 <cfcollection> tag, 151-152 <cfcomponent> tag, 259-261 displayname attribute, 261 extends attribute, 265-267 hint attribute, 261 CFCs (components) accessing, 267-268 counter.cfc, 263 creating, 259-261

How can we make this index more useful? Email us at indexes@samspublishing.com

354

CFCs (components)

employee.cfc, 265 encapsulation, 261 inheritance, 265-267 instantiating, 262-263 invoking component methods, 263-264 light.cfc, 259-262 manager.cfc, 266 meta data, 268-269 CFCustomTags.com Web site, 329 CFCZone Web site, 329 CFDEV Web site, 329 CFDEVX Web site, 329 <cfdirectory> tag, 134-136 <cfdump> tag, 307 cfencode.exe command, 235-236 cffile structure, 131-132 <cffile> tag, 15 accept attribute, 335 append action, 127-128 attributes accept, 130 action, 126-130, 133 addNewLine, 127-128 attributes, 127-130, 133 charset, 126-128, 133 destination, 130, 133 file, 126-128 filefield, 130 mode, 127-130, 133 nameConflict, 130 output, 127-128 source, 133 variable, 126 copy action, 134 delete action, 134 move action, 133 read action, 125-126 rename action, 133-134 ServerFileExt attribute, 335

upload action, 129-132 write action, 126-127 writing files, 126 <cfftp> tag, 147-149 <cffunction> tag, 164, 218-219, 222-223, 259-261, 309 <cfheader> tag, 288 <cfhttp> tag, 138-139 cfhttp variable, 139 CFHub.com Web site, 330 cfid column CDATA table, 99 CGLOBAL table, 100 CFID variable, 333-334 <cfimport> tag, 237-238 <cfinclude> tag, 174 <cfindex> tag, 152-153 <cfinvoke> tag, 263-264, 306-307 <cfinvokeargument> tag, 264 <cfldap> tag, 141-147 <cflogin> tag, 160, 177 <cfloginuser> tag, 160 <cflogout> tag, 160-161 <cfloop> tag, 14-15, 31-35, 70-71 <cfmail> tag, 140 CFMCentral Web site, 330 CFML (ColdFusion Markup Language) scripts <cfscript> tag, 341-343 conversions CFML to JavaScript, 283-284 CFML to WDDX, 281-283 WDDX to CFML, 285-286 WDDX to JavaScript, 286-287 exception handling, 346-347 if-then-else statements, 343 loops, 343-345 switch statements, 345-346 CFML2JS action, 283-284

CFX tags

355

CFML2WDDX action, 281-283 <cfmodule> tag, 236-237 <cfobject> tag, 262-263, 271, 276-277, 307-308 action attribute, 272 argumentcollection attribute, 264 class attribute, 272 component attribute, 262-264, 273 context attribute, 273 locale attribute, 273 name attribute, 272 server attribute, 273 type attribute, 272 webservice attribute, 273 <cfoutput> tag, 39-40, 70-71, 190-191 <cfpop> tag, 140-141 <cfprocparam> tag, 209-211 <cfprocresult> tag, 206-207, 213 <cfproperty> tag, 309-310 <cfquery> tag, 67-69 blockFactor parameter, 68 cachedAfter parameter, 68, 76-77 cachedWithin parameter, 68, 76-77 Datasource parameter, 68, 73-74 dbType parameter, 68, 73-74 Debug parameter, 68 example, 67 maxRows parameter, 68 Name parameter, 68 parameters, 68 Password parameter, 68 Timeout parameter, 68 Username parameter, 68 <cfqueryparam> tag, 200-201, 334 <cfrethrow> tag, 108-109 <cfreturn> tag, 222 CFScript statements, 341 <cfscript> tag, 341-343

<cfsearch> tag, 155-157 <cfset> tag, 94 <cfsetting> tag, 288 cfsqltype attribute (<cfprocparam> tag), 210 <cfstoredproc> tag, 206-207 <cfthrow> tag, 107-108 CFTOKEN variable, 333-334 <cftry> tag, 102-104 <cfwddx> tag CFML2JS action, 283-284 CFML2WDDX action, 281-283 WDDX2CFML action, 285-286 WDDX2JS action, 286-287 CFX tags, 239 C++ CFX tags attributes, 250-251 <CFX_GetMemoryStatus>, 251-253 <CFX_ReverseQuery>, 253-255 <CFX_SubString>, 250-251 <CFX_WriteMemoryStatus>, 247-248 ColdFusion variables, 251-253 creating, 247-249 installing, 257-258 queries, 253-255 registering, 257-258 Java CFX tags attributes, 241-243 <CFX_GetMemoryStatus>, 244-245 <CFX_ReverseQuery>, 245-246 <CFX_SubString>, 242 <CFX_WriteMemoryStatus>, 240-241 ColdFusion variables, 244-245 creating, 240-241 CustomTag interface, 240 queries, 245-247 registering, 256

How can we make this index more useful? Email us at indexes@samspublishing.com

356

<cfxml> tag

<cfxml> tag, 295-296 <CFX_GetMemoryStatus> tag C++ code listing, 251-253 Java code listing, 244-245 <CFX_ReverseQuery> tag C++ code listing, 253-255 Java code listing, 245-246 <CFX_SubString> tag C++ code listing, 250-251 Java code listing, 242 <CFX_WriteMemoryStatus> tag C++ code listing, 247-248 Java code listing, 240-241 CGLOBAL table, 100 changing list delimiters, 24-25 list values, 28-29 character classes, 22 characters. See also strings ASCII character codes, 9 brackets ([]), 20-21 character classes, 22 escape characters, 21-22 matching excluded characters, 199-200 groups of characters, 198 range of characters, 198-199 single characters, 197-198 pipe character (|), 21 quantifiers, 19 wildcards, 20 charset attribute <cffile> tag, 126-128, 133 <cfhttp> tag, 139 chartHeight attribute (<cfchart> tag), 115 charts, 113 administering with ColdFusion Administrator, 123 chart containers, 114-116

chart series managing, 117-118 populating with explicit values, 121 click-through behaviors, 123 creating, 113-114 populating with query values, 121-122 supported CFMX chart types, 118-120 chartWidth attribute (<cfchart> tag), 115 child elements (XML), 300 choosing list delimiters, 24 Chr() function, 9 class attribute (<cfobject> tag), 272 classes CCFXQuery, 255 CCFXRequest, 249-250 character classes, 22 DogServiceLocator, 316 RecordSet, 294 WddxDeserializer, 292-294 WddxSerializer, 290-291 clauses, WHERE, 186-187 click-through behaviors, 123 Client Management, 96-97 client prefix, 96-97 clientDirectory key (cffile structure), 131 clientFile key (cffile structure), 131 clientFileExt key (cffile structure), 131-132 clientFileName key (cffile structure), 131-132 clientManagement attribute (<cfapplication> tag), 92-93 clients Client Management, 96-97 client state information storing in database, 99-100 storing in Registry, 97-98 storing with cookies, 98-99

cookies

357

clientStorage attribute (<cfapplication>> tag), 92, 97-99 :cntrl: character class, 22 code blocks, authorizing, 162-163 ColdFusion 5, migrating to ColdFusion MX, 339-340 ColdFusion Administrator, 123 ColdFusion Developers Journal, 330 ColdFusion FAQ Web site, 330 collection attribute <cfcollection> tag, 152 <cfindex> tag, 153 <cfsearch> tag, 156 collections (Verity) adding data to, 152-155 creating, 151-152 searching, 155-157 colorList attribute (<cfchartseries> tag), 118 columnList variable, 72 Columns attribute (<cfhttp> tag), 138 COM (Component Object Model) objects, 276-277 com2java.exe program, 277 combining strings, 7-8 comma-delimited lists, 8 Common Object Request Broker Architecture (CORBA) objects, 277-280 comparing dates, 82-83 compatibility, analyzing code for, 339 Compatibility Analyzer, 339 component attribute (<cfobject> tag), 262-264, 273 Component Object Model (COM) objects, 276-277 componentName data type, 219

components (CFCs). See also objects accessing, 267-268 counter.cfc, 263 creating, 259-261 employee.cfc, 265 encapsulation, 261 inheritance, 265-267 instantiating, 262-263 invoking component methods, 263-264 light.cfc, 259-262 manager.cfc, 266 meta data, 268-269 concatenating strings, 7-8 concatenation (&) operator, 7-8 condition attribute (<cfloop> tag), 33 conditional loops, 33 connection attribute (<cfftp> tag), 148 containers, 114-116 contentSubType key (cffile structure), 131 contentType key (cffile structure), 131 context attribute (<cfobject> tag), 273 continue statements, 345 conversions arrays to lists, 48 CFML to JavaScript, 283-284 CFML to WDDX, 281-283 Java to WDDX, 290-292 lists to arrays, 25-26, 48 queries to lists, 75-76 WDDX to CFML, 285-286 WDDX to Java, 292-294 WDDX to JavaScript, 286-287 cookies, 98-99

How can we make this index more useful? Email us at indexes@samspublishing.com

358

copying

copying files, 132-134 structures, 61-63 CORBA (Common Object Request Broker Architecture) objects, 277-280 COUNT() function, 193 counter.cfc component, 263 counting array elements, 40 CREATE OR REPLACE statement, 205 CREATE PROCEDURE statement, 204-205 CreateDate() function, 80 CreateDateTime() function, 80 CreateObject() function, 262-263, 271-273, 277 createStringSet() method, 249 CreateTime() function, 80 CreateTimeSpan() function, 85-86, 93 criteria attribute (<cfsearch> tag), 156 cross-site scripting, 336-337 CSS (cross-site scripting), 336-337 currentRow variable, 72 custom tags accessing, 230 attributes, 230-231 biu.cfm, 232 calling regardless of location, 236-237 C++ CFX tags attributes, 250-251 <CFX_GetMemoryStatus>, 251-253 <CFX_ReverseQuery>, 253-255 <CFX_SubString>, 250-251 <CFX_WriteMemoryStatus>, 247-248 ColdFusion variables, 251-253 creating, 247-249

installing, 257-258 queries, 253-255 registering, 257-258 <cfimport> tag, 237-238 <cfmodule> tag, 236-237 creating, 229-230 encoding, 235-236 getDomainTag.cfm, 231 greetingTag.cfm, 230 helloWorldTag.cfm, 229-230 importing, 237-238 Java CFX tags attributes, 241-243 <CFX_GetMemoryStatus>, 244-245 <CFX_ReverseQuery>, 245-246 <CFX_SubString>, 242 <CFX_WriteMemoryStatus>, 240-241 ColdFusion variables, 244-245 creating, 240-241 CustomTag interface, 240 installing, 256 queries, 245-247 registering, 256 mailto.cfm, 234 nested tags, 234-235 return results, 231-232 start/end tags, 232-233 custom1 attribute (query objects), 156 custom2 attribute (query objects), 156 CustomTag interface, 240 Custom_Type exceptions, 105

D
dash (-), 198-199 data column CDATA table, 99 CGLOBAL table, 100

databases

359

data types. See also arrays any, 219 array, 219, 284-285 binary, 219, 286 boolean, 218, 284-286 componentName, 219 date, 219, 284-285 comparing, 82-83 date formatting, 80-82 date information functions, 86-87 Date objects, 79-80 DateTime objects, 79-80 determining time differences, 83-84 functions, 80-88 locale-specific dates/times, 88 time formatting, 84-85 Time objects creating, 79-80 time spans, 85-86 guid, 219 IDL-to-ColdFusion type mappings, 279-280 Number, 284-286 numeric, 219 query, 219, 284-285 string, 218, 284-286 structures, 51, 219, 284-286 copying, 61-63 creating, 52 displaying values in, 57 dot notation, 58-59 information functions, 52-53 inserting values into, 55-56 key names, 57 looping over, 59-60 management functions, 53-55 naming, 52 sorting, 63-66 updating values in, 56-57 utility functions, 60-61

uuid, 219 variableName, 219 void, 219 Web services, 309-311 dataBackgroundColor attribute (<cfchart> tag), 115 Database exceptions, 104 DatabaseAuthenticator.cfm file, 168-169 databases aggregate functions, 192-194 migrating to ColdFusion MX, 340 queries. See also SQL statements C++ CFX tags, 253-255 caching, 76-77 converting to lists, 75-76 creating manually, 69-70 creating with <cfquery> tag, 67-69 displaying query results, 70-71 functions, 69-70, 75-76 in-memory queries, 73-74 Java CFX tags, 245-247 populating charts with, 121-122 prepared statements, 200-202 query variables, 71-73 storing client state information in, 99-100 tables adding rows to, 183 aggregate functions, 192-194 aliases, 187-188 authentication application, 175-176 CDATA, 99 CGLOBAL, 100 deleting rows from, 184 department, 182 employees, 181-182 employee_projects, 182 grouping data in, 190-193 joining, 189-191

How can we make this index more useful? Email us at indexes@samspublishing.com

360

databases

pattern matching, 196-200 projects, 182 selecting data in, 185-189, 192-196 updating, 183-184 datasource attribute <cfquery> tag, 68, 73-74 <cfstoredproc> tag, 207 date data type, 219, 284-285 Date objects, 79-80 DateCompare() function, 82-83 DateDiff() function, 83-84 DateFormat() function, 80-82 dates/times comparing, 82-83 date formatting, 80-82 date information functions, 86-87 Date objects, 79-80 DateTime objects, 79-80 determining time differences, 83-84 functions CreateDate(), 80 CreateDateTime(), 80 CreateTime(), 80 CreateTimeSpan(), 85-86 DateCompare(), 82-83 DateDiff(), 83-84 DateFormat(), 80-82 Day(), 87 DayOfWeek(), 87 DayOfWeekAsString(), 87 DayOfYear(), 87 DaysInMonth(), 87 DaysInYear(), 87 FirstDayOfMonth(), 87 Hour(), 87 IsLeapYear(), 87 LSDateFormat(), 88 LSTimeFormat(), 88 Minute(), 87

Month(), 87 MonthAsString(), 87 Quarter(), 87 Second(), 87 TimeFormat(), 84-85 Week(), 87 Year(), 87 locale-specific dates/times, 88 time formatting, 84-85 Time objects, 79-80 time spans, 85-86 DateTime objects, 79-80 Day() function, 87 DayOfWeek() function, 87 DayOfWeekAsString() function, 87 DayOfYear() function, 87 DaysInMonth() function, 87 DaysInYear() function, 87 dbType parameter (<cfquery> tag), 68, 73-74 DCOM (Distributed Component Object Model) objects, 276-277 debug attribute <cfstoredproc> tag, 207 <cfquery> tag, 68 debug() method, 243, 249 declaring functions, 218-219 Decrypt() function, 12-13 decrypting strings, 12-13 default attribute (<cfargument> tag), 220 Defusion Web site, 330 DELETE statement, 184 deleting files, 132-134 LDAP directories, 147 table rows, 184 Delimiter attribute (<cfhttp> tag), 138

elements

361

delimiters list delimiters, 24-25 string delimiting, 8 denial of service (DOS) attacks, 335-336 department table, 182 deploying Web services, 311 deserializing WDDX (Web Distributed Data Exchange), 292-294 destination attribute (<cffile> tag), 130, 133 detail attribute (<cfthrow> tag), 107 DEVMX Web site, 330 :digit: character class, 22 directories adding entries, 143-144 creating, 136 deleting entries, 147 entry attributes, 145-146 listing contents of, 134-136 naming, 136 querying, 141-143 removing, 136 renaming entries, 146 updating entries, 144-145 directory attribute (<cfdirectory> tag), 135 displaying array values, 39-40 query results, 70-71 values in structures, 57 displayname attribute (<cfcomponent> tag), 261 DISTINCT statement, 192 Distributed Component Object Model (DCOM) objects, 276-277 DLLs (Dynamic Link Libraries), 276 do-while loops, 344

documentation, 340 documents, XML, 295 child elements, 300 creating from other objects, 298 creating with <cfxml> tag, 295-296 creating with XmlElemNew() function, 296-298 creating with XmlNew() function, 296-298 element attributes, 298-300 inner text, 300-301 parsing, 299 properties, 297 searching, 301-302 XSL transformations, 302-304 DogService interface, 316 DogService Web service, 312-314 DogServiceLocator class, 316 dollar sign ($), 20, 122 DOS (denial of service) attacks, 335-336 dot notation, 58-59 downloading files, 148-149 Duplicate() function, 61-63, 298 Dynamic Link Libraries (DLLs), 276 dynamically sized arrays, 38

E
EasyCFM.com Web site, 330 EJBs (Enterprise JavaBeans), 322-326 elements. See also tags array elements adding, 38-39 counting, 40 defined, 37 index, 37 looping through, 40 manipulating, 41

How can we make this index more useful? Email us at indexes@samspublishing.com

362

elements

returning information about, 48-49 sorting, 41-44 list elements changing values in, 28-29 empty values, 26-28 extracting, 25-26 looping through, 31-33 qualifiers, 30-31 searching, 29-30 sorting, 30 XML elements attributes, 298-300 child elements, 300 inner text, 300-301 else statement, 343 email, 140-141 employee.cfc component, 265 employee_projects table, 182 employees table, 181-182 empty list values, 26-28 encapsulation, 261 encoding custom tags, 235-236 Encrypt() function, 12-13 encrypting strings, 12-13 end tags, 232-233 Enterprise JavaBeans (EJBs), 322-326 entries (LDAP directories) adding, 143-144 attributes, 145-146 deleting, 147 renaming, 146 updating, 144-145 errorCode attribute (<cfthrow> tag), 107 errors. See exceptions escape characters, 21-22 Evolt Web site, 332

exceptions, 101-103 Any exceptions, 105 Application exceptions, 104 ArrayBoundException, 40 catching multiple exceptions, 104-105 single exceptions, 103 Custom_Type exceptions, 105 Database exceptions, 104 exception handlers, 346-347 Expression exceptions, 104 finding information about, 105-107 Lock exceptions, 104 MissingInclude, 104 NonEmptyDirectoryCannotBeDelete dException, 136 NoSuchTemplateException, 267 Object exceptions, 104 re-throwing, 108-109 SearchEngine exceptions, 105 Security exceptions, 104 Template exceptions, 104 throwing, 107-108 excluding groups of characters, 199 ranges of characters, 199-200 ExecutionMode key (ThisTag variable), 233 executionTime variable, 72 explicit Verity searches, 157 Expression exceptions, 104 extendedInfo attribute (<cfthrow> tag), 107 extends attribute (<cfcomponent> tag), 265-267 extends key, 268 Extensible Markup Language. See XML extensions attribute (<cfindex> tag), 153 extracting list elements, 25-26

FROM statement

363

F
factorial.cfm listing, 319 FAQs CF FAQ Web site, 329 ColdFusion FAQ Web site, 330 fields, 161-162 file attribute <cffile> tag, 126-128 <cfhttp> tag, 138 fileExisted key (cffile structure), 131 FileExists() function, 128 filefield attribute (<cffile> tag), 130 files, 125 appending to, 127-128 Application.cfm, 91 authentication application acmeIntranet.cfm, 172-174 application.cfm, 165 Authenticator.cfm, 167-168 DatabaseAuthenticator.cfm, 168-169 LDAPAuthenticator.cfm, 169-172 loginForm.cfm, 166-167 loginLogic.cfm, 165-166 SimpleAuthenticator.cfm, 168 user.cfm, 167 copying, 132-134 deleting, 132-134 downloading with FTP, 148-149 moving, 132-134 reading, 15, 125-126 renaming, 132-134 uploading, 129-132, 335 uploading with FTP, 147-148 verifying existence of, 128 WDSL files, 311 writing, 126-127 fileSize key (cffile structure), 131 fileWasAppended key (cffile structure), 131

fileWasOverwritten key (cffile structure), 131 fileWasRenamed key (cffile structure), 131 fileWasSaved key (cffile structure), 131 filter attribute (<cfdirectory> tag), 135 Find() function, 11, 16-17 findColumn() method, 294 finding substrings, 18-19 FindNoCase() function, 11, 16-17 FindOneOf() function, 11 FirstDayOfMonth() function, 87 FirstRowAsHeaders attribute (<cfhttp> tag), 138 Flight object, 309 font attribute (<cfchart> tag), 115 fontBold attribute (<cfchart> tag), 115 fontItalic attribute (<cfchart> tag), 115 fontSize attribute (<cfchart> tag), 115 for loops, 344 for-in loops, 344 foregroundColor attribute (<cfchart> tag), 115 format attribute (<cfchart> tag), 115 formatting dates, 79-82 times, 84-85 forms, parsing, 34-35 forward() function, 318 forwarding requests to servlets/JSPs, 318 from attribute (<cfmail> tag), 140 FROM statement, 188-189

How can we make this index more useful? Email us at indexes@samspublishing.com

364

FTP (File Transfer Protocol) servers

FTP (File Transfer Protocol) servers downloading files, 148-149 uploading files, 147-148 Full As a Goog Web site, 330 FULL OUTER JOIN statement, 191 function-call statements, 343 functions and methods addColumn(), 294 addQuery(), 241, 245-249, 253 addRow(), 246-247, 255, 294 attributeExists(), 241-243, 249-251 ArrayAppend(), 38-39, 47 ArrayAvg(), 46 ArrayClear(), 47 ArrayDelete(), 47 ArrayIsEmpty(), 49 ArrayLen(), 40, 49 ArrayMax(), 46 ArrayMin(), 46 ArrayNew(), 37-38, 44-47 ArrayPrepend(), 38-39, 47 ArrayResize(), 38, 48 ArraySet(), 48 ArraySort(), 41-43, 48 ArraySum(), 46 ArraySwap(), 48 ArrayToList(), 48 Asc(), 9 authorizing, 164 AVG(), 192-193 calling, 263-264 Chr(), 9 COUNT(), 193 CreateDate(), 80 CreateDateTime(), 80 CreateObject(), 262-263, 271-273, 277 createStringSet(), 249 CreateTime(), 80 CreateTimeSpan(), 85-86, 93

DateCompare(), 82-83 DateDiff(), 83-84 DateFormat(), 80-82 Day(), 87 DayOfWeek(), 87 DayOfWeekAsString(), 87 DayOfYear(), 87 DaysInMonth(), 87 DaysInYear(), 87 debug(), 243, 249 Decrypt(), 12-13 Duplicate(), 61-63, 298 Encrypt(), 12-13 FileExists(), 128 Find(), 11, 16-17 findColumn(), 294 FindNoCase(), 11, 16-17 FindOneOf(), 11 FirstDayOfMonth(), 87 forward(), 318 getAttribute(), 241-243, 249-251 getAttributeList(), 243, 249 GetAuthUser(), 162 getColumnCount(), 294 getColumnIndex(), 246, 255 getColumnNames(), 294 getColumns(), 246-247, 255 getData(), 246-247, 255 getDogServiceCfc(), 316 getIntAttribute(), 241-243 getField(), 294 GetMetaData(), 268-269 getName(), 246, 255 getQuery(), 243-249, 253-255 GetPageContext(), 318 getRowCount(), 246-247, 255, 294 Hash(), 13-14 Hour(), 87 HTMLEditFormat, 283, 337 include(), 319

functions and methods

365

invoking, 273-274 IsArray(), 49 IsIE(), 222 IsLeapYear(), 87 IsStruct(), 52-53 IsUserInRole(), 162-163, 176 Left(), 10-11 Len(), 11 ListAppend(), 24 ListChangeDelims(), 24-25 ListDeleteAt(), 28-29 ListFind(), 29-30 ListFindNoCase(), 29 ListFirst(), 26 ListGetAt(), 25 ListInsertAt(), 28-29 ListPrepend(), 24 ListQualify(), 30-31 ListRest(), 26 ListSetAt(), 28-29 ListSort(), 30 ListToArray(), 25-26, 48 LSDateFormat(), 88 LSTimeFormat(), 88 LTrim(), 11 MAX(), 193 Mid(), 11, 14 migrating to ColdFusion MX, 340 MIN(), 193 Minute(), 87 Month(), 87 MonthAsString(), 87 noOutputAttribute(), 223 outputTrue(), 223 overriding, 267 Power(), 223-225 processRequest(), 241 ProcessTagRequest(), 247-249 Quarter(), 87 QueryAddColumn(), 70

QueryAddRow(), 69-70 QueryNew(), 69-70 QuerySetCall(), 69-70 QuotedValueList(), 75-76 REFind(), 18-19 remote, 315 RemoveChars(), 11 Replace(), 11, 17-18 ReplaceNoCase(), 11, 17-18 reThrowException(), 249 Right(), 10-11 RTrim(), 11 Second(), 87 setData(), 246-247, 255 setField(), 294 setVariable(), 244-245, 249-253 Split(), 221 StripCR(), 12 StructAppend(), 53-54 StructClear(), 54 StructCopy(), 54, 61-63 StructCount(), 53 StructFind(), 54, 57 StructFindKey(), 61 StructFindValue(), 61 StructGet(), 61 StructInsert(), 54-56 StructIsEmpty(), 52-53 StructKeyArray(), 61 StructKeyExists(), 52-53 StructKeyList(), 61 StructKeyLists(), 52, 59-60 StructNew(), 52-54 StructSort(), 54, 63-66 StructUpdate(), 54-56 SUM(), 193 SuperSplit(), 221 throwException(), 249 TimeFormat(), 84-85 ToBase64(), 126

How can we make this index more useful? Email us at indexes@samspublishing.com

366

functions and methods

Trim(), 12, 26 user-defined functions, 217 arguments, 220-221 data types, 218-219 declaring, 218-219 recursive functions, 223-225 references, 225-227 return values, 222 suppressing output, 222-223 ValueList(), 75 Week(), 87 write(), 240-241, 249 writeDebug(), 241, 249 WriteOutput(), 341 XmlElemNew(), 296-298 XmlNew(), 296-298 XmlParse(), 299 XmlSearch(), 301-302 XmlTransform(), 302-303 Year(), 87 functions key, 268 Fusion Authority Web site, 330

G
/g argument (<cfencode.exe), 236 GeneratedContent key (ThisTag variable), 233 generating WDSL files, 311 getAttribute() method, 241-243, 249-251 getAttributeList() method, 243, 249 GetAuthUser() function, 162 getColumnCount() method, 294 getColumnIndex() method, 246, 255 getColumnNames() method, 294 getColumns() method, 246-247, 255 getData() method, 246-247, 255 getDepartmentsAndProjects stored procedure, 213

getDogServiceCfc() function, 316 getDomainClient.cfm template, 232 getDomainTag.cfm custom tag, 231 getField() method, 294 getIntAttribute() method, 241-243 GetMetaData() function, 268-269 getName() method, 246, 255 GetPageContext() function, 318 getQuery() method, 243-249, 253-255 getRowCount() method, 246-247, 255, 294 giveRaise stored procedure, 204-205 :graph: character class, 22 greetingClient.cfm template, 231 greetingTag.cfm custom tag, 230 gridlines attribute (<cfchart> tag), 115 GROUP BY statement, 193 grouping data, 163 <cfoutput> tag, 190-191 GROUP BY clause, 193 groups of characters, matching, 198-199 guid data type, 219

H
/h argument (cfencode.exe), 236 handling exceptions. See exceptions HasEndTag key (ThisTag variable), 233 Hash() function, 13-14 hashing strings, 13-14 HAVING statement, 194 helloWorldTag.cfm, 229-230 hint attribute <cfcomponent> tag, 261 <cffunction> tag, 219 Hour() function, 87 House of Fusion Web site, 330

iterating

367

HTMLEditFormat() function, 283, 337 HTTP requests, 137-139 hyphen (-), 198-199

I
IDL (Interface Definition Language), 278-280 IDs, user IDs, 162 if-then-else statements, 343 importing custom tags, 237-238 IN statement, 194-196, 209 include() function, 319 include.jsp listing, 319 including servlets/JSPs, 318-319 infile argument (cfencode.exe), 236 information functions (arrays), 48-49 inheritance, 265-267 in-memory queries, 73-74 inner text (XML), 300-301 input attribute (<cfwddx> tag), 282 input forms, parsing, 34-35 INSERT INTO statement, 183 inserting array elements, 38-39 table rows, 183 values into structures, 55-56 installing CFX tags, 257-258 instantiating components, 262-263 objects, 271-273 Web services, 307-308 Interface Definition Language (IDL), 278-280 interfaces CustomTag, 240 DogService, 316 Query, 246

Request, 243 Response, 241 Internet protocols FTP downloading files, 148-149 uploading files, 147-148 HTTP requests, 137-139 LDAP directories adding entries, 143-144 deleting entries, 147 entry attributes, 145-146 querying, 141-143 renaming entries, 146 updating entries, 144-145 POP servers, 140-141 SMTP servers, 140 invoking methods, 263-264, 273-274 .NET, 312-314 stored procedures, 206-207 Web services cfinvoke tag, 306-307 Java, 315-316 IsArray() function, 49 IsIE() function, 222 IsLeapYear() function, 87 IsStruct() function, 52-53 IsUserInRole() function, 162-163, 176 itemColumn attribute (<cfchartseries> tag), 118 iterating through lists, 31-33 through strings, 14-15

How can we make this index more useful? Email us at indexes@samspublishing.com

368

J2EE interoperability

J
J2EE interoperability, 317 EJB access, 322-326 JSPs accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319 servlets accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319 Jakarta Project Web site, 331 Java CFX tags, 239 attributes, 241-243 <CFX_GetMemoryStatus>, 244-245 <CFX_ReverseQuery>, 245-246 <CFX_SubString>, 242 <CFX_WriteMemoryStatus>, 240-241 ColdFusion variables, 244-245 creating, 240-241 CustomTag interface, 240 installing, 256 queries, 245-247 registering, 256 invoking Web services with, 315-316 Java-to-COM bridge, 277 JavaBeans, 322-326 JSPs (JavaServer Pages) accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319

objects, 275-276 converting to WDDX, 290-292 creating, 271-273, 292-294 methods, 273-274 Web sites, 331 Java-to-COM bridge, 277 JavaBeans, 322-326 JavaScript, converting CFML to, 283-284 JavaServer Pages (JSPs) accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319 Jintegra, 277 joining tables, 189-191 journals, ColdFusion Developers Journal, 330 JSP Resource Index Web site, 331 JSP Tags Web site, 331 JSPs (JavaServer Pages) accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319 j_password field, 161-162 j_username field, 161-162

K
key attribute <cfindex> tag, 153 query objects, 157 key lists, looping through, 32-33 keys, 57, 268-269 keywords AND, 186-187 AS, 187-188, 205 CREATE OR REPLACE, 205 CREATE PROCEDURE, 204-205

ListSetAt() function

369

DELETE, 184 DISTINCT, 192 FROM, 188-189 FULL OUTER JOIN, 191 GROUP BY, 193 HAVING, 194 IN, 194-196, 209 INSERT INTO, 183 LEFT OUTER JOIN, 191 LIKE, 196-199 OR, 186-187 ORDER BY, 187 OUTPUT, 211-212 RIGHT OUTER JOIN, 191 SELECT, 185 SET, 183-184 UPDATE, 183-184 VALUES, 183 WHERE, 185-186, 189-190

L
labelFormat attribute (<cfchart> tag), 115 language attribute <cfcollection> tag, 152 <cfindex> tag, 153 <cfsearch> tag, 156 LDAP (Lightweight Directory Access Protocol) directories adding entries, 143-144 deleting entries, 147 entry attributes, 145-146 querying, 141-143 renaming entries, 146 updating entries, 144-145 LDAPAuthenticator.cfm file, 169-172 LEFT OUTER JOIN statement, 191 Left() function, 10-11 Len() function, 11

libraries (tag), importing, 237-238 light.cfc component, 259, 261-262 Lightweight Directory Access Protocol. See LDAP directories LIKE operator [] (brackets) wildcard, 198 ^ (caret) wildcard, 199-200 - (dash) wildcard, 198-199 % (percent symbol) wildcard, 196-197 _ (underscore) wildcard, 197-198 list attribute (<cfloop> tag), 31-33 ListAppend() function, 24 ListChangeDelims() function, 24-25 ListDeleteAt() function, 28-29 ListFind() function, 29-30 ListFindNoCase() function, 29 ListFirst() function, 26 ListGetAt() function, 25 listing directory contents, 134-136 ListInsertAt() function, 28-29 ListPrepend() function, 24 ListQualify() function, 30-31 ListRest() function, 26 lists changing values in, 28-29 comma-delimited lists, 8 converting arrays to, 48 converting queries to, 75-76 converting to arrays, 25-26, 48 creating, 23-24 delimiters, 24-25 empty values, 26-28 extracting elements from, 25-26 looping through, 31-33 parsing input forms, 34-35 qualifiers, 30-31 searching, 29-30 sorting, 30 ListSetAt() function, 28-29

How can we make this index more useful? Email us at indexes@samspublishing.com

370

ListSort() function

ListSort() function, 30 ListToArray() function, 25-26, 48 Live Documentation Web site, 330 locale attribute (<cfobject> tag), 273 locale dates/times, 88 localfile attribute (<cfftp> tag), 148-149 localization, 340 Lock exceptions, 104 logging in/out users, 160-161 logical OR operations, 21 loginForm.cfm file, 166-167 loginLogic.cfm file, 165-166 looping through array elements, 40 through lists, 31-33 through strings, 14-15 over structures, 59-60 loops, 343-345 :lower: character class, 22 LSDateFormat() function, 88 LSTimeFormat() function, 88 LTrim() function, 11 lvisit column (CGLOBAL table), 100

M
Macromedia ColdFusion Support Center, 331 Macromedia Designer and Developer Center Web site, 330 Macromedia Online Forums Web site, 331 Macromedia Security Zone, 338 magazines, ColdFusion Developers Journal, 330 mail. See email mailto.cfm custom tag, 234

management charts chart series, 117-118 ColdFusion Administrator, 123 Client Management, 96-97 Session Management, 95-96 manager.cfc component, 266 manually creating queries, 69-70 markerSize attribute <cfchart> tag, 115 <cfchartseries> tag, 118 mask values DateFormat() function, 81 TimeFormat() function, 85 massmailer.cfm template, 234 massmailerClient.cfm template, 234 matching patterns excluded characters, 199-200 groups of characters, 198 range of characters, 198-199 single characters, 197-198 wildcard patterns, 196-197 Math.java listing, 322 MathBean.java listing, 323 MathHome.java listing, 323 MAX() function, 193 maxlength attribute (<cfprocparam> tag), 211 maxrows attribute <cfprocresult> tag, 207 <cfquery> tag, 68 <cfsearch> tag, 156 memory, query caching, 76-77 message attribute (<cfthrow> tag), 107 messages (email), 140-141 meta data, 268-269 Method attribute (<cfhttp> tag), 138 methods. See functions and methods

objects

371

Microsoft Security Bulletin, 337 Microsoft SQL Server stored procedures creating, 204-205 parameters, 207-208 Mid() function, 11, 14 migrating to ColdFusion MX, 339-340 Migration Wizard, 339-340 MIMEAttach attribute (<cfmail> tag), 140 MIN() function, 193 Minute() function, 87 MissingInclude exceptions, 104 mode attribute (<cffile> tag), 127-130, 133 Month() function, 87 MonthAsString() function, 87 moving files, 132-134 multidimensional arrays, 44-45 multiple exceptions, catching, 104-105 multiple result sets (stored procedures), 213

nameConflict attribute (<cffile> tag), 130 naming structures, 52 nested tags, 234-235 .NET, invoking Web services with, 312-314 Nielsen, Jakob (Web site), 332 NonEmptyDirectoryCannotBeDelet edException, 136 noOutputAttribute() function, 223 NoSuchTemplateException, 267 null attribute (<cfprocparam> tag), 211 Number data type, 284-286 numeric data type, 219

O
object attribute (<cfthrow> tag), 107 Object exceptions, 104 Object Management Group (OMG), 278 objects. See also components charts, 113 administering with ColdFusion Administrator, 123 chart containers, 114-116 chart series, 117-118, 121 click-through behaviors, 123 creating, 113-114 populating with query values, 121-122 supported CFMX chart types, 118-120 COM/DCOM objects, 276-277 CORBA objects, 277-280 creating, 271-273 Date, 79-80 DateTime, 79-80 Java objects, 275-276 methods, 273-274 properties, 275

N
name attribute <cfapplication> tag, 92 <cfargument> tag, 220 <cfchart> tag, 115 <cfdirectory> tag, 135 <cffunction> tag, 219 <cfhttp> tag, 138 <cfobject> tag, 272 <cfprocresult> tag, 207 <cfsearch> tag, 156 name key, 269 Name parameter (<cfquery> tag), 68

How can we make this index more useful? Email us at indexes@samspublishing.com

372

objects

Time, 79-80 XML objects child elements, 300 creating from other objects, 298 creating with <cfxml> tag, 295-296 creating with XmlElemNew() function, 296-298 creating with XmlNew() function, 296-298 element attributes, 298-300 inner text, 300-301 oldFileSize key (cffile structure), 131 OMG (Object Management Group), 278 online resources. See Web sites Open Directory ColdFusion Category Web site, 331 Open Directory Server-Side Java Category Web site, 331 Open WDDX Web site, 331 operators & (concatenation), 7-8 AND, 186-187 LIKE % (percent symbol) wildcard, 196-197 - (dash) wildcard, 198-199 [] (brackets) wildcard, 198 ^ (caret) wildcard, 199-200 _ (underscore) wildcard, 197-198 OR, 186-187 table of, 186 OR operator, 186-187 Oracle stored procedures, 205, 208-209 ORDER BY statement, 187 outfile argument (cfencode.exe), 236 output attribute <cffile> tag, 127-128 <cffunction> tag, 219, 222-223 <cfwddx> tag, 282

OUTPUT keyword, 211-212 output parameters (stored procedures), 211-212 outputTrue() function, 223 overriding functions, 267

P
paintStyle attribute (<cfchartseries> tag), 118 parameters. See attributes parsing input forms, 34-35 XML documents, 299 passing variables to stored procedures, 209-211 password attribute <cfftp> tag, 148 <cfhttp> tag, 138 <cfquery> tag, 68 <cfstoredproc> tag, 207 path attribute <cfcollection> tag, 152 <cfhttp> tag, 138 path key, 268 pattern matching excluded characters, 199-200 groups of characters, 198 range of characters, 198-199 single characters, 197-198 wildcard patterns, 196-197 percent symbol (%), 196-197 period (.), 20 pieSliceStyle attribute (<cfchart> tag), 115 pipe character (|), 21 plus sign (+), 19 POP servers, 140-141 populating charts, 121-122 Port attribute (<cfhttp> tag), 138 Power() function, 223-225

queries

373

precision values (DateCompare() function), 83 prefixes (variables) client, 96-97 session, 95-96 prepared statements, 200-202 :print: character class, 22 printing array contents, 344 PrintVars.java listing, 320-321 procedure attribute (<cfstoredproc> tag), 207 procedures, stored, 203-204 addEmployee, 211-212 calling, 206-207 creating in Microsoft SQL Server, 204-205 creating in Oracle, 205 getDepartmentsAndProjects, 213 giveRaise, 204-205 multiple result sets, 213 parameters defining in Microsoft SQL Server, 207-208 defining in Oracle, 208-209 output parameters, 211-212 passing, 209-211 passing variables to, 209-211 processRequest() method, 241 ProcessTagRequest() method, 247, 249 programs com2java.exe, 277 regsvr32, 276 projects table, 182 properties. See attributes protocols FTP downloading files, 148-149 uploading files, 147-148 HTTP requests, 137-139

LDAP directories adding entries, 143-144 deleting entries, 147 entry attributes, 145-146 querying, 141-143 renaming entries, 146 updating entries, 144-145 POP servers, 140-141 SMTP servers, 140 ProxyPort attribute (<cfhttp> tag), 139 ProxyServer attribute (<cfhttp> tag), 139 :punct: character class, 22

Q
qualifiers, 30-31 quantifiers, 19 Quarter() function, 87 queries. See also SQL statements C++ CFX tags, 253-255 caching, 76-77 converting to lists, 75-76 creating manually, 69-70 creating with <cfquery> tag, 67-69 displaying query results, 70-71 functions QueryAddColumn(), 70 QueryAddRow(), 69-70 QueryNew(), 69-70 QuerySetCall(), 69-70 QuotedValueList(), 75-76 ValueList(), 75 in-memory queries, 73-74 Java CFX tags, 245-247 LDAP directories, 141-143 populating charts with, 121-122 prepared statements, 200-202 query variables, 71-73

How can we make this index more useful? Email us at indexes@samspublishing.com

374

query attribute

query attribute <cfchartseries> tag, 118 <cfindex> tag, 153 query data type, 219, 284-285 Query interface, 246 QueryAddColumn() function, 70 QueryAddRow() function, 69-70 QueryNew() function, 69-70 QuerySetCall() function, 69-70 question mark (?), 19 QuotedValueList() function, 75-76

R
/r argument (cfencode.exe), 236 ranges of characters excluding, 199-200 matching, 198-199 reading files, 15, 125-126 receiving email, 140-141 recordCount variable, 72 recordSearched attribute (query objects), 157 RecordSet class, 294 recurse attribute (<cfindex> tag), 153 recursive functions, 223-225 Redirect attribute (<cfhttp> tag), 139 redirecting requests to servlets/JSPs, 318 references to functions, 225-227 REFind() function, 18-19 registering C++ CFX tags, 257-258 DLLs (Dynamic Link Libraries), 276 Java CFX tags, 256 Registry, 97-98 regsvr32 program, 276 remote function, 315

remotefile attribute (<cfftp> tag), 148-149 RemoveChars() function, 11 removing directories, 136 renaming directories, 136 files, 132-134 LDAP directories, 146 Replace() function, 11, 17-18 ReplaceNoCase() function, 11, 17-18 replacing substrings, 17-18 Request interface, 243 requests (HTTP), 137-139 required attribute (<cfargument> tag), 220 ResolveURL attribute (<cfhttp> tag), 139 Response interface, 241 result sets displaying, 70-71 sorting, 187 stored procedures, 213 resultset attribute (<cfprocresult> tag), 207 reThrowException() method, 249 rethrowing exceptions, 108-109 return values (functions), 222 returncode attribute (<cfstoredproc> tag), 207 returning custom tag results, 231-232 returntype attribute (<cffunction> tag), 219, 309 RIGHT OUTER JOIN statement, 191 Right() function, 10-11 role table (authentication application), 176 roles (user), 163 roles attribute (<cffunction> tag), 219

selecting data

375

rotated attribute (<cfchart> tag), 115 rows adding, 183 aliases, 187-188 deleting, 184 updating, 183-184 RTrim() function, 11

S
sandbox security, 337 scale attribute (<cfprocparam> tag), 211 scaleFrom attribute (<cfchart> tag), 116 scaleTo attribute (<cfchart> tag), 116 scope (application), 92-94 score attribute (query objects), 157 scripts (CFML) <cfscript> tag, 341-343 exception handling, 346-347 if-then-else statements, 343 loops, 343-345 switch statements, 345-346 search engines,Verity adding data to collections, 152-155 case sensitivity, 157 creating collections, 151-152 explicit searches, 157 simple searches, 155-157 Web site, 151 SearchEngine exceptions, 105 searching lists, 29-30 strings Find() function, 16-17 FindNoCase() function, 16-17 REFind() function, 18-19

Verity collections, 155-157 XML documents, 301-302 Second() function, 87 Secret Agents Web site, 331 Section 508 Web site, 332 Secure Socket Layer (SSL), 333 security authentication and authorization, 159 authentication example, 165-176 authentication realm, 159 blocks of code, 162-163 functions, 164 j_password field, 161-162 j_username field, 161-162 logins, 160 logouts, 160-161 user IDs, 162 user roles, 163 cross-site scripting, 336-337 DOS (denial of service) attacks, 335-336 encryption, 12-13 file uploading, 335 sandbox security, 337 Security exceptions, 104 SecurityFocus Vulnerability Database Web site, 338 session hijacking, 333-334 SSL (Secure Socket Layer), 333 URL injection hacking, 334-335 Web sites, 337-338 Security exceptions, 104 SecurityFocus Vulnerability Database Web site, 338 SELECT statement, 185 selecting data limiting rows returned, 185-186 multiple conditions, 186-187 multiple tables, 188-189

How can we make this index more useful? Email us at indexes@samspublishing.com

376

selecting data

pattern matching excluded characters, 199-200 groups of characters, 198 range of characters, 198-199 single characters, 197-198 wildcard patterns, 196-197 sorting results, 187 unique values, 192 Web form check boxes, 195-196 sending email, 140 HTTP requests, 137-139 series objects managing, 117-118 populating with explicit values, 121 seriesColor attribute (<cfchartseries> tag), 118 seriesLabel attribute (<cfchartseries> tag), 118 seriesPlacement attribute (<cfchart> tag), 116 server attribute <cfldap> tag, 148 <cfobject> tag, 273 server-side forwarding, 318 serverDirectory key (cffile structure), 132 serverFile key (cffile structure), 132 ServerFileExt attribute (<cffile> tag), 335 servers Apache, 315-316 FTP servers downloading files, 148-149 uploading files, 147-148 POP servers, 140-141 SMTP servers, 140 services (Web), 305 creating, 308-309 data types, 309-311 deploying, 311

DogService, 312-314 instances of, 307-308 invoking with .NET, 312-314 invoking with <cfinvoke> tag, 306-307 invoking with Java, 315-316 TravelService, 309-310 servlets accessing ColdFusion pages from, 319 data sharing, 320-322 forwarding requests to, 318 including, 318-319 session hijacking, 333-334 Session Management, 95-96 session prefix, 95-96 sessionManagement attribute (<cfapplication> tag), 92-93 sessionTimeout attribute (<cfapplication> tag), 92 SET statement, 183-184 setClientCookies attribute (<cfapplication> tag), 92-93 setData() method, 246-247, 255 setDomainCookies attribute (<cfapplication> tag), 92 setField() method, 294 Settings Migration Wizard, 339-340 setVariable() method, 244-245, 249-253 Setvars.cfm listing, 320 sharing data between ColdFusion and servlets/JSPs, 320-322 show3d attribute (<cfchart> tag), 116 showBorder attribute (<cfchart> tag), 116 showLegend attribute (<cfchart> tag), 116 showMarkers attribute (<cfchart> tag), 116 showXGridlines attribute (<cfchart> tag), 116

statements

377

showYGridlines attribute (<cfchart> tag), 116 simple Verity searches, 155-157 SimpleAuthenticator.cfm file, 168 sizing arrays, 38 SMTP servers, 140 sort attribute (<cfdirectory> tag), 135 sorting arrays, 41-44 lists, 30 result sets, 187 structures, 63-66 sortOrder argument (StructSort() function), 64 sortType argument (StructSort() function), 64 sortXAxis attribute (<cfchart> tag), 116 source attribute (<cffile> tag), 133 :space: character class, 22 Split() function, 221 SQL Server stored procedures creating, 204-205 parameters, 207-208 SQL statements, 181-182 aggregate functions, 192-193 AND operator, 186-187, 205 AS, 187-188 CREATE OR REPLACE, 205 CREATE PROCEDURE, 204-205 DELETE, 184 DISTINCT, 192 FROM, 188-189 FULL OUTER JOIN, 191 GROUP BY, 193 HAVING, 194 IN, 194-196, 209 INSERT INTO, 183 LEFT OUTER JOIN, 191

LIKE operator [] (brackets) wildcard, 198 ^ (caret) wildcard, 199-200 - (dash) wildcard, 198-199 % (percent symbol) wildcard, 196-197 _ (underscore) wildcard, 197-198 OR operator, 186-187 ORDER BY, 187 prepared statements, 200-202 RIGHT OUTER JOIN, 191 SELECT, 185 SET, 183-184 UPDATE, 183-184 VALUES, 183 WHERE, 185-186, 189-190 SSL (Secure Socket Layer), 333 start tags, 232-233 startRow attribute (<cfsearch> tag), 156 state information storing in database, 99-100 storing in Registry, 97-98 storing with cookies, 98-99 statements, 181-182 aggregate functions, 192-193 AND operator, 186-187, 205 AS, 187-188 assignment statements, 342 break, 345 catch, 346-347 CFScript, 341 continue, 345 CREATE OR REPLACE, 205 CREATE PROCEDURE, 204-205 DELETE, 184 DISTINCT, 192 FROM, 188-189 FULL OUTER JOIN, 191 function-call statements, 343 GROUP BY, 193

How can we make this index more useful? Email us at indexes@samspublishing.com

378

statements

HAVING, 194 if-then-else, 343 IN, 194-196, 209 INSERT INTO, 183 LEFT OUTER JOIN, 191 LIKE operator [] (brackets) wildcard, 198 ^ (caret) wildcard, 199-200 - (dash) wildcard, 198-199 % (percent symbol) wildcard, 196-197 _ (underscore) wildcard, 197-198 OR operator, 186-187 ORDER BY, 187 prepared statements, 200-202 RIGHT OUTER JOIN, 191 SELECT, 185 SET, 183-184 switch, 345-346 try, 346-347 UPDATE, 183-184 VALUES, 183 WHERE, 185-186, 189-190 stored procedures, 203-204 addEmployee, 211-212 calling, 206-207 creating in Microsoft SQL Server, 204-205 creating in Oracle, 205 getDepartmentsAndProjects, 213 giveRaise, 204-205 multiple result sets, 213 parameters defining in Microsoft SQL Server, 207-208 defining in Oracle, 208-209 output parameters, 211-212 passing, 209-211 passing variables to, 209-211

storing client state information with cookies, 98-99 in database, 99-100 in Registry, 97-98 string data type, 218, 284-286 strings, 7 ASCII character codes, 9 brackets ([]), 20-21 character classes, 22 concatenating, 7-8 decrypting, 12-13 delimiting, 8 encrypting, 12-13 escape characters, 21-22 hashing, 13-14 looping over, 14-15 OR operations, 21 quantifiers, 19 searching Find() function, 16-17 FindNoCase() function, 16-17 REFind() function, 18-19 substrings creating, 10-11 finding, 18-19 replacing, 17-18 substring functions, 11-12 wildcards, 20 StripCR() function, 12 struct data type, 219 StructAppend() function, 53-54 StructClear() function, 54 StructCopy() function, 54, 61-63 StructCount() function, 53 StructFind() function, 54, 57 StructFindKey() function, 61 StructFindValue() function, 61 StructGet() function, 61 StructInsert() function, 54-56

tables

379

StructIsEmpty() function, 52-53 StructKeyArray() function, 61 StructKeyExists() function, 52-53 StructKeyList() function, 61 StructKeyLists() function, 52, 59-60 StructNew() function, 52-54 StructSort() function, 54, 63-66 StructUpdate() function, 54-56 Structure argument (StructSort() function), 64 Structure data type, 284-286 Structured Query Language. See SQL statements structureReference argument (StructSort() function), 64 structures, 51 Attributes, 230-231 cffile, 131-132 conversions CFML to WDDX, 281-283 Java to WDDX, 290-292 WDDX to CFML, 285-286 WDDX to Java, 292-294 WDDX to JavaScript, 286-287 copying, 61-63 creating, 52 dot notation, 58-59 information functions, 52-53 inserting values into, 55-56 key names, 57 looping over, 59-60 management functions, 53-55 naming, 52 sorting, 63-66 ThisTag, 233 updating values in, 56-57 utility functions, 60-61 subject attribute (<cfmail> tag), 140

substrings creating, 10-11 finding, 18-19 replacing, 17-18 substring functions, 11-12 SUM() function, 193 summary attribute (query objects), 157 Sun Microsystems Java Web site, 331 SuperSplit() function, 221 suppressing function output, 222-223 swapping array values, 48 switch statements, 345-346 syndicated content (WDDX), 287-290

T
tables adding rows to, 183 aggregate functions, 192-194 aliases, 187-188 authentication application, 175-176 CDATA, 99 CGLOBAL, 100 deleting rows from, 184 department, 182 employees, 181-182 employee_projects, 182 grouping data in, 190-193 joining, 189-191 pattern matching excluded characters, 199-200 groups of characters, 198 range of characters, 198-199 single characters, 197-198 wildcard patterns, 196-197 projects, 182

How can we make this index more useful? Email us at indexes@samspublishing.com

380

tables

selecting data in limiting rows returned, 185-186 multiple conditions, 186-187 multiple tables, 188-189 sorting results, 187 unique values, 192 Web form check boxes, 195-196 updating, 183-184 tags C++ CFX tags, 239 attributes, 250-251 <CFX_GetMemoryStatus>, 251-253 <CFX_ReverseQuery>, 253-255 <CFX_SubString>, 250-251 <CFX_WriteMemoryStatus>, 247-248 ColdFusion variables, 251-253 creating, 247-249 installing, 257-258 queries, 253-255 registering, 257-258 <cfapplication>, 91-93, 97-99 <cfargument>, 220-221 <cfcatch>, 102-105 <cfchart>, 113-116, 122-123 <cfchartdata>, 113-114, 121 <cfchartseries>, 113-114, 117-118 <cfcollection>, 151-152 <cfcomponent>, 259-261 displayname attribute, 261 extends attribute, 265-267 hint attribute, 261 <cfdirectory>, 134-136 <cfdump>, 307 <cffile>, 15 accept attribute, 335 append action, 127-128 attributes, 126-130, 133 copy action, 134

delete action, 134 move action, 133 read action, 125-126 rename action, 133-134 ServerFileExt attribute, 335 upload action, 129-132 write action, 126-127 writing files, 126 <cfftp>, 147-149 <cffunction>, 164, 218-219, 222-223, 259-261, 309 <cfheader>, 288 <cfimport>, 237-238 <cfinclude>, 174 <cfindex>, 152-153 <cfinvoke>, 263-264, 306-307 <cfinvokeargument>, 264 <cfldap>, 141-147 <cflogin>, 160, 177 <cfloginuser>, 160 <cflogout>, 160-161 <cfloop>, 14-15, 31-35, 70-71 <cfmail>, 140 <cfmodule>, 236-237 <cfobject>, 262-263, 271-273, 276-277, 307-308 action attribute, 272 argumentcollection attribute, 264 class attribute, 272 component attribute, 262, 264, 273 context attribute, 273 locale attribute, 273 name attribute, 272 server attribute, 273 type attribute, 272 webservice attribute, 273 <cfoutput>, 39-40, 70-71, 190-191 <cfpop>, 140-141 <cfprocparam>, 209-211 <cfprocresult>, 206-207, 213

Time objects

381

<cfproperty>, 309-310 <cfquery>, 67-69 blockFactor parameter, 68 cachedAfter parameter, 68, 76-77 cachedWithin parameter, 68, 76-77 Datasource parameter, 68, 73-74 dbType parameter, 68, 73-74 Debug parameter, 68 example, 67 maxRows parameter, 68 Name parameter, 68 parameters, 68 Password parameter, 68 Timeout parameter, 68 Username parameter, 68 <cfqueryparam>, 200-201, 334 <cfrethrow>, 108-109 <cfreturn>, 222 <cfscript>, 341-343 <cfsearch>, 155-157 <cfset>, 94 <cfsetting>, 288 <cfstoredproc>, 206-207 <cfthrow>, 107-108 <cftry>, 102-104 <cfwddx>, 281-283 CFML2JS action, 283-284 CFML2WDDX action, 281-283 WDDX2CFML action, 285-286 WDDX2JS action, 286-287 <cfxml>, 295-296 converting to scripts <cfscript> tag, 341-343 exception handling, 346-347 if-then-else statements, 343 loops, 343-345 switch statements, 345-346 custom tags accessing, 230 attributes, 230-231

biu.cfm, 232 calling regardless of location, 236-237 <cfimport> tag, 237-238 <cfmodule> tag, 236-237 creating, 229-230 encoding, 235-236 getDomainTag.cfm, 231 greetingTag.cfm, 230 helloWorldTag.cfm, 229-230 importing, 237-238 mailto.cfm, 234 nested tags, 234-235 return results, 231-232 start/end tags, 232-233 <http>, 137-139 Java CFX tags, 239 attributes, 241-243 <CFX_GetMemoryStatus>, 244-245 <CFX_ReverseQuery>, 245-246 <CFX_SubString>, 242 <CFX_WriteMemoryStatus>, 240-241 ColdFusion variables, 244-245 creating, 240-241 CustomTag interface, 240 installing, 256 queries, 245-247 registering, 256 migrating to ColdFusion MX, 340 Template exceptions, 104 text. See lists; strings TextQualifier attribute (<cfhttp> tag), 139 ThisTag variable, 233 throwException() method, 249 throwing exceptions, 107-108 ThrowOnError attribute (<cfhttp> tag), 139 Time objects, 79-80

How can we make this index more useful? Email us at indexes@samspublishing.com

382

time spans

time spans, 85-86 timeCreated key (cffile structure), 132 TimeFormat() function, 84-85 timeLastModified key (cffile structure), 132 Timeout attribute (<cfhttp> tag), 139 Timeout parameter (<cfquery> tag), 68 times/dates comparing, 82-83 date formatting, 80-82 date information functions, 86-87 Date objects, 79-80 DateTime objects, 79-80 determining time differences, 83-84 functions CreateDate(), 80 CreateDateTime(), 80 CreateTime(), 80 CreateTimeSpan(), 85-86 DateCompare(), 82-83 DateDiff(), 83-84 DateFormat(), 80-82 Day(), 87 DayOfWeek(), 87 DayOfWeekAsString(), 87 DayOfYear(), 87 DaysInMonth(), 87 DaysInYear(), 87 FirstDayOfMonth(), 87 Hour(), 87 IsLeapYear(), 87 LSDateFormat(), 88 LSTimeFormat(), 88 Minute(), 87 Month(), 87 MonthAsString(), 87 Quarter(), 87

Second(), 87 TimeFormat(), 84-85 Week(), 87 Year(), 87 locale-specific dates/times, 88 time formatting, 84-85 Time objects, 79-80 time spans, 85-86 tipBGColor attribute (<cfchart> tag), 116 tipStyle attribute (<cfchart> tag), 116 title attribute (query objects), 157 to attribute (<cfmail> tag), 140 ToBase64() function, 126 tools Compatibility Analyzer, 339 Settings Migration Wizard, 339-340 toplevelvariable attribute (<cfwddx> tag), 282 transformationss, 302-304 TravelService Web service, 309-310 Trim() function, 12, 26 try statements, 346-347 type attribute <cfargument> tag, 220 <cfchartseries> tag, 118 <cfindex> tag, 153 <cfmail> tag, 140 <cfobject> tag, 272 <cfprocparam> tag, 210 <cfsearch> tag, 156 <cfthrow> tag, 107 type key, 268 types. See data types

U
UDFs. See user-defined functions underscore (_), 197-198

variable attribute

383

unique values, selecting, 192 UPDATE statement, 183-184 updating LDAP directories, 144-145 tables, 183-184 values in structures, 56-57 uploading files, 129-132, 147-148, 335 :upper: character class, 22 url attribute <cfchart> tag, 116, 122-123 <cfhttp> tag, 138 query objects, 157 URL injection hacking, 334-335 urlPath attribute (<cfindex> tag), 153 useit.com Web site, 332 user-defined functions, 217 arguments, 220-221 data types, 218-219 declaring, 218-219 recursive functions, 223-225 references, 225-227 return values, 222 suppressing output, 222-223 user_roles table (authentication application), 176 user table (authentication application), 175 user.cfm file, 167 UserAgent attribute (<cfhttp> tag), 139 username attribute <cfftp> tag, 148 <cfhttp> tag, 138 <cfquery> tag, 68 <cfstoredproc> tag, 207 users authentication and authorization authentication example, 165-176

authentication realm, 159 blocks of code, 162-163 functions, 164 j_password field, 161-162 j_username field, 161-162 logins, 160 logouts, 160-161 user IDs, 162 user roles, 163 logging in, 160 logging out, 160-161 user IDs, 162 usetimezoneinfo attribute (<cfwddx> tag), 282 utility functions (arrays), 47-48 uuid data type, 219

V
/v argument (cfencode.exe), 236 validate attribute (<cfwddx> tag), 282 value attribute (<cfprocparam> tag), 210 valueColumn attribute (<cfchartseries> tag), 118 ValueList() function, 75 values list values changing, 28-29 empty values, 26-28 structure values displaying, 57 inserting, 55-56 key names, 57 updating, 56-57 VALUES statement, 183 variable attribute <cffile> tag, 126 <cfprocparam> tag, 210

How can we make this index more useful? Email us at indexes@samspublishing.com

You might also like