What I Learned Today - CFEXECUTE is not Unix-friendly

Update: Based on a conversation with Paul Kukiel, this issue does not seem to exist in Ubuntu Linux. Definitely confirmed in Solaris, unsure about other Unix flavors.

For the first time ever, I needed to use the oft-maligned CFEXECUTE tag on a project. I was developing an intranet application to track the health of about 30 Unix-based servers in real time, using a variety of metrics. The application also resides on a Unix server.

One of the metrics was to ping the servers to make sure they are up. That required the usage of CFEXECUTE to run a shell script I had written to do the task.

The first issue I found was that on Unix-based systems (OS X and Unix), the tag was not returning the ping results to me, even though I was using the variable attribute. The variable was empty. After hours of trying, I ended up writing the contents of the ping to a file to read. That led me to my second issue.

My first choice was to write to the virtual filesystem. The thought was that since I would be doing 30 pings every five minutes it would be best to not have to make all those writes to the disk. The problem that brought about was that if the ping took too long to finish, CFEXECUTE wasn't waiting around for the script to finish before moving on. If the server was down it could take up to 20 seconds for the ping failure to come back. Forcing the application to wait that long every time around wasn't the answer, so I had to step back and look at the solution again.

While focusing on other aspects of the application, I ran across the third and largest issue with CFEXECUTE - it's a resource killer of alarming degree on Unix. CFEXECUTE uses the fork() method to run processes. I have found the following warning (in various forms) on multiple blogs and message boards regarding this.

"Because of the way the fork() method was implemented on unix systems (which is what cfexecute calls under the hood to run the external process) it duplicates the amount of memory of the calling process to run the external process, which is crazy. So, for example, if your JRun process is currently taking up 1 GB of RAM (a pretty common JVM size), then cfexecute will run the external process using 1 GB of RAM, which will very quickly throw an Out of Memory exception."

I ran into this problem on our QA server, which only has 384MB of swap space free and only 4GB on onboard memory. Our JVM is set for 1GB minimum so there was no way CFEXECUTE would find the space needed to make the duplicate environment.

Once I learned about this, I quickly decided to pull CFEXECUTE altogether. Instead I have a cron job that runs once a minute and outputs the ping results for each server to a separate file on the hard disk, overwriting existing files to limit space usage. The application will read these files in, and they're real-time enough in that 3-4 updates will have been made to the ping statuses by the time the next background polling is done by the application to read in the files.

