Tuesday, January 5, 2010

cfmap in ColdFusion 9 with Google Maps API - pretty cool!

Raymond Camden demonstrates how to easily map data onto websites with ColdFusion 9 map tags. Learn how to use addresses, longitude and latitude coordinates, and other data to build interactive maps.

'ColdFusion as a Service' in upcoming ColdFusion 9 - Wicked!

Extract from the article 'ColdFusion Functionality Exposed as Services' by Ben Forta:

Have you ever stopped to think about just how much functionality is baked into ColdFusion? We use to work with databases, allowing for highly flexible and dynamic SQL as well as query caching and more, and this is powered by a sophisticated internal engine. We use tags like and expect charts to be generated and displayed, without ever really paying attention to the fact that an entire Java-based charting engine is built in and actually doing the heavy lifting. The same is true for , , , support for SOAP and Web Services, XMPP and JMS integration, and so much more. While most of us focus on CFML the language, the truth is that the bulk of ColdFusion, the majority of what gets installed, is not the language but the extensive array of integrated services, services that are exposed to ColdFusion via CFML tags and functions.

But what if these services could be accessed outside of ColdFusion? If a PHP developer in the next cube over needed to merge PDF files, why couldn't he invoke the PDF manipulation services in ColdFusion? If a .NET developer needed to access Microsoft Exchange, why couldn't she use the brilliant Exchange tags in ColdFusion (rather than having to write lots and lots of .NET code, and I do mean lots and lots)? What about the Java developer who needs to easily manipulate spreadsheet files without tinkering with low level libraries?

And while we're at it, what about the Flex developer who needs to generate an e-mail message? Flex (well, Flash) has no built-in SMTP libraries, and so Flex developers who need to programmatically generate e-mail messages do so by writing code on the server. For ColdFusion developers this means creating a ColdFusion Component which accepts data from a Flex application (likely via an AMF call) and then passes that same data to a tag. In other words, code is being written on the server just to be able to pass data from Flex on the client to the tag. So why couldn't a Flex developer just invoke directly, passing it name=value pairs so it can generate an e-mail?

Well, with the upcoming ColdFusion 9, the answer to all of these questions is yes. These are all doable! In ColdFusion 9 we're exposing lots of those integrated ColdFusion services via AMF (Flash Remoting) and SOAP (Web Services). The PHP, .NET, and Java developers can invoke ColdFusion built-in Web Services, pass in data, and get back results. And the Flex developer can include a ColdFusion SWC file exposing ActionScript classes and MXML tags via simple abstracted AMF calls. Simply include the SWC in your Flash Builder project, define the ColdFusion name space like this:

<mx:Application xmlns:cf="coldfusion.service.mxml.*">

and you'll have access to CFML tags within your Flex project. For example, to send an e-mail you could use the following:

<cf:Mail id="cfMail" to="{to.text}" from="{from.text}"
subject="{subject.text}" subject="{subject.text}"
content="{body.text}" type="html" />

The above code creates an instance of the Mail object and names it "cfMail", and sets to, from, subject, etc., with the values of other Flex objects. To actually send the mail all you'd need is to invoke the following (possibly when a Send button is clicked):


cfMail.execute();

There is much more to this "ColdFusion as a Service" functionality, including lots more services exposed, and a sophisticated security model. But the bottom line is that ColdFusion is now poised to become even more valuable to Flex and AIR developers, and now even of value to developers using other platforms and languages.

There's Nothing Wrong with Paid Software!

This article is partly in response to Bernard Hickey's latest blog post, and partly just me venting about the general perception around open source software.

I think this idea that free software is by definition good software and anyone trying to make money from applications they have spent time and resources to build are evil is just ridiculous. Everything is a business. If you're not making money, it's hard to survive and keep going.

Wordpress itself, which was used as an example in the post, has a revenue model. Like most open source applications, they offer the software for free, and then provide paid support, mostly for enterprise scale deployments. And on their hosted blog service, they make money out of premium features and any ads placed on the bloggers' sites. So it's definitely not free. And who exactly ends up with any profits made? It's definitely not the good hearted developer working at nights to contribute to the community but always remaining nameless.

