July 31, 2002
The need for speed
The mailing lists seem alive with questions about which is faster: CF5 or CFMX? There are also questions about whether particular coding constructs are faster than others. The simple answer is, well, try it for yourself -getTickCount() is your friend here. The recently published Macromedia Performance Brief (see my 7.24.2002 entry) shows that users should generally expect CFMX to be 10% faster than CF5 on Windows (and much faster than CF5 on Linux). That's in general of course and your mileage may vary. Some users are complaining that their applications are slower on CFMX. On cf-talk in particular, we are starting to see code fragments posted that actually time different features. <cfwddx> seems to be slower on CFMX, as does the string insert() function. This is probably not too surprising since Java is typically slower at processing text than C/C++ in general.
A related question on that list was whether it is faster to convert a list to an array and loop through it, rather than use
listGetAt(). Since that is effectively a string processing function, it shouldn't really surprise you that using listToArray() and looping through the array is much faster (about 20x faster according to initial tests).
July 30, 2002
isStruct(arguments)?
Remember some time ago when I warned that there are certain things that you can't call duplicate() on? Here's another one: duplicate(arguments) will fail with a null pointer error. isStruct(arguments) returns true and you can structCopy() it just fine. The workaround is to loop over the items in arguments and copy them individually into a new struct (which you can then duplicate).
July 26, 2002
Oops!
Apologies to anyone who tried to download files from my ColdFusion Resources page in the last two hours. That'll teach me to properly QA 'small' changes! Thanx to 'Chris' who was the first to report the 404 Error.BACFUG
Last night I was part of a panel-format meeting of the Bay Area ColdFusion User Group. Nathan Dinterfass showed how easy it is to manipulate XML in ColdFusion MX, Matt Liotta showed how you can easily wrap Java functionality in ColdFusion Components, Brian Ghidinelli showed a simple Rich Internet Application to maintain a little database and I showed how you can do useful stuff with CFC metadata. The panel example code will be uploaded to the BACFUG Code Sharing page in due course. In the meantime, you can download my example code, along with my previous BACFUG presentation, from my ColdFusion Resources page.
July 24, 2002
Performance!
Macromedia has now announced official performance data for ColdFusion MX. You can read the whitepaper, ColdFusion MX Performance Brief (PDF, 363 KB), on the Macromedia - ColdFusion MX : White Papers, Data Sheets and Briefs web page.The summary says "See how ColdFusion MX outperforms ColdFusion 5 in both performance and scalability. Testing was done on Windows, Linux, and Solaris."
July 23, 2002
Refactoring
Following on from a discussion about process and 'good' code on BACFUG, Matt Liotta posted a link to an article he wrote for DevX about refactoring an application to make it more maintainable and more adaptable. His story makes good reading as it explains both the why and the how behind his architectural process.July 22, 2002
Chinese locales
ColdFusion MX supports three Chinese locales that are not covered in the main documentation so you might have missed them. They are: Chinese (China); Chinese (Hong Kong); Chinese (Taiwan). These correspond to the underlying locales zh_CN, zh_HK and zh_TW respectively. How can you find out what a ColdFusion MX locale maps to in general? You can access the Java code underneath as this code fragment shows:<cfset pc = getPageContext() />
<cfset setLocale("Chinese (China)") />
<cfoutput>
Current Locale: #getLocale()# --- Current date: #LSDateFormat(now(), "ddd-mmm-yyyy")#
<!--- look at locale in the java pageContext object --->
<br>here's the language for the ServletResponse object:
<strong>#pc.getResponse().getLocale().getLanguage().toString()#</strong>
<br>here's the country for the ServletResponse object:
<strong>#pc.getResponse().getLocale().getCountry().toString()#</strong>
</cfoutput>
In this example we examine Chinese (China) but you can try it for other values of locale. Credit to my colleague Seth Hodgson for this example.
Service-specific Error 2
A few people may have seen this when trying to start ColdFusion MX. Everything was working fine, you changed some Java and JVM Settings and things were still fine. Then you restarted... and ColdFusion MX wouldn't come back up and all you got was that cryptic message. This can happen if you put something illegal into the JVM Arguments field in the administrator. That field gets written out to the configuration file and next time you try to start ColdFusion MX, it fails because the java driver can't make sense of these new options.
To fix the problem, you'll need to edit {CFMX}/runtime/bin/jvm.config and correct the java.args line back to something like this:
# Arguments to VM
java.args=-DJINTEGRA_NATIVE_MODE -DJINTEGRA_PREFETCH_ENUMS -Xmx512m
i.e., remove the extraneous arguments you added in the JVM Arguments field. Note that on your platform there may be quite a few more, valid, arguments than shown above - see below for what's in my jvm.config. Then you should be able to restart ColdFusion MX.
# Arguments to VM
java.args=-DJINTEGRA_NATIVE_MODE -Xmx512m \
-Xbootclasspath/a:{application.home}/lib/webchartsJava2D.jar \
-Djava.awt.graphicsenv=com.gp.java2d.ExGraphicsEnvironment \
-DJINTEGRA_PREFETCH_ENUMS
Note: I have broken this onto multiple lines for readability and added \ at the end of each line to indicate continuation!
July 19, 2002
More on Fusebox
My comments about Jeff & Nat's book have a page of their own. If you don't agree with me, let me know!Error Reporting
Someone recently observed on BACFUG that the default error 'page' no longer contains a link to email the site administrator. Earlier versions of ColdFusion allowed you to specify an "administrator email" and that was shown in the default error messages. In ColdFusion MX, you can control the format of the error information by editing{CFMX}/wwwroot/WEB-INF/exception/detail.cfm. You could change it to email all the details directly to your webmaster address if you wanted. All errors are reported through this file via specific exception handlers in the coldfusion/runtime subfolder. Even cfabort output can be controlled via AbortMessageException.cfm (if you specify showError=, otherwise it uses AbortException.cfm - the only exception handler file that does not invoke detail.cfm).
This allows you to create custom detail pages for any exception you throw via
cfthrow. If you specify type="foo", the CustomException.cfm handler will be invoked which just delegates to detail.cfm as shipped. However, if you change the custom handler to this:
<!--- error handler for custom user exceptions ---> <cfimport taglib="../../" prefix="ex"> <cftry> <cfinclude template="/WEB-INF/exception/#replace(error.type,'.','/')#.cfm"> <cfcatch type="any"> <!--- no user-supplier handler ---> <ex:detail error=#error#/> </cfcatch> <cftry>then you can put
foo.cfm in the exception folder and it will be executed instead of the default detail.cfm handler! Note the call to replace() which maps hierarchical exceptions like "foo.bar" to directory paths like foo/bar.
I am indebted to two members of the ColdFusion Product Team: Spike Washburn for the insight into how this actually works and Edwin Smith for implemented such flexible machinery in the first place! You guys rock!
Performance?
Macromedia has not said much about the performance of ColdFusion MX compared to earlier releases. Some people find this particularly strange after the trumpeting about CF5 being so much faster than CF4.5. Back in May, eWeek did some tests (on the Release Candidate) and published their results as part of an article about application server maturity. Their tests indicate that CFMX scales better than CF5. Since those tests (on the Release Candidate), further performance enhancements were made to the product so the shipping release should be even better.If I hear of more performance results being published, I'll provide links to them here.
blogs & links
I've added Pete Freitag's blog to the links on the left (and he has an RSS feed) and here's an interesting site: cfbughunt.org. I'm not entirely sure of the value of this site at the moment - I hope that it will become genuinely valuable in terms of troubleshooting problems with ColdFusion MX rather than just a platform for people to gripe about the new release. Some of the issues reported so far are not really bugs - however, given the dramatic nature of the release, there's going to be a lot of interesting features and changes that many people will insist are bugs.
July 17, 2002
More on /
A colleague of mine, David Keith, bemoaned the fact that Dreamweaver MX generates<cfset > when you click the set option in the CFML Basic tag insert menu. He wanted it to generate the closing / so that the result would be XHTML-compliant. He soon figured out how: in the Dreamweaver MX install directory, edit Configuration/Objects/CFML Basic/Cfset.htm and change the code that gets inserted to have ' />' instead of just '>' - the extra space improves readability. Thanx David!
<cf_custom_tag/>
A question that crops up fairly regularly is "Why does ColdFusion execute my tag twice?". If you have a custom tag foo.cfm and call it as <cf_foo>, it will be called just once, as most people expect. If you call it as <cf_foo/>, it will be called twice - notice the /. This is a surprise to many people but if you think it through, it becomes clear what is going on... Consider the following code:
<cf_foo>stuff</cf_foo>
Here we are calling foo.cfm with obvious start and end tags. Not surprisingly, foo.cfm is executed twice: once for the start tag and once for the end tag. How can we tell these situations apart? We can test thisTag.executionMode. It will equal "start" for the first execution and "end" for the second execution. This allows complex custom tags to perform some setup at the start and then deal with the tag body (stuff in the example above, referenced as thisTag.generatedContent) during the "end" execution. We would write:
<!--- foo.cfm --->
<cfif thisTag.executionMode is "start">
<!--- perform setup --->
<cfelse>
<!--- handle thisTag.generatedContent --->
<cfset thisTag.generatedContent = "<b>" & thisTag.generatedContent & "</b>"/>
</cfif>
All this does is to embolden the body of the tag so no setup is needed in this case.
Now let's go back to our original example: <cf_foo/>. The / indicates a 'closing tag' so this is identical to <cf_foo></cf_foo>. It should be clear from this that foo.cfm will be executed twice. What should you do about this? Well, you have a choice... you can either code your custom tag so that it will not allow an end tag (by testing thisTag.hasEndTag and throwing an exception if that is true) or you can have your tag only execute at the start (when thisTag.executionMode is "start"). I prefer the second option because it then allows you to specify the closing / and have XHTML-compliant CFML source code, without affecting the behavior of your program. Of course, you still have to be careful when using custom tags that you didn't write - but if a third-party custom tag isn't robust enough to handle this, I recommend you complain to the tag's author!
July 15, 2002
Read!
The long weekend away allowed me to read Jeff & Nat's new book on Fusebox from cover to cover. It's a good book: it's very well-written and I now have a much better understanding of why Fusebox works for people. I might even try rewriting my personal site using PHP Fusebox, just for the experience. I have a lot of notes about the book that I'll be writing up over the next few days, where I will go into more detail about what I really think of Fusebox, FLiP - the Fusebox Lifecycle Process - and Fusedocs. For now, I'd say that whether or not you're already a Fusebox fan, this book should be on your reading list.What's in a name?
Another caveat about names... A while back I commented on naming web service components and highlighted some restrictions brought about by the move to Java. Another change between CF5 and CFMX concerns the built-in scopes. They now behave as structures and they are effectively reserved words. That means that if you had an unqualified variable whose name matched one of the built-in scopes (e.g., url, file, request), then your code will not work correctly in CFMX.
<cfset url = "..."> <!--- url is reserved in CFMX --->
Even if it worked in earlier versions of ColdFusion, it is still bad practice since it creates clashes between names - a very bad practice, banned by most coding guidelines!
July 10, 2002
.what?
Want ColdFusion MX to serve up .foo files? Matthew Haughey asked how to do just that on BACFUG recently and later posted the answer to his own question: "Editing the web.xml file located at {CFMX}/wwwroot/WEB-INF, by adding these lines did the trick:"
<servlet-mapping>
<servlet-name>CfmServlet</servlet-name>
<url-pattern>*.foo</url-pattern>
</servlet-mapping<<servlet-mapping>
<servlet-name>CfmServlet</servlet-name>
<url-pattern>*.foo/*</url-pattern>
</servlet-mapping>
Another servlet-mapping that may be useful to you is FileServlet which will cause ColdFusion MX to render files that match the url-pattern "as-is", i.e., without interpretation.
Reading...
My copy of "Fusebox: Developing ColdFusion Applications" by Jeff Peters and Nat Papovich turned up today. I'm off to Chicago tomorrow (for a long weekend for a friend's wedding) so it'll make good reading material for the flights and any 'off' hours I may have. I'll write up my thoughts next week I expect.July 09, 2002
RSS
In response to various requests, I've added an RSS 0.92 feed to this blog. See the left margin. Thanx to Geoff Bowers for finding this gem and to VoidStar for providing the "rssifier". I've also added a link to Geoff's wonderful "full as a goog" aggregation of blogs.
July 07, 2002
Hidden metadata
When you're writing components, it can be useful to walk through the metadata of the component's parents or through all of the properties - declared and inherited - in the component. It would be useful to be able to get all of this metadata in a single call, rather than having to navigate through arrays and structs to find what you're looking for. If you are willing to cfinclude a file from inside the administrator directory tree, you can do this.
/CFIDE/componentutils/_component_utils.cfm contains two useful methods: getAncestors() and getProperties(). You can use these as follows:
<cfset metadata = getMetadata(this)/>
<cfset ancestors = getAncestors(metadata)/>
<cfset properties = getProperties(ancestors)/>
The results are an array of metadata (ancestors) and a struct of metadata (properties). The properties struct has every single declared and inherited cfproperty as a key. The keys' values are also structs, containing two items: implementedin and metadata. The former is the name of the component in which the property is declared. The latter is the metadata for the property itself.
Once again, note that cfproperty is not related to variables declared in the public this scope or in the unnamed private scope of a component. However, cfproperty can be very useful when writing components that map database or other such 'external' structures.
July 04, 2002
var is your friend!
Back on 6.19.2002 I was talking about local variables. I want to emphasize how important these are when you're writing functions, especially inside ColdFusion Components. If you work with complex data structures - including XML - you will probably find yourself writing functions to manipulate parts of those structures. Those functions will probably call other functions. As soon as you start doing that, you need to make sure that any variables used in one function do not clash with variables used in the other functions. That's why var should become second-nature. If you declare your function inside <cfscript> then begin with a block of 'declarations': for every single variable you use in that function, initialize it at the head of the function with var variableName = someValue;. If you're declaring functions using the new cffunction tag - which is recommended since you can specify argument and return type validation! - then you should begin with a block of cfset tags that 'declare' each variable you intend to use:
<cfset var variableName = someValue/>. This will ensure that your functions will not overwrite each other's variables.
July 02, 2002
Quiet?
If I'm a little quiet this week, it's because I have my head down in a ColdFusion MX project. In between all my architectural tasks, it's nice to roll up my sleeves and actually use ColdFusion in anger! The more I use it, the more I like it. I'm doing some complex database work as well as making heavy usage of the component metadata machinery. I like how easy it is to work with databases in ColdFusion and relatively straightforward to map CFC instances to / from a relational database by using additional metadata in the component to direct the mapping. I'm using the cfproperty tag to specify which data members in the component should be persisted and how they should be mapped to the database. More on this in due course.