Colony on Railo - Initial Testing Looks Good

Today we did initial testing of the Colony platform on the open source Railo 3.1 application server. I am pleased to share our findings that so far Colony runs without any issues on Railo. While we expect most customers that deploy CFML application to continue to use the award winning Adobe ColdFusion 9 Application Server, we are testing Colony on Railo in order to provide a fully free and open source distribution of the Colony application platform. 

CFEclipse CFE project nature and the F12 function

A client of ours just asked me to show him how to load a project file in the browser using the F12 key in CFEclipse, so I thought I would document this little gem for everyone. In order to have CFE fire the browser with your project file from the F12 key, your project has to be set to the CFEclipse Nature. So how do you set the project to a CFEclipse nature? Well, that's the rub.

I am running Eclipse 3.3.2 (Europa). Iknow, I'm a bit of a slug upgrading Eclipse. When I find a stable build that works with all of my plugins, I tend to stay on it for a long time. In my version of Eclipse, there seems to be no automated way to set the CFEclipse nature in the project. Instead, I had to manually add the CFE Nature to the .project file. Fortunately, that's fairly easy. Here is the .project file for my Colony project on my laptop:

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
    <name>colony</name>
    <comment></comment>
    <projects>
    </projects>
    <buildSpec>
    </buildSpec>
    <natures>
        <nature>org.cfeclipse.cfml.cfenature</nature>
    </natures>
</projectDescription>
 

As you can see, in the<natures/> section of the XML declaration, I have added the CFE Nature. All you need to do is add that line to the .project file (which lives in the root of the project), save the file, close and re-open the project. On Eclipse 3.3.2, you will then have an option called "Edit URL" when you right click on the project name in the Navigator pane. Select Edit URL and set the URL to the project root. Mine, for instance, is http://dev.cfcolony.org/. Don't forget the trailing slash! Then you should be able to open a file in the project and hit the F12 key to launch the file in the browser. Keep in mind with framework based sites like Colony (Model-Glue 2), you should launch the root index.cfm file, anything else may result in errors in execution. (There are exceptions I will gloss over here.)

In the Ganymeade version of Eclipse, right clicking on the project name in the Navigator shows a Ad/Remove CFEclipse Project Nature option. Click that option and another right click on the project name shows a CFEclipse menu option with a submenu option of Edit URL.

ColdFusion Struct Key Names and JS/AS Code

If you use ColdFusion in conjunction with Javascript libraries like ExtJS or Actionscript in Flex or Flash, you are probably used to seeing ColdFusion structures rendered in JS/AS in ALLCAPS, like so:

 

CF code:
<cffunction name="getStruct" access="remote" returntype="struct">
     <cfset mystruct = structnew()/>
     <cfset mystruct.somekey = "somevalue"/>
     <cfreturn mystruct>
</cffunction>

JS result:
// a little pseudo-code
myObject = myService.getStruct();

//alert will show "somevalue"
alert(myObject.SOMEKEY);

By default, ColdFusion translates all struct keys into all caps when it returns a structure via a Web service or AMF call. If you would rather maintain your struct keys in lowercase or camelCase, you can simply use bracket notation instead of dot notation. Personally, I prefer dot notation for convenience, but sometimes I encounter a situation where dot notation would not work because the keyshave spaces or other characters that are illegal in ColdFusion variable names. As we can see with this example, there is another good reason to use bracket notation - case matching between client and server struct keys.

If we change our ColdFusion code to use bracket notation instead of dot notation, we'll get the keys in the case they were written:

CF code:
<cffunction name="getStruct" access="remote" returntype="struct">
     <cfset mystruct = structnew()/>
     <cfset mystruct["someKey"] = "somevalue"/>
     <cfreturn mystruct>
</cffunction>

JS result:
// a little pseudo-code
myObject = myService.getStruct();

//alert will show "somevalue"
alert(myObject.someKey);

 

Gartner Praises ColdFusion

Kristen Webb Schofield writes that a new Gartner report praises Adobe ColdFusion and recommends that agencies continue their investment in the platform, which they see as enjoying a bright future with Adobe as its steward. You can buy the report from the Gartner web site.

Usability - Ugh, We Keep Making the Same Mistakes

Why isn't email address/name a top level, persistent email search feature for every mail client in exstence? If I am looking for an email in my mailbox, I generally am looking for a person by name or email address, a company by name or domain, or a particular topic.

Take Thunderbird, my current email client. I can quick search a subject or sender in any single folder, but if I want to search the entire mailbox, I have to pull up a special interface that leaves a lot to be desired from a usability perspective.