As for the suggestion of government always using open source software, I find such thinking disturbing. Let's not forget that there can be a large compromise on quality and functionality between paid products and 'free' products. When it comes to such things as databases, where data integrity is of the highest priority, especially for governments, isn't it wise to invest in an Enterprise scale trusted and tried application like Oracle or Microsoft's SQL Server, rather than going for an open source technology which doesn't guarantee support and doesn't take accountability for major bugs? And security is another point. With open source software, the core application code is available for all too see.

We already know how difficult it is to have an online business driven solely by Ads. Look at the struggle online news sites are currently facing. Even with their vast scale of advertising inventory, there are now serious suggestions of going back to the paid subscription model for their content. If we applied the open source principle to them, wouldn't it mean they should make their content free? How is this sustainable?

Google has also contributed heavily to the idea that software should be free and open-source, like they did with Android and Chrome. But let's not forget the real reason why they are doing this. It's to spread their tentacles and get as many people using their products as possible so that they can ultimately make larger profits out of their core business, which is selling Ads. If they truly believed in making everything 'free', we would have their search algorithm by now.

I strongly feel that we need to change our mindset and move away from the idea that open source is always good. There is nothing wrong with a person/company coming up with a good idea, executing that idea into a software application, and selling that to others who have good use for it.

I am not suggesting that open source software is bad, infact I think it has had a profound positive effect on paving the web, and will continue to have a major role and influence on how the internet is moulded. I myself have benefitted from and contributed to open source projects.

What I am saying however, is that there is absolutely nothing wrong with paid software, and it shouldn't be implied otherwise.

CFDocument Woes & Using Java iText Instead

I was developing a client invoice generation functionality, and one of the requirements was having different headers and footers in the same invoice document. Example, the first page header would contain the address details while the rest of the pages wouldn't.

I quickly found out that this cannot be achieved using cfdocument. The header and footer margins cannot be dynamically changed, although the content of the header and footer sections can be changed.

Example code snippet:

<cfoutput>
<cfset margintop = 2>
<cfset marginbottom = 1.5>

<cfdocument format="PDF">
     <cfdocumentsection margintop="#margintop#" marginbottom="#marginbottom#">
           <cfdocumentitem type="header" evalAtPrint="true">
                 <cfif cfdocument.CurrentPageNumber EQ 1>
                       <cfset margintop = 2>
                       This is the header that goes on my first page only.
                       It's a long header so the margin needs to be set accordingly otherwise it all gets squashed!
                       Header Header Header Header Header Header Header Header Header Header Header Header
                       Header Header Header Header Header Header Header Header Header Header Header Header
                       Header Header Header Header Header Header Header Header Header Header Header Header
                 <cfelse>
                       <cfset margintop = 0.5>
                       This is the header that goes on my other pages and is not as long hence requires a shorter margin to prevent unnecessary white spacing.
                 </cfif>
           </cfdocumentitem>
           <cfdocumentitem type="footer" evalAtPrint="true">
                 <cfif cfdocument.CurrentPageNumber EQ 1>
                       <cfset marginbottom = 1.5>
                       This is the footer that goes on my first page only.
                       It's a long footer so the margin needs to be set accordingly otherwise it all gets squashed!
                       Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer
                 <cfelse>
                       <cfset marginbottom = 0.5>
                       This is the footer that goes on my other pages and is not as long hence requires a shorter margin to prevent unnecessary white spacing.
                 </cfif>
           </cfdocumentitem>
           <cfloop index="i" from="1" to="1000">
dummy text
   </cfloop>
     </cfdocumentsection>
</cfdocument>
</cfoutput>


So I decided to write to Adam Lehman at Adobe to see if I was misisng something.
Response was that yes, CF can't handle that, and it will be logged as a bug.
And no feedback after that so not sure if it has been fixed for CF9.

But one of the awesomest things about ColdFusion is that it's Java!
You have the full power of Java at your disposal. So pretty much, there's nothing that you can't do ;-)

