How I Do Things: ColdFusion and Ajax Requests

I get a lot of questions about handling Ajax requests. What are some best practices? How do you format your requests? How do you taylor your functions in ColdFusion? Those sort of questions. We (the ColdFusion community) have embraced Ajax. Many of us spent so much time working mostly server side code on top of simple (and often poorly thought out and formatted) forms, but user habits have changed. And so have we. You have to move with the times to keep up in the digital rat race.

Let's start with the stuff most CFers already know; the server side stuff. When we make an Ajax request, most often we will hit a function within a CFC. We need data. You can hit a .cfm page, and return a bunch of preformatted HTML, but often that's a lot of data transfer when your really only need a few bits of data. HTML code can get heavy, what with all the tags and such. Similarly, you could return data in XML form (WDDX is native in ColdFusion). XML is sometimes a required format, for certain requests, but like HTML it can often be overly verbose. When working with server side requests from the client, you want to minimize data transfer as much as possible. That's why Flash performs best using the AMF format, and why most Ajax applications work with JSON.

When constructing your ColdFusion functions for remote requests, you don't have to write functions that can only be used via Ajax. In fact, that would probably be bad practice, as it would really minimize the code reusability of your functions. Basically, the only true requirement, at the server side, is that you set your function's access attribute to remote.

view plain print about
1/*    
2 *    COMPONENT SomeComponent
3 *    Just some example component
4 *
5 *    @name SomeComponent
6 *    @displayName SomeComponent
7 *    @output false
8 */

9component {
10    /*
11     *    FUNCTION SomeFunction
12     *    This is just some function you've written
13     *
14     *    @access remote
15     *    @returnType struct
16     *    @output false
17     */

18    function SomeFunction(required string arg1, required numeric arg2) {
19        var retVal = {"success" = true, "message" = "", "data" = ""};
20        // some function stuff here
21        return retVal
22    }
23}

That's it. Nothing to it. Did you see the required line? The one you need for making a remote request of this function?

view plain print about
1*    @access remote

Ok, we're off and running. Sort of. "Hey, Cutter, what's with returning a struct? I thought we were working with JSON?" Well, let me explain...

Have you ever worked on some Ajax call, loaded the page up in the browser, and nothing happened? You change variables, do this and that, and get nowhere? Finally you open up Firebug, watch the request, and see that you CF code has errored out for some reason. Instead of data coming back, the browser is getting the thousands of lines of HTML and Javascript that is the ColdFusion error display. In the immortal words of Charlie Brown: AAUUGGHH!

What happens if your user sees this behavior? Some variable isn't getting set when you think it is, or some db guy added or removed a column from your database without your knowledge. An error gets thrown, and you never even realize it. Ouch! Users are quick to abandon you. How can you gracefully handle these things?

Well, how do you deal with error handling within your application? If this answer is "I don't", then you might want to explore another career path. No, just kidding (kind of). If you don't use error handling, this is the perfect scenario to start. There is no reason why you're users should see (or, in this case, not see) these types of issues within your application. By carefully thinking things out, you can provide a better user experience.

view plain print about
1/*
2     *    FUNCTION SomeFunction
3     *    This is just some function you've written
4     *
5     *    @access remote
6     *    @returnType struct
7     *    @output false
8     */

9    function SomeFunction (boolean debug = false, required string arg1, required numeric arg2) {
10        var retVal = {"success" = true, "message" = "", "data" = ""};
11        var q = new Query();
12        try {
13            // do your query stuff, setting the result to the "data" key
14            if (!retVal["data"].recordCount) {
15                throw(type = "ourCustom", errorCode = "RC_000", message = "No records were returned matching your criteria.");
16            }
17        } catch (any excpt) {
18            LogTheError(excpt); // Custom error logging for us
19            retVal["success"] = false;
20            if (!ARGUMENTS.debug) {
21                retVal["data"] = ""; // Clear the "data" key, so we don't pass unneeded bits back to the client. Override this by passing 'debug' into the request.
22                if (excpt.type eq "ourCustom") {
23                    retVal["message"] = excpt.message;
24                    retVal["errorCode"] = excpt.errorCode;
25                } else {
26                    retVal["message"] = "There was an error in making your request, and our administrators have been notified.";
27                    retVal["errorCode"] = "UN_001"; // Internal error code for an undefined error
28                }
29            } else {
30                retVal["message"] = excpt.message;
31                // and anything else you might want
32            }
33        }
34        return retVal;
35    }