We keep making the same mistakes, in part because we keep building the same tools over and over again, trying to re-invent whatever application space we are designing for as we go.  One of the main reasons for us torelease the Colony application platform was to create the potential for large scale code re-use across a broad spectrum of typical web applications. 

Are you planning on building an application in CFML from scratch? Take a look at Colony and see if it might help you. A tutorial on using Colony is coming soon, stay tuned.

Introducing the Colony application platform

For the past three years, I have been working on and off on an open-source CFML-based Web application. It  started out as a simple system to store arbitrary structured and unstructured content. I started using it to build more and more complex Web applications, and over time it grew in size and scope. I thought about it for awhile as a content management system, but content management is not what I was aiming for, and not where the platform has really evolved.

After struggling with terminology and purpose, I started thinking about the application as an application platform. What is that? I see it as an implementation of typical application patterns in an integrated package that allows a develoepr to use it in whole or in part, building on the core libraries to create a new solution. 

Once I had the concept clear in my head, I started casting about for a name. After lots of pondering and brainstorming sessions with my colleagues on the CF-Community list, I decided to call the platform Colony. To me, Colony is all about staking out new territory on the Web, building compelling new services, and advancing the state of software.

Colony is also about shared effort and shared reward. To that end, we have just released the platform in alpha under the Apache Software License 2.0. You can get the alpha code and see more about the platform at www.cfcolony.org. The site is graphically challenged and light on content at the moment, but that wil cahnge soon. 

Gotcha in ColdFusion 8 round() function

There is a gotcha in the ColdFusion round() function that you may not have seen. According to the ColdFusion docs, a value ending in .5 will always be rounded up to the nearest integer. However, ColdFusion has two ways of casting real numbers into underlying Java datatypes - double and float. It appears that when numbers are cast as double, they may not round up. Take a look at this sample code someone I know was having a problem with:

<cfscript>
  AmounttoTax = 30;
  CATaxExt = AmounttoTax * 0.0725;
  CATax = Round(CATaxExt * 100) / 100;
 </cfscript>
 <cfoutput>#CATax#<br></cfoutput>

According to the CF documentation, this code should yield a value of 2.18, because the original value of 217.5 (2.175 * 100) should be rounded up to 218. Instead, it rounds down to 217, and the resulting value is 2.17. 

Let's try changing the underlying datatype:

<cfscript>
  AmounttoTax = 30;
  CATaxExt = AmounttoTax * 0.0725;
  CATax = Round(javacast("float",CATaxExt * 100)) / 100;
</cfscript>
<cfoutput>#CATax#<br></cfoutput> 

This code yields the expected value of 2.18.

Just to be sure, let's cast the value explicitly into a double:

<cfscript>
  AmounttoTax = 30;
  CATaxExt = AmounttoTax * 0.0725;
  CATax = Round(javacast("double",CATaxExt * 100)) / 100;
</cfscript>
<cfoutput>#CATax#<br></cfoutput> 

Once again, we end up with a value of 2.17 instead of 2.18, leading (in the case of this code example) to a tax calculation that is one penny short of the correct withholding amount for the transaction. One penny may not be a lot in isolation, but over a large volume of transactions it may acculumulate into a non-trivial amount. 

ColdFusion and ExtJS - Pre-populating a TreePanel in Ext