Hence to get around my problem, I used the iText jar that comes with CF8.
Specificlly, the PDFPageEventHelper class.
We cannot use this class directly from CF, but it's relatively simple to write a java class that extends this class, and overwrite the functions you need to. In my case, the onEndPage method.

The onEndPage method is called just before a new page is started and initialized. This is the best method to add some headers and footers.

It worked like a charm!

A simple example as follows:

First create the Java class file, export the jar and place it in the CF lib folder. (You will need to restart CF for it to pick up the jar.)


package MyiTextUtil; 

import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.ExceptionConverter;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfWriter;


public class MyPDFPageEvent extends PdfPageEventHelper {
public PdfWriter writer;
public Document document;
private Phrase header;
private Phrase footer;

public static void main(String[] args) {    
} 

public MyPDFPageEvent(PdfWriter writer, Document document) {  
this.writer = writer;
this.document = document;
    } 

public void onOpenDocument(PdfWriter writer, Document document)
{
}

public void onEndPage(PdfWriter writer, Document document) {
       try {
         PdfContentByte cb = writer.getDirectContent();  
            if (writer.getPageNumber() == 1) {
             header = new Phrase("This is the header that goes on my first page only. " +
               "It is a long header so the margin needs to be set accordingly otherwise it all gets squashed! " +
               "Header Header Header Header Header Header Header Header Header Header Header Header" +
               "Header Header Header Header Header Header Header Header Header Header Header Header" +
               "Header Header Header Header Header Header Header Header Header Header Header Header");
             footer = new Phrase("This is the footer that goes on my first page only." +
               "It is a long footer so the margin needs to be set accordingly otherwise it all gets squashed!" +
               "Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer Footer");
            }
            else {
             header = new Phrase("This is the header that goes on my other pages and is not as long hence requires a shorter margin to prevent unnecessary white spacing.");
             footer = new Phrase("This is the footer that goes on my other pages and is not as long hence requires a shorter margin to prevent unnecessary white spacing");
            }
            
            ColumnText ct = new ColumnText(cb);
            ct.setSimpleColumn(document.leftMargin(), document.getPageSize().height(), document.getPageSize().width() - document.rightMargin(), document.bottomMargin(), 15, Element.ALIGN_CENTER);
            ct.addText(header);
            ct.go();
            ct.setSimpleColumn(document.leftMargin(), 0, document.getPageSize().width() - document.rightMargin(), document.topMargin(), 15, Element.ALIGN_CENTER);
            ct.addText(footer);
            ct.go();

            document.setMargins(20, 20, 50, 50);
        }
       catch (Exception e) {
        throw new ExceptionConverter(e);
       }
    }
}
</code>

Now from your CF code, use the iText library as follows:

<code>
<cfscript>

sPdfFileName = "test.pdf";
sPdfFilePath = ExpandPath(sPdfFileName);

oPageSize = CreateObject("java", "com.lowagie.text.PageSize");
oDocument = CreateObject("java", "com.lowagie.text.Document").init(oPageSize.A4, javacast("float",20), javacast("float",20), javacast("float",100), javacast("float",100));
oOutStream = CreateObject("java", "java.io.FileOutputStream").init(sPdfFilePath);
oWriter = CreateObject("java", "com.lowagie.text.pdf.PdfWriter").getInstance(oDocument, oOutStream);

oDocument.open();

oPDFPageEvent = CreateObject("java", "MyiTextUtil.MyPDFPageEvent");
oPDFPageEvent.init(oWriter, oDocument);
oWriter.setPageEvent(oPDFPageEvent);

sDummyText = "";
for (i=0; i<1000; i++){
sDummyText = sDummyText & " dummy text";
}

oParagraph = CreateObject("java", "com.lowagie.text.Paragraph");
oParagraph.init(sDummyText);
oDocument.add(oParagraph);

oDocument.close();
oOutStream.close();

</cfscript>

<cfcontent file="#sPdfFilePath#" deletefile="yes" type="application/pdf">