Now, there are probably better ways to write your catch statements, but I'm just trying to lay some foundation here. The basic idea is, taylor a message to send back to your users in the event of a failure. "ErrorCode?" Many enterprise products have custom error codes they use to denote that a specific error has occurred (like the above 'RC_000' for no returned records).

So, we have a success key, denoting whether the requested ColdFusion function did what we wanted. We have a data key that we use for returning the data generated by the request. And, we have a message key that we may, or may not, use only in the event of an error. We've also used and errorCode in this scenario, only in the event of an error. "Cutter, something doesn't look right?"

view plain print about
1// You use the quotes and this struct access notation to preserve the casing of key names, which is important in JavaScript
2    var retVal = {"success" = true, "message" = "", "data" = ""};
3    ...
4    retVal["message"] = excpt.message;

That should be the basics of the server side stuff. But, then the question comes, "What if our model isn't under the web root? What if the model CFC's aren't web accessible?" OK, fair enough. Then you'll need a proxy CFC. Let's regroup a sec, and show what this might look like.

view plain print about
1/*    
2 *    COMPONENT SomeComponent
3 *    Just some example component
4 *    This example sits outside the webroot, in a 'com' directory that's mapped in the CFIDE
5 *
6 *    @name SomeComponent
7 *    @displayName SomeComponent
8 *    @output false
9 */

10component {
11    /*
12    *    FUNCTION SomeFunction
13    *    This is just some function you've written
14    *
15    *    @access public
16    *    @returnType struct
17    *    @output false
18    */

19    function SomeFunction (boolean debug = false, required string arg1, required numeric arg2) {
20        var retVal = {"success" = true, "message" = "", "data" = ""};
21        var q = new Query();
22        try {
23            // do your query stuff, setting the result to the "data" key
24            
25            if (!retVal["data"].recordCount) {
26                throw(type = "ourCustom", errorCode = "RC_000", message = "No records were returned matching your criteria.");
27            }
28        } catch (any excpt) {
29            LogTheError(excpt); // Custom error logging for us
30            retVal["success"] = false;
31            if (!ARGUMENTS.debug) {
32                retVal["data"] = ""' // Clear the "data" key, so we don't pass unneeded bits back to the client. Override this by passing 'debug' into the request.
33                if (excpt.type eq "ourCustom") {
34                    retVal["message"] = excpt.message;
35                    retVal["errorCode"] = excpt.errorCode;
36                } else {
37                    retVal["message"] = "There was an error in making your request, and our administrators have been notified.";
38                    retVal["errorCode"] = "UN_001"; // Internal error code for an undefined error
39                }
40            } else {
41                retVal["message"] = excpt.message;
42                // and anything else you might want
43            }
44        }
45        return retVal;
46    }
47}

And the remoting proxy...

view plain print about
1/*    
2 *    COMPONENT SomeComponent_Remote
3 *    Just some example proxy component
4 *
5 *    @name SomeComponent_Remote
6 *    @displayName SomeComponent_Remote
7 *    @output false
8 *    @extends "com.cc.Examples.SomeComponent"
9 */

10component {
11    /*
12     *    FUNCTION SomeFunction
13     *    This is just some function you've written
14     *
15     *    @access remote
16     *    @returnType struct
17     *    @output false
18     */

19    function SomeFunction (boolean debug = false, required string arg1, required numeric arg2) {
20        return Super.SomeFunction(argumentCollection = ARGUMENTS);
21    }
22}

"Wait a second! I thought you said this post was about Ajax? Where's the JSON? Where's the Javascript?" Well, Ajax is useless without the data, right? Now that you have your CFC's and functions setup, let's look at the Javascript to make our requests. First we'll look at a basic JQuery Ajax call. First, let's set a global JS variable in the page.

view plain print about
1<cfscript>
2    param type = "boolean" name = "URL.debug" default = false;
3    
4    savecontent variable = "REQUEST.adder" {
5        WriteOutput('<script type="text/javascript">var debug = #URL.debug#;</script>');
6    }
7    REQUEST.addHeaderOutput &= REQUEST.adder;
8
</cfscript>
9...
10<cfhtmlhead text="#REQUEST.addHeaderOutput#" />

Next we'll setup our Ajax call.

