MSOC Part 5: Application Event Handlers, An Intro

Handling the "big picture" application flow in ColdFusion is extremely easy, as a large part of it occurs directly in your application's Application.cfc file. Getting a handle on what's going on, in an MSOC app or any other app, is a matter of managing that application flow effectively. If you have a solid understanding of the event handlers available to you, then you can really begin to see the power behind ColdFusion's application framework.


MSOC Part 4: Application Constructor

OK, we're still working through setting up our Application for a Multi Site One Codebase scenario, and we haven't really gotten into the application yet (aside from dynamically setting the application name). Now is where we really have to take some serious consideration to variable scoping and application workflow. Thing is, there are dozens of ways to skin this cat (no, the view layer stuff comes later). The things we'll talk about in these next several posts aren't MSOC specific either, as they can apply to most any application. The first step is getting a hard handle on what you can do within the Application.cfc, and start to use it as intended.


I'm an ACP again for 2011

Once again I slid under the radar to become an Adobe Community Professional in 2011. I am, again, extremely humbled and honored to be included in such excellent company. Last year was my first year as an ACP, and events of last year prohibited me from being as involved as I would like (I've already posted more this month than all of last year put together), so I was very happy that I made the list again this year to get it on target. For those unfamiliar with the program:

The Adobe Community Professionals Program is a community based program made up of Adobe customers who share their product expertise with the world-wide Adobe community. The Adobe Community Professionals' mission is to provide high caliber peer-to-peer communication educating and improving the product skills of Adobe customers worldwide.

Adobe has also introduced a new community program, Adobe Community Champions. This is a new program for those who maybe don't have the time to write as much, or possibly the fiscal resources to speak at conferences, but are no less vocal in their support of Adobe products or in helping to educate newer members of the community. Congratulations to this new group. In brief:

Adobe Community Champions are the "megaphone" of the Adobe community, whose objective is to evangelize Adobe products beyond the existing circle of our Adobe ecosystem and helping connect people to the Adobe community.

2011 is going to be a great year, and I can't wait to see what Adobe has in the works. Congratulations to all the new and returning ACP's, and the new ACC's.

Scripted Noob: Queries (and Issues)

OK, I'm not a noob. Not even with cfscript. In fact, I love cfscript, and prefer to script as much as I can. ColdFusion 9 created a much greater degree of parity between cftags and cfscript. Unfortunately, the events of the past year and a half have left me with few opportunities to work on ColdFusion 9, so I'm playing catch-up on some of these great new enhancements. I did script the application.cfc back in October of 2009, but aside from that I was buried in writing a book, new job responsibilities, and more. I'm working to write all future ColdFusion examples in as much script as possible, but I still hit the occassional hurdle and ask for help.

So, while writing my examples for my last post, I kept hitting a snag while scripting a query. After banging my head on the wall for a while, I finally pinged the ColdFusionJedi himself for assistance. Ray probably though I was off my nutter, having never scripted a query, but we did run into something worth talking about.

First, I've gotten into a habit of scoping querynames. Why? If you didn't (in ColdFusion 8 or earlier) they were part of the VARIABLES scope. This can give you unintentional results, if you aren't careful, so I'd gotten into scoping querynames.

view plain print about
1<cffunction name="getSiteId" output="false" access="public" returntype="struct">
2     <cfargument name="cgiScope" required="true" type="struct" />
3 <cfset var LOCAL = StructNew() />
4 <cfset LOCAL.retVal = StructNew() />
5 <cfset LOCAL.retVal['success'] = true />
6 <cftry>
7     <cfquery name="LOCAL.qSiteId" datasource="#VARIABLES.instance.dsn#">
8     SELECT    siteId
9 FROM    sites
10 WHERE    urlAddress = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.cgiScope.server_name#" />
11 </cfquery>
12 <cfif LOCAL.qSiteId.recordCount>
13     <cfset LOCAL.retVal['result'] = LOCAL.qSiteId />
14 <cfelse>
15     <cfthrow type="My_Custom" errorcode="001" message="No siteId was found for this domain." />
16 </cfif>
17 <cfcatch type="any">
18     <cfset LOCAL.retVal['success'] = false />
19 <cfset LOCAL.retVal['message'] = CFCATCH.message />
20 <!--- Any other error handling --->
21 </cfcatch>
22 </cftry>
23 <cfreturn LOCAL.retVal />
24 </cffunction>

We'll come back to that in just a minute. I also like to pass argumentCollections into functions. Maybe it's just me, but it's something I do. So, I created a collection to pass into the constructor of a new Query.

view plain print about
3 /**
4 * FUNCTION getSiteIdByUrl
5 * @access public
6 * @returnType struct
7 * @output false
8 */

9 function getSiteIdByUrl(required struct cgiScope) {
10 LOCAL.retVal = {};
11 LOCAL.retVal['success'] = true;
12 LOCAL.qPrms = {};
13 = "LOCAL.qSiteId";
14 LOCAL.qPrms.datasource = VARIABLES.instance.dsn;
15 LOCAL.qPrms.sql = "SELECT siteId
16 FROM sites
17 WHERE UrlAddress = :urlAddress";
18 LOCAL.q = new Query(argumentCollection = LOCAL.qPrms);
19 LOCAL.q.addParam(name = "urlAddress", value = ARGUMENTS.cgiScope.urlAddress,
20 cfsqltype = "cf_sql_varchar");
21         try {
22     LOCAL.retVal['queryResult'] = LOCAL.q.execute();
23 if (!LOCAL.retVal.queryResult.recordCount) {
24     throw (type = "My_Custom",errorcode = "001",message = "No siteId was found for this domain.");
25 }
26 } catch (any excpt) {
27     LOCAL.retVal['success'] = false;
28 LOCAL.retVal['message'] = excpt.message;
29 // other error handling here
30 }
31 return LOCAL.retVal;
32 }

Alright, it seems to look OK, right? So why is it erroring? Well, Ray first told me to break it down some. Make it simple, remove the param, stuff like that. No dice. Then he said use getResult() after the execute statement. Uh huh. Then I decided to take the LOCAL scope out (it's local anyway, right?) Still no dice. Finally Ray said "Don't use argumentCollection." Use the set methods instead. Bam! It worked! I thanked Ray for the assist, and went back to recreating the full function.

Whoops! Not working again. Could not find retVal.qSiteId in LOCAL (or something like that). Now what? But then I saw I had put the LOCAL scope back on the query name. Took it off, and it worked like a charm.

view plain print about
3 /**
4 * FUNCTION getSiteIdByUrl
5 * @access public
6 * @returnType struct
7 * @output false
8 */

9 function getSiteIdByUrl(required struct cgiScope) {
10 var retVal = {};
11 retVal['success'] = true;
12 var sql = "";
13 var q = new Query();
14 q.setName("qSiteId");
15 q.setDatasource(VARIABLES.instance.dsn);
16 sql = "SELECT siteId
17 FROM sites
18 WHERE UrlAddress = :urlAddress";
19 q.setSQL(sql);
20 q.addParam(name = "urlAddress", value = ARGUMENTS.cgiScope.urlAddress,
21 cfsqltype = "cf_sql_varchar");
22         try {
23     retVal['queryResult'] = q.execute().getResult();
24 if (!retVal.queryResult.recordCount) {
25     throw(type = "My_Custom",errorcode = "001",message = "No siteId was found for this domain.");
26 }
27 } catch (any excpt) {
28     retVal['success'] = false;
29 retVal['message'] = excpt.message;
30 // other error handling here
31 }
32 return retVal;
33 }

Now, I should go back and test argumentCollection without the LOCAL scope on the query name, but for now I'm just happy that it works. I really do love scripting, and see where this really brings ColdFusion (once again) to another level as a language choice.

MSOC Part 3: Setting Up Your Applications

In part two we setup our web server to handle trafficing multiple domains to our single codebase, but now we have to get our applications setup. This is where we have to start thinking about separation. Since each domain is driven off the same set of physical files, we now have to consider how we separate one domain's sessions/actions/resources from another. You don't want one user's session to be cross domain in any way, and it wouldn't be good to have the media assets of one site displaying on another. Where do we begin? With the code.


New Custom Tag for Google Maps: CFGMap

Two years back I had to write a new mapping implementation for my former employer, who wanted to move away from MapQuest. We chose Google Maps, for a number of reasons. I wrote the implementation using Scott Mebberson's implementation, the Google Maps Tag. I was quick and easy, and ultimately we had to ditch it at the last minute. Why? Google's licensing at the time was too restrictive for our use case. Running almost 2,000 sites of of one codebase, we would have had to get a separate license key for each site, or get an enterprise license through Google. We were going down that path originally, but the cost at the time was almost $40k, and required some work on their part that they couldn't make our deadline (during the holidays), so when the autorenewal kicked in on (cheaper) MapQuest we just rolled with it.

Last month my former employer once again wanted to get rid of MapQuest. First we looked at our implementation, realizing that the same hurdles were in place. Next we looked at ColdFusion 9's new cfmap tags. That implementation works the same way, requiring the API key per domain. Luckily, I remembered seeing a tweet from someone about changes to Google's Maps API. A change that wouldn't require an API key anymore. So, I went to check it out.

The latest version of the Google Maps Javascript API is very nice, and has one very significant change.

The JavaScript Maps API V3 is a free service, available for any web site that is free to consumers.

This was perfect, as all of our sites were free to consumers. The first thing I did was try to make some adjustments to my initial implementation from Scott's tags. This didn't work, as there were some major differences in Google's new implementation. I ended up rewriting the entire implementation to work with the new API, creating my own custom tag. In forking Scott's code, I had to keep the license the same, which allows me to put it back out to the community at large (and with the approval of my old boss).

CFGMap is now available on RIAForge for download. My simple example source code is included with the download, and all code is heavily documented. My example uses JQuery for the basic DOM manipulation involved, but JQuery is not required to use the tag itself. You'll want to pay special attention to the testmap.js file, which shows how you can access your map object to plot directions and stuff. The tag puts a map on the page, and plots the points you've fed to it. It will even trigger a callback method, that you define, for passing lat/lng info that's been goelocated back to your database, reducing the geolocation hits on subsequent map visits.

It's only the first go-around with the updated library, and I'm sure that changes will need to be made at some point. I welcome any and all feedback, questions, and suggestions.

MSOC Part 2 - Web Server Configuration

When you're writing an application to run on multiple domains, but using a single copy of code, the first thing you have to work out is your Web Server Configuration. Basically there will be a DNS entry for each domain pointed to your server's IP Address. Then your web server will have to be configured to handle those domains. Rarely, in an MSOC environment, will you need a separate site entry for each site you're serving, requiring one entry to serve all sites. If you're working with IIS on Windows you could just use the Default Web Site entry in your IIS manager. Apache is a little different.


Many Sites, One Codebase

The ultimate of reusable code: using one codebase to serve many sites. Not copies of that codebase mind you, but one actual set of code templates. Update a template to fix a bug or add a feature and...? Presto! You've just updated X number of sites at once. Genius! Or is it?


Out With the Old, In With the New: Welcome 2011

2010 brought a lot of changes to the Blades household, the biggest of which being a promotion into management at my job. I took over managing both the Systems and the Development Team, at a time when there wasn't much team left. This began a long, arduous task of recruiting and hiring, and by the end of the year I had hired three ColdFusion developers, one User Interface/User Experience specialist, one SQL developer, one desktop administrator, and one systems administrator. Along with this we took on the completion of four major projects with very short deadlines. These had begun prior to me taking the new position, and were out the door fairly close to schedule, but with the fun that accompanies projects that are poorly planned ahead of time. One of them had a necessary last minute technology change on client-side rendering, which happened very quickly but dragged our testing and deployment schedule immensely. I even pushed through a switch on our mapping application at the last minute, to use Google's JavaScript v3 Maps API, which I actually got to write myself (you don't write much code in management).

2010 also brought my new book, Learning Ext JS 3.2. Due to some scheduling issues with one of the other authors, the book was released way behind schedule, finally arriving just weeks prior to the Sencha Conference. ExtLLC became Sencha, who released their new Sencha Touch framework for mobile development. During the Sencha Conference they also showed some sneaks of the upcoming Ext JS 4; a rewrite of most of the existing framework making it leaner, meaner, faster, better. Hey look! New book material ;)

Adobe had some killer announcements as well. After Apple had banned new apps to the AppStore from cross compilers, Adobe turned around and focused heavily on the Android platform, releasing some very nice tool enhancements for creating mobile content, as well as getting the Flash Player into the Android Marketplace. Later in the year, Apple reversed their cross-compiler position, and Adobe immediately began to work again on tools for creating mobile content on the iOS platform. Unfortunately for me, I was so heavily entrenched at the office that I didn't get to spend near enough time playing with these new developments (Adobe or Sencha). That's about to change though...

In my new role, as a manager, I had hoped to have the ability to drive technical change in a positive direction. Unfortunately, events that long preceded my taking the position worked hard to prevent that momentum, and many of the decisions were no longer mine to make. This, coupled with the drain on my time, my family, and my ability to do the things that I enjoy, were taking a heavy toll on my personal well being. Time had finally come for a change.

December was a very interesting month. First I recieved an offer for a new opportunity, once again doing full time development, but this time as a telecommuter with flex schedule. I wasn't looking at the time, but it kind of came to me, and appeared a perfect fit. I resigned my management position, with the customary two week notice. This was bittersweet, as I had devoted a lot of time and effort to the company over the past five years, but this new opp was too good to pass up, and works with a vertical that's very near and dear to my heart. On a Friday I said goodbye, and the following Monday went to work with the new gig for a week before taking 10 days of pre-planned family fun for my daughter's birthday and the Christmas holiday.

Now that my time is settling back into some semblence of a groove, I once again have some time to re-devote to the different development communities I enjoy being a part of. Jay Garcia recently asked me to convert his book's Ext JS examples over to ColdFusion for some of his readers. Ext JS 4 is due for public beta very soon. And Adobe has many things on tap for this coming year, for which I am on pins and needles. 2011 will be a new start, and is already shaping up to be a great year.

Rookie Mistakes: cfparam

All of us make rookie mistakes. I've always said it's best to learn from the successes and failures of others. One of my guys came to me earlier this week with one I had made myself a few years back. Something so simple, yet easily overlooked. Now, this guy has been writing ColdFusion for years, yet breezed right over it, assuming things acted one way when they acted completely another.

view plain print about
1<cfparam name="REQUEST.base" default="" type="string" />

So, fairly simple, right? My guy expected REQUEST.base to be an empty string. But it wasn't. Why? Well, in an earlier part of the process was another cfparam tag:

view plain print about
1<cfparam name="REQUEST.base" default="#CGI.query_string#" type="string" />

The code in the first block was in a template that, in this case, was nested within another template (via cfinclude) that contained the second code block in it's head. So CGI.query_string was already assigned to REQUEST.base. What one has to remember is that, with cfparam, the default is only applied to the variable if the variable does not already exist. If the variable already exists, then the cfparam tag has no effect. Basically this is just like writing something like this:

view plain print about
1<cfparam name="REQUEST.base" default="" type="string" />
2<!--- The same as --->
3<cfif not StructKeyExists(REQUEST,"base")>
4    <cfset REQUEST.base = "" />

Personally, I prefer the cfparam ;) (less code) Now, my guy was expecting an empty string, and was baffled as to why some other code wasn't processing properly. Once we sat down to dig in further, he saw that the value was not an empty string, and went about trying to figure out why it had a value. His confusion and frustration turned into anger over such a rookie hit, but everybody does it at some point.

Previous Entries / More Entries