And you're done!

The great thing about using the iText library is that you have so much more control on the layout of the PDF. More here: iText Docs

__type__ Struct VS CreateObject()

How come I didn't know about this? Apparently I wasn't alone. There are quite a few people on the web who were surprised themselves to learn of this, and probably a lot out there who haven't yet heard about it, hence hoping this blog helps.

Thanks to one of my colleagues, I have learnt the __type__ Struct method of creating objects sent from ColdFusion to Flex, and there is absolutely no question that this method provides far better performance than using CreateObject().To demonstrate the performance difference, I will loop over a query to create objects for each row and append it to an Array that would be sent back to Flex, once using CreateObject(), and then using __type__.

Using the table ART in the CFARTGALLERY database (example DB comes with CF), I will create an 'object' for it (ColdFusion component with properties getters and setters).


<cfcomponent output="false" alias="ArtBean">

<cfproperty name="artid" type="numeric" default="0" hint="Primary key of the ART table.">
<cfproperty name="artistid" type="numeric" default="0" hint="FK to ARTIST table">
<cfproperty name="artname" type="string" default="" hint="Art name">
<cfproperty name="description" type="string" default="" hint="Art description">
<cfproperty name="issold" type="boolean" default="0" hint="Has this Art been sold?">
<cfproperty name="largeimage" type="string" default="" hint="Large Image filename">
<cfproperty name="mediaid" type="numeric" default="0" hint="FK to MEDIA table">
<cfproperty name="price" type="numeric" default="0" hint="Art price">

<cfset variables.artid = 0>
<cfset variables.artistid = 0>
<cfset variables.artname = "">
<cfset variables.description = "">
<cfset variables.issold = false>
<cfset variables.largeimage = "">
<cfset variables.mediaid = 0>
<cfset variables.price = 0>

<cffunction name="init" output="false" returntype="ARTBean">
<cfreturn this>
</cffunction>

<cffunction name="getartid" output="false" access="public" returntype="any">
<cfreturn variables.artid>
</cffunction>

<cffunction name="setartid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.artid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getartistid" output="false" access="public" returntype="any">
<cfreturn variables.artistid>
</cffunction>

<cffunction name="setartistid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.artistid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getartname" output="false" access="public" returntype="any">
<cfreturn variables.artname>
</cffunction>

<cffunction name="setartname" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.artname = arguments.val>
</cffunction>

<cffunction name="getdescription" output="false" access="public" returntype="any">
<cfreturn variables.description>
</cffunction>

<cffunction name="setdescription" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.description = arguments.val>
</cffunction>

<cffunction name="getissold" output="false" access="public" returntype="any">
<cfreturn variables.issold>
</cffunction>