view plain print about
1$.ajax({
2    url: '/my/cfc/location/SomeComponentRemote.cfc'
3    dataType: 'json',
4    data: {
5        method: 'SomeFunction', // The method we're calling
6        returnFormat: 'JSON', // Give us the return as JSON
7        debug: debug, // the global default 'debug' set earlier by ColdFusion
8        arg1: 'This is my argument',
9        arg2: 42
10    },
11    success: function (response, status, options) {
12        if (response.data) {
13            // Do something with the data
14        } else {
15            // This means the request failed, and response.data should contain
16            // a 'message' key, to present to the user, and an 'errorCode' key
17            // that you're application might act on
18        }
19    }
20});

Here's the same basic Ajax request, using Sencha's Ext JS.

view plain print about
1Ext.Ajax.request({
2    url: '/my/cfc/location/SomeComponentRemote.cfc',
3    params: {
4        method: 'SomeFunction', // The method we're calling
5        returnFormat: 'JSON', // Give us the return as JSON
6        debug: debug, // the global default 'debug' set earlier by ColdFusion
7        arg1: 'This is my argument',
8        arg2: 42
9    },
10    success: function (response, options) {
11        var response = Ext.util.JSON.decode(response.responseText)
12        if (response.data) {
13            // Do something with the data
14        } else {
15            // This means the request failed, and response.data should contain
16            // a 'message' key, to present to the user, and an 'errorCode' key
17            // that you're application might act on
18        }
19    }
20});

I'm always surprised at how few CFers know this little bit. In case you missed it, the key piece to your request parameters comes down to one important argument.

view plain print about
1returnType: 'JSON'

When this argument is passed in the Ajax request parameters, ColdFusion will automatically serialize your native ColdFusion response objects into JSON. This is what allows you to set your returnType to a struct in your function definitions.

Most of this stuff has been around for quite a while. Some use it a lot more than others. Some are just afraid of client side stuff in general. There's a ton of material out there on how to do these things. This is my way of working with it. That doesn't make it right, or better than anybody else's. It's just what I've developed as a working pattern over the years. How do you handle it?

ColdFusion Hotfix 2 Released

Yesterday, Adobe released their second cumulative hotfix for their popular ColdFusion web application server platform.

Cumulative Hotfix 2 is the result of thousands of hours of review, development, and testing by the ColdFusion engineering team. These guys (and gals) review every bug submission, ask questions and gather feedback from many people within the CF development community, and work hard to address the issues in a timely manner.

Hotfix 2 covers major and minor issues dealing with JSON serialization, ORM relations, Solr indexing, cross-site scripting security, MS Exchange integration, and much much more. See the change list for more information.

A big thanks to the Adobe CF Engineering team for their ongoing, and ever increasing support, or the web's first application server.

Learn Something New

A great man once told me that "A Day Without Learning Is A Day You're Dead From The Neck Up". I strive to learn something new every day, and this week it's really easy.

Starting today (September 12, 2011), Adobe is hosting Adobe ColdFusion Developer Week. Whether you're a long time ColdFusion developer looking to pick up something new, a Flex developer looking to learn about easy integration, or a complete programming noob looking to get started in web development, there is something for everyone in this weeks list of sessions.

Being involved in several different dev communities, I often hear "ColdFusion is still around?" If you're part of that crowd, you really need to check out some of these sessions, covering everything from the basics, to PDF generation and caching and ORM usage, to server monitoring and mobile development. There's a lot to take in here, and it's all free.

MSOC Part 7: onSession Event Handler

Wow! I just discovered that this post has been sitting since February, waiting to be finished and posted. Time flies! Well, now that I've upgraded to the latest version of BlogCFC, it's a good time to add a new post.

In our last post, in this series, we talked about the onApplication event handlers, which automatically fire when a ColdFusion application starts and ends. This go around we'll discuss the onSession event handlers. As you may have guessed, the onSession handlers (onSessionStart and onSessionEnd) wrap a user session. The onSessionStart method fires when a user first visits an application, allowing you to initialize any SESSION scope variables.

[More]

New Job, New Home, A Lot of Work

It's been a very busy year, up til now. Work ramped up in February, contracting me for additional hours for a month and a half straight, after which I've worked on a sting of side projects. This helped me finance a move to Jacksonville, Florida. My new (daytime) job is full-time telecommute, which allows me to put my desk anywhere. Teresa wanted to get back to sunshine and beaches, being tired of the cold and snow of Tennessee winters, and chose Jacksonville for it's location and proximity to family and friends. Jacksonville is a great area, and we nailed a terrific place in Fleming Island. I like it because there's lots of tech (user groups and such), and it's not far from other tech centers (Orlando, Tampa, Atlanta, etc). It doesn't hurt that I can maintain a year around tan or that the beach is a short drive away.