I now completely understand why so many hosting services disable this tag. In addition to the security risks inherent to it, the toll it can take on your server (unless it's loaded up big time in memory) just is not worth it when there are other options to solving the problem.

What I Learned Today - createObject() has one big downside

Recently I talked about conforming to using init() methods in my components. I had planned to use createObject().init() to make the call in one nice quick line.

There's just one problem, createObject() won't work for me in my preferred site structuring.

I like to house all my application-level and session-level variables in separate include files in an includes/ directory beneath the web root. In Application.cfc, I call to those files in onApplicationStart() and onSessionStart(). It's a nice way for me to compartmentalize code and keep Application.cfc from getting too messy. I've done this for years now in dozens of sites.

I keep all my components in a components directory on the same level as the includes directory. I initialize the components in the application scope because they change so little (only when a code release is done) that it makes sense to keep them there to limit overhead by only creating the object one time.

Until recently I would use CFOBJECT to create the component variable. I would call it like this:

view plain print about
1<cfobject name="application.mySite.cfc.myComponent" component="components.myComponent" />

Never a problem. I try to change it to:

view plain print about
1<cfset application.mySite.cfc.myComponent = createObject("component","components.myComponent").init() />

and it blows up with the error "Could not find the ColdFusion component or interface components.myComponent".

For an hour I could not figure out why until digging enough showed me one huge difference between createObject() and CFOBJECT. While CFOBJECT is smart enough to know that the dot notation of the directory structure begins at the site root, createObject() only looks from the location of the template the call resides in. In this case it's the includes directory.

For kicks I added the createObject() call directly into the onApplicationStart() function in Application.cfc. It worked fine, confirming my lesson of the night that it has no capability to traverse up a directory structure, only down.

So it looks like I'll be sticking with CFOBJECT after all, at least until Adobe decides to give createObject() a little more flexibility. What stinks is that I'll have to make separate calls to the init() functions to get them run. Kinda defeats the purpose of them a little bit I think.

What I Learned Today - Tesing IE7 - IE9 in one place

This probably isn't news to many of you, but I am one of those that actively avoids Internet Explorer as a browser. In fact it's fair to say that 99% of my usage of IE comes either from testing for cross-browser support or connecting remotely to the servers at work (considering that the education field is so Mac-friendly, it's somewhat ironic that ED itself has practically no support for Macs).

That being said, I am a very vocal advocate of cross-browser support for all major browsers. Based on the logs for the sites I host and at work, that means supporting IE7 and IE8, which account for about 60% of the traffic on average combined. Thankfully IE6 is down to less than 5% overall usage, so it's off my radar unless explicitly asked for (thank God!). But IE9 has started to catch on since it's release, with about the same usage rate as IE6 but trending upwards.

Knowing that I will be needing to support IE9 going forward for my own work (and guessing that a mandate to support it is not too far off at my day job), I recently upgraded my VM from WinXP to Win7, and installed IE9 on it. I then was going to install the IE Collections software package to get all the IE browsers I needed, as I did on WinXP, when I learned that this was no longer needed for me.

IE9 has a Developer Toolbar like Firefox and Chrome, accessed by the F12 key. On it is the option to choose what version of IE to render the page in. You can choose IE7, IE8, or IE9. No more third party software for this task!

As much as it pains me to say this, Microsoft actually got something right with this one.

For those not on IE9, I would recommend giving the IE Collections site a look. It's a hack for them to be able to have multiple versions of IE on one machine, but it always worked for me.

What I Learned Today - using CFCATCH as a struct

I've been working on a error handling application and ran into an issue. I could not pass along the CFCATCH scope as a struct as an argument. I could do it for any other scope. Testing revealed a weird oddity where ColdFusion does not see the CFCATCH scope as a struct.

Try this code

view plain print about
1<cftry>
2    <cfthrow type="Application" message="This is a test error">
3    <cfcatch type="any">
4        <cfoutput>#isStruct(cfcatch)#</cfoutput>
5        <cfset variables.copyOfCactch = duplicate(cfcatch) />
6        <cfdump expand="true" label="cfcatch" var="#cfcatch#" />
7        <cfoutput>#isStruct(variables.copyOfCactch)#</cfoutput>
8        <cfdump expand="true" label="variables.copyOfCactch" var="#variables.copyOfCactch#" />
9    </cfcatch>
10</cftry>

and this is what it returns:

NO YES

(plus the related dumps)

What I Learned Today - Mac Mail font problem and a hack fix

I use Mail.app for much of my emailing (when not using my iPhone), and I've noticed that no matter what I do, my emails always end up in Times New Roman font on any other email client but Mail.app, no matter what I do. This includes using basic fonts that all computers have, including Arial, Verdana, and Courier New. After searching around for this for a bit, I learned that this behavior has been consistent in Mail.app for years and no fix by Apple.

I ran across a forum that had some ideas for a solution. They are all hacks, but I found one that I liked the best. It seems that, for whatever reason, this font issue does not affect the signatures. So I edited my signatures to use the same font that I want to send emails with, and added a line at the top that says "<begin message here>". As long as I start my message on that line, and not above it, the fonts work as expected in all mail reasders that I could test, Outlook and Thunderbird included.

I sure wish Apple would fix this, as multiple bugs have been submitted on it. But at least I can use Mail.app for corporate emails and not have it look like crap any more.

Comments are moderated solely for spam-prevention purposes.

What I Learned Today - Data loss sucks.

Over the long weekend, Viviotech had a major outage due to a failed upgrade. I am one of the servers that had failures on the Viviotech backup process as well, which means my most recent server backup was mid-June. Not a very good weekend for me (but better than the one the Viviotech guys had to endure).

I do weekly backups every Monday of my databases, and all my web files are saved in a local CVS repository that I have backed up on my own. Unfortunately, the outage happened before I could do my latest rounds of backup, so my database backups are more than one week old. 

I've lost two blog posts that were done last week, and I know of at least two comments that got lost as well. My apologies to those people.

Comments are moderated for spam-prevention purposes.

What I Learned Today - MAMP Pro and SSL

A couple of projects I work in involve using SSL in some places. For the longest time I could never get SSL to work right with MAMP PRO (I have v1.84). Today I ran across a blog post that finally got it working for me.

http://www.rockettheme.com/blog/coding/310-getting-ssl-to-work-with-mamp-pro

What I Learned Today - Have your ColdFusion on OS X and your Verity too.

This is a followup to my previous post on this topic, and a solution found.

Thanks to comments here and from House of Fusion, I learned that Verity does not work on Macintosh OS X. An alternate solution must be found. Thanks in part to Dave Watts of Figleaf, I found it.

Setting Up Verity

From Dave's suggestion, I installed CF9 on an OS that has Verity - Windows in my case.I have an instance of WinXP SP3 running via VMware Fusion. I have the network adapter set up for NAT, so it gets it's own internal IP address.

I installed it as a standalone edition with the local web server on port 8500. After installation I ran the web-based config to get it all up and running. At that point, I disabled all the CF services except for the ColdFusion Search Services, which is set to Automatic. I went into Windows Firewall and opened up the three ports Verity uses.

I went back into my OS X CF admin, and changed the Verity K2 host to the IP address of my Windows instance. Tried to connect to the service and.... nothing. At this point it was 1700 on Friday so I shut down and took the weekend off from this.

I returned to this task this morning with a fresh head, and found some documentation on CF's instance of Verity. It only allows connections from one machine, and by default it is set up to be 127.0.0.1. A search found this address in two XML files in my C:\ColdFusion9\verity\Data\host directory, as well as the C:\ColdFusion9\verity\verity-install.cfg file. I went into all three files, and changed the IP address and mask from 127.0.0.1 and 255.255.255.255 to my OS X internal IP address and a mask of 255.255.255.0. I restarted the ColdFusion Search service on Windows. I cannot tell you for certain which change was the difference maker, but when I refreshed the ColdFusion Collections link on my OS X CF9 admin server, it was connected to the Windows verity!

Fun with soft links

The next task was to set up the collection that I was going to use. The function to do the indexing is set up to create the collection first if it does not exist.

As a reminder, my QA and production environment are Unix boxes, which does have Verity support from CF. On it, CF is set up in the /opt/coldfusion directory. Therefore the verity collections are in /opt/coldfusion/verity/collections/. I have been very strict in making sure that my local development environment on OS X mimics the directory structures used on QA and production, using soft links where needed. So on OS X, I created /opt, and inside /opt created a soft link for "coldfusion" to link to my /Applications/ColdFusion9/ directory.

The path to the verity folder is /opt/coldfusion/verity/collections/. My first instict was that CF would create the directories as needed. I ran my indexing template, and got back the following:

The collection was reindexed. 0 records have been inserted. 0 records have been updated.


Not what I was expecting. I looked in my /Applications/ColdFusion9 directory, and saw no new "verity" directory created. So where was it? I looked on my WinXP instance in C:\ColdFusion9\verity\collections, and saw no new collections created there. I was stumped until I looked at the C:\ drive, and found an "opt" directory there. Expanding it, I found C:\opt\coldfusion\verity\collections\ and my collection inside there!

Now knowing what had happened, I needed to link OS X to that verity folder. I shared the C:\opt\coldfusion\verity folder on Windows, and mounted it in OS X. I then went into /Applications/ColdFusion9, and made a soft link to the mount point for the verity shared folder at /Volumes/verity. So now /opt/coldfusion/verity links properly to the C:\opt\coldfusion\verity on Windows. I think I've got it all, so I run the index again.

The collection was reindexed. 0 records have been inserted. 0 records have been updated.


Huh?

The Final Hurdle - proper location of the documents

Looking in the CF9 admin, I read this:

Because you have a remote K2 server configured, you must ensure that the documents to be indexed are accessible from the ColdFusion server machine as well as the computer on which the K2 search services run.

And that's when I realized that the documents to be indexed couldn't be on OS X, they had to be on windows. I created an uploads directory on Windows, and shared it. I mounted the share on OS X. Then, again mimicing our QA and production structure, I soft linked the root uploads directory to the mount point (/Volumes/uploads).

A final running of the indexing returned this:

The collection was reindexed. 106 records have been inserted. 0 records have been updated.


Wahoo! And a test of the search returned results as expected!

Comments are moderated solely for spam-prevention purposes only.

What I Learned Today - I blame Ben Nadel for this post...

Okay, the title is meant to be facetious, and attention grabbing. Just so we're all on the same page. But it's not untrue.

Through Ben Nadel's fantastic blog, I learned the practice of creating and scoping a LOCAL struct in a CF function (CFMX7 and CF8), and assigning any other local variable created in the function into that struct. That way I did not need to manage multiple variables, trying to make sure they were all locally scoped. It seemed more efficient.

Well, with the migration of my VPS to CF9 (and the impending upgrade of my work's servers to CF9), I found blog posts talking about how others that had used this practice were finding oddities with their code upon upgrading. It seems that this practice was somehow colliding with the new protected "local" scope within functions in CF9. The new scope allows coders to do exactly what I was doing, only without having to scope that local struct first.

In trying to figure out what changes I needed to make, I went asking around trying to find out if this meant that we no longer had to scope local variables anymore inside of functions, if unscoped variables would automatically get put in the new local scope. To hear and read the documentation, you certainly could come to that conclusion.

Thanks to Ray Camden, I have been straightened out. It turns out this is not the case, and I am not the first to ask him this. He was kind enough to make this topic an entry in his just as fantastic blog.

Bottom line, you don't have to scope things put in the local scope, but you must explicity call the variable in the local scope (local.myVariable). And all other variables must still be scoped for protection.

What I Learned Today - Flex Training Follow-up

I wanted to pass along my experiences from my Flex Training for ColdFusion Developers that I took yesterday in Chicago.
 
This was a course from Adobe that was along the lines of an "intro to..." kind of course regarding Flex. Flex is the bridge that connects Flash to a backend (in this case CF9 but it can connect to any backend). It was aimed at people with my skill sets, but it was clear within the first hour that if you had some Flash and Actionscript background you were going to get a lot more out of it. I had neither, but I was able to follow along well enough.
 
Our trainer was Jeff Tapper, a senior Flex developer who has worked on some high profile projects, most recently working with Major League Baseball on their MLB.tv video feeds (I hope I got that right). He took a good amount of time in trying to make sure that we did not fall behind much.
 
On the Flex/Flash end they laid out some interesting (albeit very basic) usages. A lot of using Flash as a front end for basic forms and having it call CF9 to do some processing and returning data to the front end without a page reload. They did show that Flex can do form validation natively, and since it is not tied to a feature that can be disabled like JavaScript, it lessens the need for writing both front end and back end code for data input validation to a nice-to-have instead of a must-have. With a standard HTML front-end, if a user disabled JavaScript they could still submit the form and therefore having server side backend validation is an absolute must. Here, they either have Flash enabled and get it all, or they don't and don't see the form. There is no in-between.
 
Using Flash Builder you could set the Flash compiler to make the flash application accessible with a single checkbox in the options. I would be curious to see what our 508 team would think after taking a look at some of the demos we did, to determine if it truly is 508-compatible or not.
 
I did like the Flash Builder tool a lot, it has a nice GUI interface for creating the Flash front-ends and allowed you to manage some of the actions for the form objects as well. If I was going to build a Flex application, I would choose to use Flash Builder (which is built on the Eclipse IDE). But it costs at least $250 for the standard copy, and can get up to $700 for the Premium (which includes network monitoring to test the efficiency of your Flex application).
 
What I would need though is a lot more training on the Flex language itself. This I can self-teach given time I believe, but there's a lot still to be learned on that front.
 
What I ended up walking away most impressed with though was the new CF9 server and the ColdFusion Builder application. CF Builder is currently in Beta 2 phase, and is also an Eclipse-based IDE. It did have a couple of bugs but nothing major, and I really liked how it could be integrated with your CF server, either a local one or a remote one, to give you some control over the server, and through RDS get a lot of quick access to the database.
 
As far as CF9, the single biggest thing I walked away from this was the introduction I got to CF9's ORM (Object Relational Mapping) feature. After a first introduction to it, and watching a couple of other presentations on it since, this could be a tool that changes how we interact with databases. It enables you to write fairly generic code, and use functions instead of queries for accessing the database (and supports all parts of a CRUD interaction). Because of this, you could write CF code that would work the same with a database no matter what server it's on - be it MSSQL, MySQL, or Oracle (to name the three most common ones). It also takes a lot less code to interact with the database, which will save us time in coding. ColdFusion Builder has extensions that can speed this up even more, by connecting to your database via RDS, reading the configuration of a table, and auto-generating the components needed for you. CF9 also natively handles a lot of this communication as well. If you couldn't tell, I'm pretty excited about trying this out.

More Entries