ColdFusion 9 bug: Change in behavior with CGI variables
ColdFusion, Technology, CFMLEDIT: As Raymond Camden has pointed out in the comments, this is actually documented behavior, so I would like to state that this obviously isn't a bug in ColdFusion. However the fact remains that two developers (the only 2 out of 12+ that have upgraded to CF9) are seeing different behavior after upgrading to CF9 than the rest of the team. Unfortunately I will be unable to test this further until 3/15 and will update this post then.
Our company has an application that leverages SiteMinder for authentication. One of our developers, Andrew Leaf, noticed that when he installed ColdFusion 9 that he could no longer authenticate to the application. I was using an early beta version of CF9 and had no problems, so I initially dismissed the notion that this could be a ColdFusion 9 issue. After spending some time with Andrew as he walked through the ColdFusion Builder step debugger, we found the suspect block of code. The application code checked to see if a CGI variable has been set using StructKeyExists() and if it does, it sets session information based on that value. Today I set ColdFusion 9 up on new machine and am seeing the exact same behavior. What we are seeing is that with ColdFusion 9, when you test for existence of *any* variables in the CGI scope, it always returns true.
It essentially behaves in such a way that any variable name appears to be defined and paramed with a blank value in the CGI scope. For instance:
<cfdump var="#StructKeyExists(cgi,"someRandomString")" /> outputs YES
Likewise:
<cfdump var="#cgi.someRandomString#" /> outputs [empty string]
Need a "rock star" ColdFusion speaker for 7/30/2010 event in Dallas
ColdFusion, General, Technology, Conferences, CFMLFor 2010, the organizers have offered a travel and expenses budget to to each track (CF being one of those) to pay for travel and expenses to get one "rock star" from each of the technologies. Based on last year's attendees, I would say that a large percentage were beginner-intermediate, with our regular core of advanced guys around as well. The event will be on Friday July 30, in Addison. Unfortunately this falls during the same week as CFUnited, which obviously zaps a number of speakers from the pool of availability, but if you are interested in speaking, please let me know! I am dshuck pretty much everywhere... gmail, twitter, facebook, etc.