A lot of work has come my way, often tacking an additional 40 to 60 hours a week on top of my normal day job schedule. Often I'll take a project that takes a week or two, then take a few weeks off to spend with the family (and catch up on my reading). I have a list of posts I need to write, due to exposure to some projects I hadn't previously been exposed to. Part of that already started with some exposure to the DataTables JQuery plugin, but I'm also lining up posts for jqGrid, jsTree, and the cfUniForm project. Evernote is filling up with little tidbits. The most difficult piece is coming up with the time to write examples. I'm particular about writing well formed code and documentation, which is why my posts sometimes get spaced out a bit.

One of the things I have discovered, in my exposure to these other projects, is how much I miss working with Ext JS day-to-day. JQuery UI is a good project, but lacks the maturity of Ext JS, and is missing too many key components for writing web applications (Data Stores, Grid, Tree, Menus, Tooltips, etc). My exposure to those other projects was an attempt to fill needs for which Ext JS would have been better suited, while locked into using JQuery UI. The JQuery UI team is working on closing that gap, but there is a lot of catch up necessary to match the breadth and power of Ext JS.

Speaking of Ext JS, Packt Publishing asked me to write the next Ext JS book on my own. While very flattered, I had to carefully weigh what that commitment would mean. Ultimately, I could not justify committing seven and a half months to writing the book with all of the other responsibilities I have right now. I will write a few articles for Packt (as part of my contract on the last book), but feel like I can continue to create blog content that would be more timely (no six month editorial process) and have a greater reach, and do so as my schedule permits without being a burden on my family. Sencha has already announced What to Expect in Ext JS 4.1, and recently put Ext Designer 1.2 in Beta, so there's a lot to talk about here.

Last, but definitely not least, I'm following all the buzz about the upcoming ColdFusion "Zeus". A quick Google Search already brings up a ton of info that Adobe has put out regarding the next version of the ColdFusion server platform, and it looks to once again be a significant release. Some of the big things already mentioned have been the move from JRun to Tomcat, the retirement of Verity in favor of Solr, the upgrade to Axis 2, and the inclusion of closures in CFML. That's just some of what's coming, as Adobe appears to be giving more and more detail during the various conferences through the year (and you never know the whole story until it's released).

How Did I Get Started With ColdFusion

Today, developers across the web answer "How I Got Started In ColdFusion". Each tale is unique, and this one is mine. My discovery of ColdFusion was a blessing, leading to a great life and fulfilling career of fantastic experiences with outstanding friends, acquaintances, and mentors. Here's how it started for me....

In the early nineties I was stationed at Ft. Meade, MD, working for the National Security Agency. Professional Development is a key thing in the military. We were expected to constantly learn and foster new skills that might help us do our jobs better. Being interested in computers at an early age, I started taking classes on computer systems and, having a background in linguistics, once again became fascinated with computer programming, taking classes in C and C++.

This new thing called "the internet" was becoming a big deal around the office. On my first PC, in the barracks, I was becoming a junky of Prodigy (then AOL, then my first ISP...) and learning to master 'search' through Alta Vista. At work someone asked me to create a 'web page' for our department on our local intranet. NCSA Mosaic was rapidly being replaced with Netscape Navigator, and updates to HTML had introduced new tags, like TABLE and IMG. Things were changing rapidly.

I started teaching myself HTML. Compared to 'programming languages', HTML was a walk in the park, though a bit frustrating in it's layout limitations. The internet was really becoming a big thing, and I thought I was ahead of the curve. I could make a living doing this? Wow, that would be cool! I had been in the Army for almost a decade. Still young, and single, with no kids, I had been thinking it was time to move on in the world. The Army had been good to me, but I thought I could do more, be more, back in the civilian world. (Mental Note: kick self later for being young and dumb)

I became friends with a guy who owned a screenprinting and advertising specialties company in southern Delaware. I created a quick web site for them, which helped them get some state funding for growth and expansion. They offered me a job, leading me to believe that my new skills could help them augment their corporate branding offerings to another level. After a brief assignment overseas, I put in paperwork to get out of the Army, packed up my stuff, and moved to the Eastern Shore to start a new life and career.