If you have used ExtJS, you know that it contains a rich TreePanel control that, dare I say, rivals Flex in its functionality. The TreePanel control offers ColdFusion developers a very easy way to pre-populate the nodes of the Tree when the Tree loads, using nothing but Javascript. Take a look at the code below. (Note that this entire code block, and other Ext code, would normally be enclosed in an Ext.onReady(function(){}); command, but that code has been truncated for brevity. 

The first line creates a new Ext.tree object. The next section creates a TreePanel object to display the contents of the tree on the page. Note that the TreePanel has an attribute called loader that is tied to a TreeLoader object. The TreeLoader allows us to specify a URL that will be called to load the array of nodes for the tree. The last section of the code renders the tree and expands thenodes to be visible once the tree renders.

    var Tree = Ext.tree;
   
    var tree = new Tree.TreePanel({
        el:'tree-div',
        useArrows:true,
        autoScroll:true,
        animate:true,
        enableDD:true,
        containerScroll: true,
           rootVisible: false,
        root: {
            nodeType: 'async',
            text: '--',
            draggable:false,
            id:'0'
           
        },
         loader: new Ext.tree.TreeLoader({
          url:'/services/ObjectProxy.cfc?method=getChildren',
          requestMethod:'GET',
           preloadChildren: true,
          baseParams:{}
        })
    });

    // render the tree
    tree.render();
    tree.getRootNode().expand();

In this code, the TreeLoader calls url:'/services/ObjectProxy.cfc?method=getChildren'. Let's take a look at that function to see what data the tree needs to populate its nodes. This code is taken from our forthcoming open source application platform in its RoseCMS implementation. 

 

    <cffunction name="getChildren" access="remote" returntype="string" returnformat="plain">
        <cfset var applicationFacade = application.RoseBeanFactory.getBean("applicationFacade")/>
       
        <cfset var cmTreeManager = application.RoseBeanFactory.getBean("cmTreeManager")/>
        <cfset var cmTreeNodeManager = application.RoseBeanFactory.getBean("cmTreeNodeManager")/>
        <cfset var treeID = cmTreeManager.getcmTreesQuery(TreeName="Site Navigation").qList.TreeID />
        <cfset var json = createobject("component","views.util.json")>

            <!--- otherwise, get the tree from the database and set it into a TreeWalker --->
            <cfset siteNavTree= application.RoseBeanFactory.getBean("cmTreeWalker") />
            <cfset treeNodesQ = cmTreeNodeManager.getcmTreeNodesQuery(TreeID=treeID,orderby="ParentID, sortOrder asc") />
            <cfset siteNavTree.setTree(treeNodesQ.qList) />
            <cfset applicationFacade.set("siteNavTree",duplicate(siteNavTree))/>       
            <cfset tarray = json.encode(siteNavTree.getTree(),"array") />
           
        <cfset tarray = replaceNoCase(tarray,"treenodeid","id","all")/>
        <cfset tarray = replaceNoCase(tarray,"nodename","text","all")/>
        <cfset tarray = replace(tarray,"PARENTID","parentid","all")/>
        <cfset tarray = replace(tarray,"CHILDREN","children","all")/>
        <cfset tarray = replace(tarray,"SORTORDER","sortorder","all")/>
        <cfset tarray = replace(tarray,"OBJECTID","objectid","all")/>
        <cfset tarray = replace(tarray,"TYPEID","typeid","all")/>
        <cfset tarray = replace(tarray,"TREEID","treeid","all")/>
        <cfset tarray = replace(tarray,"TYPENAME","typename","all")/>
        <cfset tarray = replaceNoCase(tarray,"typename"":""webPage""","typename"":""webPage"",""icon"":""/scripts/ext/resources/images/default/tree/leaf.gif""","all")/>
        <cfset tarray = replaceNoCase(tarray,"typename"":""menuSection""","typename"":""menuSection"",""icon"":""/scripts/ext/resources/images/default/tree/folder.gif""","all")/>
     
        <cfreturn tarray />
    </cffunction>

Again for brevity, I will skip the first section of the code and focus on the code that retrieves the tree in question and formats the nodes. 

This line gets a cmTreeWalker object from ColdSpring:
<cfset siteNavTree= application.RoseBeanFactory.getBean("cmTreeWalker") />

cmTreeWalker is a mini-TreeWalker object designed to hold the contents of a tree. For now, let's just understand that it contains the definition of the Tree and its nodes and their relationships.

This line gets the nodes of the tree out of the database:
<cfset treeNodesQ = cmTreeNodeManager.getcmTreeNodesQuery(TreeID=treeID,orderby="ParentID, sortOrder asc") />

This line feeds the nodes into the cmTreeWalker object for organization into a tree:
<cfset siteNavTree.setTree(treeNodesQ.qList) />

This line drops the tree into the application scope, we're not concerned with it at this time:
<cfset applicationFacade.set("siteNavTree",duplicate(siteNavTree))/>   

This line encodes the array of nodes into JSON format, which is what the TreeLoader object is expecting to receive:   
<cfset tarray = json.encode(siteNavTree.getTree(),"array") />

The last section performs string manipulation on the encoded array. Without going into the reason for the string manipulation, I really like being able to perform string manipulation on JSON-encoded data like arrays.

But what do we actually get back? Take a look at the return from the browser. I have added line breaks for readability, but you get the picture.

[{"typeid":24,"id":1,"sortorder":1,"objectid":1,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"home","treeid":2,"parentid":"","children":
[{"typeid":30,"id":8,"sortorder":1,"objectid":13,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Services","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":22,"sortorder":0,"objectid":28,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Overview","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":21,"sortorder":1,"objectid":27,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Staffing Solutions","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":24,"sortorder":2,"objectid":30,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Project Based Consulting","treeid":2,"parentid":8,"children":
[]},{"typeid":24,"id":34,"sortorder":4,"objectid":41,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Strategic IT Consulting","treeid":2,"parentid":8,"children":
[]}]},{"typeid":30,"id":26,"sortorder":2,"objectid":32,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Case Studies","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":31,"sortorder":1,"objectid":37,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Business Credit Services","treeid":2,"parentid":26,"children":
[]},{"typeid":24,"id":32,"sortorder":2,"objectid":38,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Delaney Educational Enterprises","treeid":2,"parentid":26,"children":
[]},{"typeid":24,"id":33,"sortorder":3,"objectid":39,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Invitrogen","treeid":2,"parentid":26,"children":
[]}]},{"typeid":30,"id":11,"sortorder":3,"objectid":17,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Company","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":19,"sortorder":1,"objectid":25,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Who We Are","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":17,"sortorder":2,"objectid":23,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Management","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":12,"sortorder":3,"objectid":18,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Mission Statement","treeid":2,"parentid":11,"children":
[]},{"typeid":24,"id":18,"sortorder":4,"objectid":24,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Safe Harbor Policy","treeid":2,"parentid":11,"children":
[]}]},{"typeid":30,"id":27,"sortorder":5,"objectid":33,"typename":"menuSection","icon":"/scripts/ext/resources/images/default/tree/folder.gif"
,"text":"Community","treeid":2,"parentid":1,"children":
[{"typeid":24,"id":28,"sortorder":1,"objectid":34,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"San Diego Adobe Developers User Group ","treeid":2,"parentid":27,"children":
[]},{"typeid":24,"id":30,"sortorder":2,"objectid":36,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"March Mingle","treeid":2,"parentid":27,"children":
[]}]},{"typeid":24,"id":20,"sortorder":6,"objectid":26,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Blog","treeid":2,"parentid":1,"children":
[]},{"typeid":24,"id":6,"sortorder":8,"objectid":11,"typename":"webPage","icon":"/scripts/ext/resources/images/default/tree/leaf.gif"
,"text":"Contact Us","treeid":2,"parentid":1,"children":
[]}]}]