<cffunction name="setissold" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsBoolean(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.issold = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid boolean"/>
</cfif>
</cffunction>

<cffunction name="getlargeimage" output="false" access="public" returntype="any">
<cfreturn variables.largeimage>
</cffunction>

<cffunction name="setlargeimage" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.largeimage = arguments.val>
</cffunction>

<cffunction name="getmediaid" output="false" access="public" returntype="any">
<cfreturn variables.mediaid>
</cffunction>

<cffunction name="setmediaid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.mediaid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getprice" output="false" access="public" returntype="any">
<cfreturn variables.price>
</cffunction>

<cffunction name="setprice" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.price = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

</cfcomponent>

The ART table is queried to get all the rows in it. I then loop over the resultant query to create the above objects, one loop using the CreateObject() method and the other using the __type__ Struct method. I spit out the times it takes for both to show the difference. So my test code would look something like this:


<cfquery name="qArt" datasource="cfartgallery">
select * from art
</cfquery>

<cfset aArt1 = ArrayNew(1)>
<cfset aArt2 = ArrayNew(1)>

<!--- query loop using CreateObject() --->
<cftimer label="query loop using CreateObject()" type="debug">
<cfloop query="qArt">
<cfset oArt = createObject("component", "ArtBean").init()>
<cfset oArt.setArtID(artid)>
<cfset oArt.setArtistID(artistid)>
<cfset oArt.setArtName(artname)>
<cfset oArt.setDescription(description)>
<cfset oArt.setIsSold(issold)>
<cfset oArt.setLargeImage(largeimage)>
<cfset oArt.setMediaID(mediaid)>
<cfset oArt.setPrice(price)>
<cfset ArrayAppend(aArt1, oArt)>
</cfloop>
</cftimer>

<!--- <cfdump var="#aArt1#"> --->

<!--- query loop using  '__type__' --->
<cftimer label="query loop using  '__type__'" type="debug">
<cfloop query="qArt">
<cfset oArt = StructNew()>
<cfset oArt['__type__'] = "ArtBean">
<cfset oArt['artid'] = artid>
<cfset oArt['artistid'] = artistid>
<cfset oArt['artname'] = artname>
<cfset oArt['description'] = description>
<cfset oArt['issold'] = issold>
<cfset oArt['largeimage'] = largeimage>
<cfset oArt['mediaid'] = mediaid>
<cfset oArt['price'] = price>
<cfset ArrayAppend(aArt2, oArt)>
</cfloop>
</cftimer>

<!--- <cfdump var="#aArt2#"> --->

The CFTIMER times for 10 test cases were as below:

1. [164ms] query loop using CreateObject()
[1ms] query loop using '__type__'

2. [152ms] query loop using CreateObject()
[1ms] query loop using '__type__'

3. [178ms] query loop using CreateObject()
[1ms] query loop using '__type__'

4. [169ms] query loop using CreateObject()
[1ms] query loop using '__type__'

5. [187ms] query loop using CreateObject()
[1ms] query loop using '__type__'

6. [141ms] query loop using CreateObject()
[1ms] query loop using '__type__'

7. [154ms] query loop using CreateObject()
[1ms] query loop using '__type__'

8. [171ms] query loop using CreateObject()
[1ms] query loop using '__type__'

9. [171ms] query loop using CreateObject()
[2ms] query loop using '__type__'

10.[158ms] query loop using CreateObject()
[1ms] query loop using '__type__'

This shows the massive performance saving when using __type__ over CreateObject(). In large datasets, the performance gain is absolutely phenomenal!

Also, this is a fully documented feature: http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=UseFlexDataService_05.html

It only works via LiveCycle so CF8 comes with this feature.

Advanced Session Management

Ever wanted to manage all the available user sessions in your application?

A use case can be that for applications requiring registration and license, a registered user can only be logged in and using the application once at any given time (for a one user license). If another login with the same credentials occur, you would want to allow the latest login, and kill the old session.

This is what the fabulous World of WarCraft application does. Now this is trickier than it sounds. It is much easier to not allow any new logins once a login has occurred, but this can raise many usability concerns, such as if someone was logged in at work, and is now at home and wants to continue, it would be quite frustrating to not be able to log back in and the last thing you would want to do is drive all the way back to work to log yourself out!

So instead, the latest login should be allowed and any previous existing sessions for the same credentials should be destroyed.

How can we achieve this in ColdFusion?

First, get the SessionTracker object which has some very useful methods as displayed below:

<cfset oSessionTracker = createObject("java","coldfusion.runtime.SessionTracker")>

Dump of SessionTracker Object



Then, using the 'getSessionCollection' method of the SessionTracker object, retrieve a collection of SessionScope objects which have the methods indicated below. The SessionScope object is the one you're after.

<!--- returns a collection of SessionScope Objects --->
<cfset stAllSessions = oSessionTracker.getSessionCollection(Application.ApplicationName)>

Dump of SessionScope Object



Now that you have that collection, you can loop through and individually interrogate each SessionScope object and kill the ones you don't like....Muahahahahahahaha!

So in the simple example below, I am saying that if the sessionid is of a certain value, kill that session.

<cfloop collection="#stAllSessions#" item="key">
<cfset stUserSession = StructFind(stAllSessions, key)>
<!--- if some condition, kill this session --->
<cfif stUserSession.sessionid EQ 1>
<cfset stUserSession.setMaxInactiveInterval(0)>
</cfif>
</cfloop>

There are many different ways a session can be destroyed but I have found that setting the inactive interval timeout to 0 works quite well as done above.

Ofcourse you would need to get a bit smart here and assign a unique field to the user's session upon login, so say Session.loginname = 'some unique identifier for the user'. And then, in the loop above, you would be checking to see if any previous session with that identifier exists.

Such advanced session management capabilities combined with ColdFusion's out of the box session management (http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=sharedVars_10.html) makes it a very powerful tool.

CFTHREAD Rocks!

With the release of ColdFusion 8, several new features were added, one of them being cfthread. The online documentation says: "The cfthread tag enables you to create threads, independent streams of code execution, in your ColdFusion application. You use this tag to run or end a thread, temporarily stop thread execution, or join together multiple threads."

The obvious advantage of using threads in an application would be performance enhancement. For me, when working with Flex and ColdFusion, ColdFusion is the layer that would query the database and create objects out of the retrieved data and send it back to Flex. To create these objects, CF would have to loop through the query and generate them but as you can imagine, with a large dataset, this loop can take some time.So to demonstrate how well cfthread works, I will use the scenario above. That is, I will loop over a query to create objects for each row and append it to an Array (that is usually sent back to Flex).

Using the table ART in the CFARTGALLERY database (example DB comes with CF), I will create an object for it (ColdFusion component with properties getters and setters).

<cfcomponent output="false" alias="ArtBean">

<cfproperty name="artid" type="numeric" default="0" hint="Primary key of the ART table.">
<cfproperty name="artistid" type="numeric" default="0" hint="FK to ARTIST table">
<cfproperty name="artname" type="string" default="" hint="Art name">
<cfproperty name="description" type="string" default="" hint="Art description">
<cfproperty name="issold" type="boolean" default="0" hint="Has this Art been sold?">
<cfproperty name="largeimage" type="string" default="" hint="Large Image filename">
<cfproperty name="mediaid" type="numeric" default="0" hint="FK to MEDIA table">
<cfproperty name="price" type="numeric" default="0" hint="Art price">

<cfset variables.artid = 0>
<cfset variables.artistid = 0>
<cfset variables.artname = "">
<cfset variables.description = "">
<cfset variables.issold = false>
<cfset variables.largeimage = "">
<cfset variables.mediaid = 0>
<cfset variables.price = 0>

<cffunction name="init" output="false" returntype="ARTBean">
<cfreturn this>
</cffunction>

<cffunction name="getartid" output="false" access="public" returntype="any">
<cfreturn variables.artid>
</cffunction>

<cffunction name="setartid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.artid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getartistid" output="false" access="public" returntype="any">
<cfreturn variables.artistid>
</cffunction>

<cffunction name="setartistid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.artistid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getartname" output="false" access="public" returntype="any">
<cfreturn variables.artname>
</cffunction>

<cffunction name="setartname" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.artname = arguments.val>
</cffunction>

<cffunction name="getdescription" output="false" access="public" returntype="any">
<cfreturn variables.description>
</cffunction>

<cffunction name="setdescription" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.description = arguments.val>
</cffunction>

<cffunction name="getissold" output="false" access="public" returntype="any">
<cfreturn variables.issold>
</cffunction>

<cffunction name="setissold" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsBoolean(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.issold = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid boolean"/>
</cfif>
</cffunction>

<cffunction name="getlargeimage" output="false" access="public" returntype="any">
<cfreturn variables.largeimage>
</cffunction>

<cffunction name="setlargeimage" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfset variables.largeimage = arguments.val>
</cffunction>

<cffunction name="getmediaid" output="false" access="public" returntype="any">
<cfreturn variables.mediaid>
</cffunction>

<cffunction name="setmediaid" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.mediaid = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

<cffunction name="getprice" output="false" access="public" returntype="any">
<cfreturn variables.price>
</cffunction>

<cffunction name="setprice" output="false" access="public" returntype="void">
<cfargument name="val" required="true">
<cfif (IsNumeric(arguments.val)) OR (arguments.val EQ "")>
<cfset variables.price = arguments.val>
<cfelse>
<cfthrow message="'#arguments.val#' is not a valid numeric"/>
</cfif>
</cffunction>

</cfcomponent>


The ART table is queried to get all the rows in it. I then loop over the resultant query to create the above objects, one loop the normal way, the other one using threads. I spit out the times it takes for both to show the difference. So my test code would look something like this:

<cfquery name="qArt" datasource="cfartgallery">
select * from art
</cfquery>

<cfset aArt1 = ArrayNew(1)>
<cfset aArt2 = ArrayNew(1)>
<cfset aArt3 = ArrayNew(1)>

<!--- normal query loop --->
<cftimer label="normal query loop time" type="debug">
<cfloop query="qArt">
<cfset oArt = createObject("component", "ARTBean").init()>
<cfset oArt.setArtID(artid)>
<cfset oArt.setArtistID(artistid)>
<cfset oArt.setArtName(artname)>
<cfset oArt.setDescription(description)>
<cfset oArt.setIsSold(issold)>
<cfset oArt.setLargeImage(largeimage)>
<cfset oArt.setMediaID(mediaid)>
<cfset oArt.setPrice(price)>
<cfset ArrayAppend(aArt1, oArt)>
</cfloop>
</cftimer>


<!--- thread query loop --->
<cfset iHalfWay = Round(qArt.recordcount / 2)>
<cftimer label="thread query loop time" type="debug">
<cfthread action="run" name="ArtThread1" priority="normal">
<cfloop query="qArt" startrow="1" endrow="#iHalfWay#">
<cfset oArt = createObject("component", "ARTBean").init()>
<cfset oArt.setArtID(artid)>
<cfset oArt.setArtistID(artistid)>
<cfset oArt.setArtName(artname)>
<cfset oArt.setDescription(description)>
<cfset oArt.setIsSold(issold)>
<cfset oArt.setLargeImage(largeimage)>
<cfset oArt.setMediaID(mediaid)>
<cfset oArt.setPrice(price)>
<cfset ArrayAppend(aArt2, oArt)>
</cfloop>
</cfthread>

<cfthread action="run" name="ArtThread2" priority="normal">

<cfloop query="qArt" startrow="#iHalfWay+1#" endrow="#qArt.recordcount#">
<cfset oArt = createObject("component", "ARTBean").init()>
<cfset oArt.setArtID(artid)>
<cfset oArt.setArtistID(artistid)>
<cfset oArt.setArtName(artname)>
<cfset oArt.setDescription(description)>
<cfset oArt.setIsSold(issold)>
<cfset oArt.setLargeImage(largeimage)>
<cfset oArt.setMediaID(mediaid)>
<cfset oArt.setPrice(price)>
<cfset ArrayAppend(aArt3, oArt)>
</cfloop>
</cfthread>

<cfthread action="join" name="ArtThread1, ArtThread2" timeout="1000" />

<cfloop array="#aArt3#" index="i">
<cfset ArrayAppend(aArt2,i)>
</cfloop>

</cftimer>


The CFTIMER times for 10 test cases were as below:

1.
[192ms] normal query loop time
[26ms] thread query loop time

2.
[189ms] normal query loop time
[21ms] thread query loop time

3.
[186ms] normal query loop time
[23ms] thread query loop time

4.
[254ms] normal query loop time
[19ms] thread query loop time

5.
[189ms] normal query loop time
[20ms] thread query loop time

6.
[188ms] normal query loop time
[19ms] thread query loop time

7.
[192ms] normal query loop time
[46ms] thread query loop time

8.
[194ms] normal query loop time
[17ms] thread query loop time

9.
[138ms] normal query loop time
[16ms] thread query loop time

10.
[142ms] normal query loop time
[37ms] thread query loop time


As it can be seen from the above times, cfthread is significantly faster, but ofcourse there's always a wrong and right way to use it. You wouldn't want to overuse and generate too many threads which then queue up and can also have context switching issues, and you have to be careful using it with shared scopes, such as Application, to ensure deadlocking scenarios are avoided.

There's a great tutorial on cfthread on Ben Nadel's blog here: http://www.bennadel.com/blog/743-Learning-ColdFusion-8-CFThread-Part-I-Data-Exchange.htm


Martin Jones asked: "Great article - my only question is: "How do you know that your query isn't being cached and giving you quicker response times in the 2nd run (with cfthread).
What are the times if you reverse the order ie: run the cfthread query first and then the normal query last."

My answer to that would be that the cftimer tags are around the looping only hence query generation time should have no impact whatsoever on the looping times. The query is generated once and then the two loops are done over the same query. But just to confirm, I reversed the order of the loops and here's the result:

1.
[34ms] thread query loop time
[219ms] normal query loop time

2.
[54ms] thread query loop time
[161ms] normal query loop time

3.
[44ms] thread query loop time
[171ms] normal query loop time

4.
[42ms] thread query loop time
[197ms] normal query loop time

5.
[27ms] thread query loop time
[180ms] normal query loop time

The results are pretty much the same as above showing cfthread is significantly faster.

Dynamic Datasource Management

Dynamic management of datasources (create/update/delete) can be achieved using the CF8 admin API, specifically the Administrator and Datasource components.

CFIDE.ADMINAPI.Administrator
CFIDE.ADMINAPI.Datasource

On local developer installations, the path is usually: C:\ColdFusion8\wwwroot\CFIDE\adminapi\datasource.cfc and it can be viewed through the CFC explorer which details all the functions and their definitions.

http://localhost:8500/CFIDE/componentutils/cfcexplorer.cfc?METHOD=getcfcinhtml&
PATH=/CFIDE/adminapi/datasource.cfc&NAME=CFIDE.adminapi.datasource

You cannot use the datasource API until you have successfully logged in, which can be done using the Administrator API.So just to keep things secure, I usually create a user account through CFAdmin and gave it limited API access only to the DataSources service.
After successfully logging in, I then used the Datasource API to create the required datasource.

<!--- log in CFAdmin User --->
<cfset administratorService = CreateObject("component","CFIDE.adminapi.administrator")>
<cfset bIsLoggedIn = administratorService.Login(CFUser.Password,CFUser.Username)>

<cfif bIsLoggedIn>
<cfset datasourceService = createObject("component", "CFIDE.adminapi.datasource")>
<!--- check to see if datasource with same name doesn't already exist --->
<cfif datasourceService.VerifyDSN(datasource_name) eq false>
<cfset stArgs = StructNew()>
<cfset stArgs.name = datasource_name>
<cfset stArgs.database = database_name>
<cfset stArgs.host = database_host>
<cfset stArgs.port = database_port>
<cfset stArgs.driver = datasource_driver>
<cfset stArgs.selectmethod = datasource_selectmethod>
<cfset stArgs.username = database_username>
<cfset stArgs.password = database_password>

<!--- create datasource - this one's for MSSQL DB --->
<cfset datasourceService.SetMSSQL(argumentCollection=stArgs)>
</cfif>
</cfif>

If you need to delete the datasource after using it, for example, if a datasource is created when a user logs in and is only required to exist for the length of the user's session, then the deleteDatasource() method can be used when the user's session ends.


I also needed to verify how many datasources can be created on a ColdFusion server. I couldn't find any documentation which provided a limit to the number of datasources, so I used a loop and managed to create 1000 datasources with no problem. But each datasource has a memory footprint so there must be a max number recommended, after which it could start having an adverse effect on performance.

So I created 100 datasources in a loop and measured the memory difference before and after and repeated this process a number of times, and it seems that a datasource occupies between 30 - 40 KB.

Note: In ColdFusion 8, Using the Administrator API to create or modify a data source would set the Validation Query value to "0", which would disable connection pooling for the data source. This issue has been fixed in ColdFusion 8 Update 1.