Things didn't quite work out as planned. The company really wanted someone to sell product to the military, develop paperwork automation processes in the office, and help out with 'production' (printing T-Shirts and stuff). Transitioning from military life to civilian life isn't always easy, and I had trapped myself in a strange place, with no real friends, in a job that wasn't going anywhere. I did this for three years, working 18+ hour days for next-to-nothing, becoming more and more disillusioned and watching myself slide behind the curve again. I had taught myself Visual Basic for Applications, tying together workflow between different MS Office applications, but my web development advancement had grown stale, being relegated to maintaining the company website with MS FrontPage (shiver). Then the best thing happened, I was layed off. The company decided they really needed a salesman more than a computer guru.

Unemployment has the fortunate by-product of forcing one to do the things they need to do. I knew that I loved computers, and had a talent for languages. I needed to get back to those things. For two months I called Manpower everyday, hauling stuff around warehouses or working in a buddies garage changing oil and tires, while scanning the want ads. Finally, I saw an ad for a Corporate Support Specialist with a regional Internet Service Provider. I went in for the interview, they gave me a test (on ColdFusion), and I failed. I didn't know the language, didn't have much for reference, and bombed it.

Luckily, the interviewer saw some potential. They referred me up to the manager of the Tech Support team. He was a retired Army guy himself, and had an open position in his department. The thought was, bring me in, learn the ropes and the business, and work on increasing my skills until I could transition to development. Now we're talking! I didn't make much, but it was more than the screenprinting company, better than being unemployed doing odd jobs, and it was getting back on track. I jumped on it.

I was a good Tech Support rep. We talked 70 year old ladies through manually creating a Windows Dialer to dial into our service. We talked 80 year old men through setting up and using email. Yes, that TV looking thing in front of you is called a 'monitor' and, no, that is not a cupholder (seriously). Things were pretty smooth, and I wasn't on the phone all the time, so I started brushing back up on web dev skills. I got on a mailing list to learn JavaScript (which had passed me by til then). I started playing with DHTML, and this new thing called CSS (Layout! Hot Damn!) To make it worthwhile, I created the first real FAQ for the ISP; a set of interactive, online tutorials for the basic tasks we always talked about on the phone (setting up email programs, configuring browser settings, etc.)

This was all good, but wasn't getting me moved to Corporate Support. I took on a real challenge on my own. I got a copy of Visual Studio from the college, and began to create a dialer application for the company. I had already gotten up to speed on the changes in HTML, picked up better than passable skills in JavaScript, and was fairly good with CSS. Now I needed to dig in deeper into programming, so I started learning Visual Basic (not a big jump from VBA), then InstallShield scripting, which in turn led to diving back in to C++. Four months later the ISP was pressing it's own CD's of the dialer app for distribution to new clients. And, they were hiring again in the Corporate Support department.

Delmarva Online was a small regional Internet Service Provider, with about 14,000 dialup clients, that also ran a small hosting business. They hosted roughly 600 corporate sites for everything from churches to school districts to car dealerships and small manufacturing companies. In it's earliest days they had used a server-side technology called IHTML, and by the time I moved into Corporate Support they still had one or two clients on that platform. But the majority of their clients were on something called ColdFusion, a server-side technology built on C++ by a company called Allaire. Coming in to Corporate Support, my job was similar to that of Tech Support; walking clients through email configuration and stuff. But, I also got the responsibility of taking on minor coding tasks to hosted sites, slowly learning ColdFusion.