The function creates a JSON-encoded representation of the tree nodes that Ext can digest.

Next time I will go into some detail about the format of the array of nodes in ColdFusion to give you an idea of how to format the data on the server before sending it off to Ext.

ColdFusion and ExtJS - Ext.Ajax.request

Lately we have been working with the ExtJS 2.2 library on a large ColdFusion-based project, and I wanted to share some of the techniques we are using to implement AJAX-based functionality.

At its core, AJAX functionality is all about building a richer experience on the client, and that richer experience generally starts with refreshing data on the screen without refreshing the entire page from the server. With Ext, we are often accomplishing this task by sending a request via Ext.Ajax.request() and replacing div contents with HTML fragments returned from the server. Developers can use Ext to draw more compelling UI elements programmatically, but we have found that in many cases, simply using Ext as a transport for HTML fragments serves our needs just as well. 

Just as often, we combine the use of HTML fragments with returned JSON-encoded objects to build an interface that is both easy to construct from a developer/designer perspective and has the ability to handle complex data.

In this code sample, the Javascript function getAccountSummary() calls a like-named method on the server, which checks the user's session information and returns both a form filled out with the user's details and several JSON-encoded objects. In particular, the addresses object contains an array of addresses that the user can edit in the account form, using more JS magic.

     function getAccountSummary(divID){
        setCursor('wait');
        Ext.Ajax.request({
         
          url: '/services/IdentityProxy.cfc?wsdl&method=getAccountSummary',
          
          success: function(response,options){
              var main = document.getElementById("main");
              var ret = Ext.util.JSON.decode(response.responseText);
              user = ret.USER;
              userphone = ret.PHONE;
              userfax = ret.FAX;                           
            addresses = ret.ADDRESSES;
                     
            main.innerHTML=ret.body;       
            showDiv("cancelpwdEdit","hide");     
            showDiv("pwdconfirm","hide");     
            setCursor('default');

          },
          failure: function (response,options){
              alert(response.responseText);
              setCursor('default');
              },

           params:{returnFormat:'JSON'}
        });
               
    } 

As you can see in the function, the returned data is set into variables that are scoped to the browser, meaning they are accessible to other Javascript functions. The div called "main" is then populated with the account form using main.innerHTML = ret.body. 

This technique suffers from a potential security risk (doesn't everything?)- cross-site scripting attacks. There are ways to guard against XSS attacks, more on that another time. 

Bolt - The new CF IDE

If you haven't heard yet, at MAX2008 Adobe introduced a new Eclipse-based IDE for ColdFusion. Code-named Bolt, the IDE is scheduled for release in 2009. Some of the features demonstrated at the conference look very compelling and should close the gap between CF and other scripting languages in terms of having a full-featured IDE. I am still a fan of CFEclipse, but it is good to see Adobe taking an interest in delivering a commercial IDE for CF developers. I have a feeling that the commercial success of CF8 has encouraged Adobe to invest strategically in the CF platform and toolset. I can't wait to get my hands on it!

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.