UDF: pcase() - Captilize first letter of each word in a string
ColdFusion, Tips and Tricks, Technology, CFML, UDFEnter the user-defined function pcase(). Through the use of regular expressions we are doing a search for word patterns and capitalizing the first letter in each word. Enjoy!
<cffunction name="pcase" access="public" output="false" returntype="string">
<cfargument name="string" type="string" required="true" />
<cfreturn REReplaceNoCase(LCase(arguments.string),"(^[a-z*]|[ *][a-z*])","\U\1\E","all") />
</cffunction>
Implicit constructor and CF9 style implicit accessors without ColdFusion 9
ColdFusion, Tips and Tricks, Technology, CFMLFor my implementation of this, I use a standard BaseBean class in a number of my projects which is extended by all of my model class objects. By leveraging the OnMissingMethod() in this BaseBean, I create these implicit accessors, in addition to an implicit constructor init() method as well.
There are multiple examples on the internet that show how you can use OnMissingMethod(), but for the most part, the examples that I have come across do not protect the class from being having infinite previously undefined properties added to it from the outside. For instance, I could have a class named Person.cfc with define properties of "firstName" and "lastName", but nothing would stop someone from doing Person.setThisIsNotAProperty(true) and that value would be dynamically added to the Person instance. While some might argue that the flexibility that this approach offers is a positive thing, I prefer to lock my classes down just a bit more. For instance I like the fact that I can open up a model class and see exactly what properties it can contain with clearly defined <cfproperty/> tags. From a maintainability standpoint the idea of "mystery" dynamic properties strike me as just wrong.
For my implicit constructor, I take an approach where I loop through any named arguments that were passed, and if the name matches a property that I have defined with <cfproperty/>, then it will be passed to a setter method. Any arguments that are passed that are not defined as a property are discarded.
So let's take a look at what this looks like. First I will paste the entire BaseBean, and below we will break it apart to discuss what is going on.
<cfcomponent output="false">
<cffunction name="init" access="public" output="false" returntype="BaseBean">
<cfset var i = "" />
<cfset initializePropertyList() />
<cfloop list="#this.propertyList#" index="i">
<cfif StructKeyExists(arguments,i)>
<cfset set(i,arguments[i]) />
</cfif>
</cfloop>
<cfreturn this />
</cffunction>
<cffunction name="initializePropertyList" access="private" output="false" returntype="void">
<cfset var i = "" />
<cfif NOT StructKeyExists(this,"propertyList")>
<cfset this.propertyList = "" />
<cfif ArrayLen(GetMetadata(this).properties)>
<cfloop from="1" to="#ArrayLen(GetMetadata(this).properties)#" index="i">
<cfset this.propertyList = ListAppend(this.propertyList,GetMetadata(this).properties[i].name)>
</cfloop>
</cfif>
</cfif>
<cfreturn />
</cffunction>
<cffunction name="onMissingMethod" access="public" output="false" returntype="any">
<cfargument name="missingMethodName" type="string" required="true" />
<cfargument name="missingMethodArguments" type="struct" required="true" />
<cfset var property = "" />
<cfif ReFindNoCase("^[gs](et)",arguments.missingMethodName)>
<cfset property = ReReplaceNoCase(arguments.missingMethodName,"^[gs](et)","") />
<cfset initializePropertyList() />
<cfif ListFindNoCase(this.propertyList,property)>
<cfif StructIsEmpty(arguments.missingMethodArguments)>
<cfreturn get(property) />
<cfelse>
<cfset set(property,arguments.missingMethodArguments[1]) />
<cfreturn this />
</cfif>
<cfelse>
<cfthrow message="The class #GetMetadata(this).name# does not have a property named #property# so the implicit #arguments.missingMethodName#() method is not available." />
</cfif>
</cfif>
<cfreturn />
</cffunction>
<cffunction name="get" access="public" output="false" returntype="any">
<cfargument name="property" type="string" required="true" />
<cfset var value = "" />
<cfset value = variables[arguments.property] />
<cfreturn value />
</cffunction>
<cffunction name="set" access="public" output="false" returntype="void">
<cfargument name="property" type="string" required="true" />
<cfargument name="value" type="any" required="true" />
<cfset variables[arguments.property] = arguments.value />
<cfreturn />
</cffunction>
</cfcomponent>Before we walk through the actual flow, I would like to point out the method initializePropertyList() that you see here:
<cffunction name="initializePropertyList" access="private" output="false" returntype="void"> <cfset var i = "" /> <cfif NOT StructKeyExists(this,"propertyList")> <cfset this.propertyList = "" /> <cfif ArrayLen(GetMetadata(this).properties)> <cfloop from="1" to="#ArrayLen(GetMetadata(this).properties)#" index="i"> <cfset this.propertyList = ListAppend(this.propertyList,GetMetadata(this).properties[i].name)> </cfloop> </cfif> </cfif> <cfreturn /> </cffunction>
By using the GetMetadata() function, we are able to introspect our instance and create a list of property names which we can reference in our other methods. This is what enables us to enforce the rules that I described above in which we make sure that a property exists before setting it in the constructor or through an implicit accessor. You will see in both the init() and OnMissingMethod() methods that we call this method to ensure the list of properties is available before testing against it.
With that established, let's take a look at the init() constructor method:
<cffunction name="init" access="public" output="false" returntype="BaseBean"> <cfset var i = "" /> <cfset initializePropertyList() /> <cfloop list="#this.propertyList#" index="i"> <cfif StructKeyExists(arguments,i)> <cfset set(i,arguments[i]) /> </cfif> </cfloop> <cfreturn this /> </cffunction>
After making sure that our propertyList has been initialized, we loop through that list and if there is a matching named argument, we call the set() method which sets that value into the variables scope.
So with the object initialized, let's take a look at our accessors. For our example, let's say that we have a Person object and we are doing to set our firstName property like this: person.setFirstName("Dave"). Since that setter method doesn't exist in our Person class, the OnMissingMethod() below will be invoked.
<cffunction name="onMissingMethod" access="public" output="false" returntype="any">
<cfargument name="missingMethodName" type="string" required="true" />
<cfargument name="missingMethodArguments" type="struct" required="true" />
<cfset var property = "" />
<cfif ReFindNoCase("^[gs](et)",arguments.missingMethodName)>
<cfset property = ReReplaceNoCase(arguments.missingMethodName,"^[gs](et)","") />
<cfset initializePropertyList() />
<cfif ListFindNoCase(this.propertyList,property)>
<cfif StructIsEmpty(arguments.missingMethodArguments)>
<cfreturn get(property) />
<cfelse>
<cfset set(property,arguments.missingMethodArguments[1]) />
<cfreturn this />
</cfif>
<cfelse>
<cfthrow message="The class #GetMetadata(this).name# does not have a property named #property# so the implicit #arguments.missingMethodName#() method is not available." />
</cfif>
</cfif>
<cfreturn />
</cffunction>We are then doing a regular expression test to see if the missing method names starts with either "get" or "set". If so, we derive the name of the target property by stripping the "get" or "set" off of the string. After making sure that our propertyList has been defined, we then look in that list to see if the target property actually exists in the instance. If it does, the request is then routed on to either the getter or the setter depending on whether arguments were passed to it. Otherwise an exception will be thrown to let the developer know that he or she has attempted to access an undefined property.
One thing you might notice is that when our conditional block routes the request to the setter, we return an instance of the class itself. This allows us to do method chaining like this: person.setFirstName("Dave").setLastName("Shuck"). it should be noted that this is not the approach that Adobe took with ColdFusion 9.
So let's take a look at it in action. Here is our Person.cfc class.
<cfcomponent output="false" extends="BaseBean"> <cfproperty name="id" type="string" /> <cfproperty name="firstName" type="string" /> <cfproperty name="lastName" type="string" /> </cfcomponent>
As you can see in the <cfcomponent/> tag, we are extending our BaseBean which is all that we need to have a workable domain class object.
When we put this together and access it in our code, we can do this:
<cfset person = CreateObject("component","Person").init(id=7,firstName="Dave", lastName="Shuck") />
<cfdump var="#person#" />
Once we have an instance, we can modify those properties like so:
<cfset person.setId(8).setFirstName("Johnny").setLastName("Rotten") />
I have not actually tested this on Railo, but I can confirm that it works in OpenBlueDragon (including GAE version), and ColdFusion versions 8 and 9.