Having started on-line classes for Computer Science, with the University of Maryland University College, I was really getting in to Object Oriented Programming. .NET was in beta at this point, and I was thinking that it was going to be 'the thing', but I was getting better and better everyday with ColdFusion. Our lead programmer, Joel Firestone, had taken his hobby site for guitar (Guitarists.net) to a new level, getting it mentioned in Guitar Player magazine. Pretty cool for a site that had taken a few months of "spare time" to develop in ColdFusion (Joel moved to PHP later in life, and has since moved his site to that as well, but that's another story). We had one or two sites on ASP that had taken me forever to implement easy stuff, like mail, that took me seconds in ColdFusion. I really started to pick it up, combining ColdFusion with JavaScript, CSS and XHTML. They started giving me small apps to write, then full site rewrites. ColdFusion 5 was released, and UDF's were the new rage, then Blackstone started talking about CFC's, and I quickly started seeing full Object Oriented web development on the horizon. ColdFusion was moving to a Java EE server, and the changes were awesome!

Jump ahead a decade. Wow, how times have changed! ColdFusion has changed hands twice (from Allaire to Macromedia to Adobe), and has gotten better and better with each iteration. I've watched ColdFusion grow with the web, and continually been amazed by the things that have been done. I've had the opportunity to play with close to a dozen server-side technologies in that time, and always come back to ColdFusion for the core of my work. It does far more than just pay the bills, and I'm never short of work, and thank God every day for the opportunities that have come my way this past decade. ColdFusion development (and developers) change and grow every day. Millions of sites with outdated code are being upgraded, or rewritten, to more modern development standards. ColdFusion is an incredibly easy language to learn and use, which also makes it easy to write bad code as well. Today we see developers apply new skills and standards towards writing scalable and efficient code, which highlights the ROI of ColdFusion development in that those upgrades and rewrites are accomplished in a fraction of the time (and cost, and resources) than it would take in many competing technologies. The web's first web application server platform and language has proven itself as an enterprise ready rapid application development platform, and modern developers are proving it, time and again, as a real world solution toward answering real business needs rapidly and effectively.

I've seen great frameworks come about, outstanding public (and private) sites, Web Services enter the fray and change and grow, ColdFusion developers (historically server-side people) embrace JavaScript and Ajax, mobile become the new hotness, Open Source projects multiply and grow and grow and grow... It's an exciting time to work on the web, and just as exciting to work with such a dynamic technology as ColdFusion. I can't wait to see what comes next!

The Next Wave: Ext JS 4 Beta 1

I only have a few minutes, as I am totally buried right now, but I wanted to reach out to everybody to spread the word about Ext JS 4 Beta 1 being released yesterday. Some of you may have been following the Developer Previews on the Sencha Blog, and if you have you already know some of the amazing work coming out of Sencha for this release. If you haven't, well let me give you a really quick recap of some of what you could expect:

There's also an entirely new rendering engine under the hood, new components, and more. You can even run Ext JS 3 & 4 on the same page, if needed. Go to the Sencha site to see some great examples of what you can do. It is still a beta, so the forums are pretty busy with people learning the ropes and filing bugs as they're found, but the foundation has been laid for a truly ground-breaking update to this library.

Java RegEx replaceAll In ColdFusion

In an odd turn, I was given text like below to display on a page.

view plain print about
1Some Title<br />
2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;A.&amp;nbsp;&amp;nbsp;Some&amp;nbsp;subheader&amp;nbsp;here<br />
3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;B.&amp;nbsp;&amp;nbsp;Some&amp;nbsp;other&amp;nbsp;subheader&amp;nbsp;here<br />

Two issues here (aside from the fact that it should have been an HTML list): 1) it needed to retain the spacing format, and 2)it needed to wrap within a sized element. A non-breaking space, when viewed, appears as a space ( ), but isn't an actual space, so the browser doesn't know where to break the text when wrapping it in an element. Hmmmm....

[More]

MSOC Part 6: onApplication Event Handlers

In our last MSOC post we gave a brief introduction to the ColdFusion application framework's event handlers. With this post, we'll give a brief explanation of the onApplication handlers.

onApplicationStart

The CFC constructor is the first thing that processes on a request, but the first request to an application will then run the onApplicationStart() method. This method will run prior to any other code in the request, and is your opportunity to setup variables in the shared APPLICATION scope. Within the onApplication handlers, you are able to manipulate the APPLICATION scope without being required to lock access to the scope, as no other process can access the variables until the method is completed, and preventing race conditions.

The APPLICATION scope is a where you place those variables you will use throughout the entire application. For instance, you may wish to keep a simple variable that holds the phone number for your Customer Support team, so that if the number changes it can instantly update throughout the site. The APPLICATION scope is also where you would store objects that you would use throughout your application, like a user or page manager.

onApplicationEnd

As you might have guessed, this is the last method to fire during the lifetime of your application. In your constructor you would have set THIS.applicationTimeout. This is the length of time that the application will continue to live after it's last request. After this timeout limit is reached is when the onApplicationEnd will fire. This allows you to do some cleanup, or some application level logging. It is important to note that this only occurrs if the application does timeout. It will not fire if the server instance is shutdown/restarted.

Consider This

When you want to write to the APPLICATION scope from one of the other event handlers, you must lock access to prevent race conditions. You may, in your onServerStart method, have created a struct to log each active application (start up, last hit, timeout, number of sessions). When accessing the SERVER scope, you must also lock access.

You also have to heavily consider how much work you're putting into each of these event handlers, especially within the onApplicationStart. The more process you have there, the longer the startup time is for your application. It will be up to you just how much of a trade off that startup time is to the overall performance of your application.

The last thing you must consider (and I'll say this a lot during this series) is what variables and objects belong in what scopes. Remember that everything you place in the APPLICATION scope adds to the overall RAM utilization of that app. Everything you place in the SESSION scope will add to the overall RAM utilization of that session. This is especially important to consider in an MSOC environment. Try to think of it in these terms: the size of the SERVER scope + THE size of every APPLICATION + the size of every SESSION in every application must be less than the amount of RAM allocated to your ColdFusion instance. Careful planning is critical.

In our next piece we'll discuss the onSession event handlers. Hopefully you're finding this series beneficial. As always, any feedback/comments/questions are welcome.

Using The DataTables JQuery Plugin

For adminstrative applications, most of my readers know I'm a huge proponent of the Ext JS library. But for front-end, consumer facing sites, I'm often pushed to use JQuery. JQuery is very light weight, and wonderful for DOM manipulation, but it isn't a component library. When you want widgets for advanced data display, you have to use something like JQueryUI. Unfortunately, JQueryUI doesn't yet have a grid component (though they are working on it). So when I recently needed a dynamic, paging data grid, I started looking for something that used server-side data requests and could be skinned using the ThemeRoller. That's when I came upon the DataTables plugin.

It took me some time to figure out the works of how the plugin makes server-side requests. What I found was that, by default the plugin passes an extreme amount of data on a request, and not typically in a format very conducive for our needs. I also had to find a way to pass the method name and data returnFormat needed. That's when I discovered that I could override it's default request. Once I figured that out, I wrote a method to parse the data to create a request object more conducive to a ColdFusion Component request. It passes the following arguments along in a request:

  • iDisplayStart - The number of the first record in the requested set.
  • iDisplayLength - The number of records to return in the requested set.
  • sEcho - A DataTables request identifier, to be echoed back with the return.
  • aoSort - If present, this will be a JSON string representation of sort columns and orders. It's an array of objects:
    • colName - the column name
    • sortDir - the sort direction (asc|desc)

After getting data to my CFC, I had to build my paging query. For my example here I wanted to use the MySQL database I use for my blog, so this was a learning experience for me. The biggest trick for me was getting the TotalCount of records, as this is extremely different from MS SQL, requiring two separate SQL statements for the query and the count. Since DataTables can also sort off of different columns, I needed a way to dynamically set the ORDER BY clause of the query. You can't bind parameters to the ORDER BY clause, but you want to protect your server from SQL injection attack, so you have to validate that part of the request (especially as it's an ajax request, which would be easier to manipulate). Pete Frietag came up with a little regex expression that could be used in this case.

We set up our component to return a structure in the following format:

  • success - A boolean to denote success or failure of the request.
  • message - Only returned if the request fails, a message to state why the failure occurred.
  • totalCount - The total number of records available for the filters applied.
  • result - The paging query.

The last piece of the puzzle was back on the client-side again, where the ColdFusion return had to be put back into a format that can be consumed by the DataTables plugin. This was actually very easy because of the way that ColdFusion returns query data.

Once I had these methods, I wanted to find out how to write a feature plugin for DataTables. One where I could identify additional config arguments in DataTables, and have it automatically work. I contacted Allan Jardine, who wrote DataTables (and has some great web dev tools on his site). He never wrote in that capability, saying that the method override was the only way to make this happen. What I did discover was that I could add options to the standard DataTables config. I created a new option for DataTables, oCFReaderDT, which takes an object of options. Only one argument is required, "method", to define the method to call in the CFC request. I also setup the processor to accept an option, "sForm", as a string selector of a form whose values you may need in the request (i.e.: 'form#myForm'). Then I wrote a custom function that encapsulated the previously written methods into one method, that could then be used as the value of the "fnServerData" option in the DataTables configuration object.

In the download link below is a zip file with all of the files for the example, which has been heavily commented so you know what's going on. Though written for ColdFusion 9, I have included both scripted and non-scripted CFC's. I hope you find this useful, and please leave any comments/questions/suggestions through the Comment Form or Contact links below.

Previous Entries / More Entries