Link to the HFDP 4 CF Connect recordings
ColdFusion, TechnologyThe entire archive can be found here.

Naked Domains in Google App Engine with GoDaddy
ColdFusion, Servers, Technology, JavaWhat does this mean? For my example of hikethecanyon.org, I originally set the site up with an id of hikethecanyon-org which was initially served as http://hikethecanyon-org.appspot.com. After adding the domain hikethecanyon.org, I was able to add the host "www" so that it would respond to http://www.hikethecanyon.org. However, since GAE doesn't support naked domains, I was unable to set it up to respond to http://hikethecanyon.org. I was able find a workaround using GoDaddy's domain forwarding functionality. While walking fellow CFML developer Paul Kukiel through the process today, he mentioned that someone should blog this, so here it is with screenshots along the way (edit - it appears that he decided to blog it himself as well!)
For my example, I am going to use a domain I have had sitting around doing nothing for a few years (j4n.org) and walk through the process with some screenshots and explanations along the way.
First, you need to create your application in the GAE dashboard and deploy your application. Many people have covered this topic in our community including Paul Kukiel and Aaron Lynch, so I will skip to the next step and assume that you have a running application on Google App Engine. As you can see below, I have set up an app with an ID of j4n-org that is answering on http://j4n-org.appspot.com which is just running a Mach-II skeleton application.

The next thing that we want to do is add the j4n.org domain to this application. When you go into the GAE dashboard, you will see that option under the "Application Settings" section labeled "Domain Setup"

On this page you will see a note that tells you that the domain you add must be set up for Google Apps, a service that allows all sorts of functionality, including email services and more. NOTE: You do not have to use any of those services, you simply need to sign up!

By clicking the link that says Sign up for Google Apps, you will be taken to a page like this:

Walk through the process filling out your personal information and you will come to a screen that looks like this:

For our example, we are going to choose to create a new CNAME record in DNS to prove to Google that we own the domain. By choosing that option

At this point we need to go to the GoDaddy DNS manager and create a host name google46353a9a2d07a035 and point it to google.com. You can see what that looks like below:

Once that record is in place we can go back to the Google Apps page and click the button labeled I've completed the steps above. If all goes well, you will see a page directing you through additional setup. For our purposes in this example, we are done with Google Apps for the time being. Now we want to go back to the GAE dashboard and tell it that we want to use the j4n.org domain.
When you enter that domain you should see a message that looks like this:

This will bring us back into the Google Apps control panel for the j4n.org domain and we can add hostnames to our application like this:

When you click add you will see an page like the one below instructing you to add another CNAME to GoDaddy for the host "www".

In the screen below you will see that we have successfully added the "www" host and while we were in there I went ahead and deleted the temporary CNAME that we had to create earlier to validate the ownership of the domain.

At this point we can actually reach our application with the address http://www.j4n.org as you see below:

While this is pretty cool and all, we still can't access our application with http://j4n.org. For this, we will count on GoDaddy to do the rest. Go back into the GoDaddy domain manager look for this link:

On the next page, you will want to fill out the field like you see before. However, before you do, click the "learn more" link in the bottom left corner. On that page there is a very important note: For your domain to forward, your domain's A record must be
64.202.189.170Once we enter in www.j4n.org you can see that our plan is going to work by hitting the "Preview" link to the right and seeing a snapshot of our app!

You will notice that we accepted the default settings which actually does a 301 redirect from http://j4n.org to http://www.j4n.org. This means that we will never actually see http://j4n.org in the address bar after the page has loaded. If you click on the advanced settings you can select "masking" which allows the url to remain in the address bar as http://j4n.org. Each option has its own advantages. Select what is right for your application and choose OK.
That is all you need to do! Now, a word of warning... we have all gotten spoiled by the seemingly instant DNS changes in recent years. You will find that when you make this forwarding change, you may need to wait up to half an hour or so before you can see the finished product (your mileage may vary).






Loading....