Writing Portable Code

Charlie Arehart recently blogged about how to programmatically reload the WSDL proxy for a WebService defined in the ColdFusion Administrator (http://carehart.org/blog/client/index.cfm/2006/12/12/refreshing_web_services_programmatically). My first thought after reading the blog entry title was "Why would anyone want to do that?"... and then it occured to me that perhaps I should blog my reasons for responding as such. It's no slight on Charlie, a good friend and smart guy... and for the record, I found the entry interesting and do recommend reading it to anyone interested in programattically refreshing CF Administrator WebService settings.

If you were to ask me what I consider my strongest skill as a ColdFusion Developer, I would say that I like to think it's my ability to write code that is extremely portable and easy to read/maintain. Regarding portability: it's rare to have to change more than two or three variables (at most) in my applications in order to make them run. It doesn't matter if you copy the code from one machine to another or from one directory structure to another - just change 2 or 3 global variable values and it'll run. That, after all, is what portability is all about.

What that means is that my applications tend to rely as little as possible on settings in the ColdFusion Administrator, as these settings make an application less portable. No CF mappings, Custom Tag Paths, registered Web Services, etc. are required for the app to function. Yes, that includes trying to avoid the necessity for pre-existing verity collections and datasources, although obviously you do have to draw the line somewhere - if a DSN must exist, it must.

I'm not saying that it's the end of the world if you use these administrator features, but I personally recommend getting in the habit of trying to reduce the need for them. You'll generally find that testing is easier, deploying your applications to new environments is smoother, and running multiple copies of your app on the same instance is less painful. If you are going to write an application that requires settings in the administrator, by all means document this well. Prior to CFMX 7, avoiding relying on CF Settings was more important than it is now, since we have J2EE packaging and deployment, but it's still very important (not only for portability but also for readability).

Here are a few ways you can begin to make your code more portable:

  • Use CFModule (with 'template' attribute) or CFImport (with relative path) for calling custom tags (CFIMPORT is preferred)
  • Represent your UI with custom tags
  • Make all forms post to #cgi.script_name#
  • Use relative paths for cfincludes, CSS, Javascript, images, and hyperlinks
  • store paths in application variables and use base paths, with other paths extending those values
  • get objects from an object factory and/or resource pool implementation
  • whenever possible - try not to rely on mappings, custom tag paths, or other path or mapping variables in general

Comments
dc's Gravatar cfimport needs to be a hard coded string, that to me means that your app is not very portable without using mappings in the administrator (or symbolic links in Unix... but you really don't want to do that)

You then say do not use mappings. With proper planning, mappings are not an issue and can be set from a config variable meaning meaning very easy portability.

Assets for your HTML layer... again, easy to use a configuration variable and use a virtual web path. Using relative paths for your assets is a pain in the backside unless your are using the front controller patter and can ensure that the path relativeness will stay the same. Yes there are functions out there that calculate relativeness from one file location to another.. why bother in my opinion, just use the web server mapping rather than using up processor time doing unnecessary file system reads and calculations.

your reasons are honorable, just not convinced that the 'don't do that' tone is entirely correct.
# Posted By dc | 12/13/06 7:06 AM
Charlie Arehart's Gravatar Simon, I appreciate your pointing folks to the entry, but I have to take exception with the broad conclusion you seem to have drawn and the inference that you're making. What has what I written about got to do with "relying on settings in the ColdFusion Administrator"? I think you've misunderstood something.

What I discussed is something that will apply to anyone who invokes web services from CFML, period. If it read somehow that it applies only to those who "add web services to the Admin console" or "renames them in the Admin console", that would be an unfortunate conclusion and I'd ask you or anyone else to tell me how I might reword to correct that misconception.

The fact is that if you invoke a web service from CFML, a proxy/stub will be created. The problem is that sometimes the web service API on the other end changes, and this cached proxy/stub becomes out of synch. The solution then is to flush it.

I simply started with pointing out that the common approach to flushing it is to do so from the Admin console. Naturally, some will want to do that programmatically so as to avoid having to use the UI to get the task done. I make no comment on whether they should do so, or if it's a problem if they have to do it often. I was simply showing folks how to do it if they need to.

Really, there's nothing about this that has to do with an optional "use of administrator features". It just is what it is: a solution to a possible problem, and a poorly documented one at that.

Still, I don't want you to see the blog entry as all bad. You make other useful points that do support your general assertion of keeping code portable. I'd support that 100%. I just think it's unfortunate that my entry was used as a proofcase of bad practice. I hope I've persuaded you and others that it just isn't the case.

BTW: I see you're still using the default captcha settings in BlogCFC. I blogged about how to change that (http://carehart.org/blog/client/index.cfm/2006/8/1...) and then later did another where I simply give you the captcha.xml with the handful of changes needed. You can just drop that in and ?reinit your blog and voila, simpler captcha. See http://carehart.org/blog/client/index.cfm/2006/10/... Hope that's helpful to you--and, more important, to your readers/commenters.
# Posted By Charlie Arehart | 12/13/06 10:29 AM
Simon Horwith's Gravatar Charlie - your entry just inspired me to blog about the imnportance of not using application specific CF Administrator settings - that's all. It's one of those things I've meant to blog about for ages but never got around to. As for the captcha thing - because I'm generally far too busy to post, let alone muck around with aestheitics (I was running a 3 year old version of the code and DB until 2 months ago), I haven't messed with it. I didn't know there was anything wrong with how it works now. That said, I'll try and do something about it ASAP.

As for the other comments from DC:
I WISH that cfimport could take a dynamic path like cfmodule does - and until cfimport I only used cfmodule. CFIMPORT is a pre-compiler directive, so it has to be hard-coded... but it's a relative path, which means that if you copy your directory structure from one place to another, it will just work. There is no such thing as 'proper planning' that will not require you to still create the mapping if you rely on them. After 11+ years of doing this, trust me - I make very proper use of storing paths in variables... but if you require a mapping to be there, it does make your code less portable. I'm not sure what you're refering to with regards to HTML assets, unless you don't agree with my recomendation to use Custom Tags for the presentation tier.
# Posted By Simon Horwith | 12/13/06 11:42 AM
Simon Horwith's Gravatar Charlie - hope I did make it clear in my last comment - your entry wasn't intended to be a proofcase - it just reminded me that I'd meant to write about this sometime ago and still hadn't. I've also been meaning to write about list datatypes and several other things I think folks will find iteresting... but I've been VERY BUSY at work.
# Posted By Simon Horwith | 12/13/06 11:45 AM
Charlie Arehart's Gravatar Simon, I appreciate the reply, but I don't sense that you're completely backing off of the inference to be drawn from your note. You started it out saying, 'My first thought after reading the blog entry title was "Why would anyone want to do that?"... and then it occurred to me that perhaps I should blog my reasons for responding as such...'

So can you confirm for the record (lest anyone think we're mincing words here) that you see now that there's nothing about what I posted that is really related to the rest of your point? I mean, sure, I can understand one thought leading to another, but it does seem that when you wrote the post you were clearly regarding what I wrote as something to spark your argument (otherwise, why mention it at all?)

I realize you're busy, so I'm sorry to draw this out. I just know that you get plenty of readership and I'd just like to clarify things for anyone reading this. Thanks in advance.
# Posted By Charlie Arehart | 12/13/06 3:31 PM
Peter Bell's Gravatar If you've got it - use it :->

I'm going to argue there is nothing wrong with the judicious use of CF Mappings, DSNs and just about anything else that for your application solves a problem more elegantly than another approach (although I agree you don't need to be hard coding things like server names and directory paths which can be calculated once in a config script and stored in application scope).

The trick is to write a simple build engine (or to use something like Ant) to auto-deploy. I have a simple system in my old procedural application generator where I can select a server from a drop down list and an application from a list and "deploy", "snapshot", "restore from", etc. It takes care of mappings, DSNs, creating data tables in selected databases if required, etc.
# Posted By Peter Bell | 12/13/06 4:13 PM
Charlie Arehart's Gravatar Oh, an as for the captcha, I appreciate your willingness to consider doing something. Let me reply both to "what's wrong with it as is?" and "not having time".

The problem is that the default setting (of Lyla captcha, which BlogCFC uses) creates a very complex captcha (shown to anyone who tries to create a comment here). In earlier blog posts (linked to there) I explain why that's generally just not needed. We're not guarding Fort Knox in our blogs. We just want to keep out spam pests. My assertion is that we don't need a "double keyed deadbolt lock" so much as just a "screen door"--the simpler captcha, and so I originally showed how to change the file to effect the simplification.

But others also balked at taking the time to change it, so I created the later blog post (the one listed above) that simply offers the updated lyla captch.xml file. You can just drop it into your current BlogCFC deployment. No need to twiddle or even think about anything. Reinit the blog (with the ?reinit query string) and in less than half a minute you can have a greatly simplified captcha.

Again, other entries pointed to from there explain why this is not a bad thing. And I show there what it will look like if you do it, so you can know what you're getting into. This is simply about doing something to benefit your readers every time they comment, at a one time cost of a few seconds to implement the updated file.

Indeed, later versions of Ray's BlogCFC incorporate this new simpler captcha, so many have come to see the benefit. Hope that's helpful.
# Posted By Charlie Arehart | 12/13/06 4:31 PM
Simon Horwith's Gravatar That's correct, Charlie - there's nothing about what you posted related to what I blogged, aside from the fact that your entry is what reminded me to post about making applications more portable... that's all. The entry itself is good, as usual (that's why I read your blog, after all) and I found it interesting. I don't typically register, or encourage registering of, web services in the CF Admin because of the portability issues I described... which is why your entry reminded me to blog about portability.
# Posted By Simon Horwith | 12/13/06 9:35 PM
Simon Horwith's Gravatar Peter, I agree that will tools like ANT and the J2EE deployment features in CF, relying on administrator settings in your apps doesn't have to make porting your app a complete nuisance like it used to. That said, if you're distributing code, not everyone wants to use Ant or the J2EE deployment wizard to install a code base - if you can write an application so it can be easily copied from one place to another then I'd still encourage it. Also, from a readability perspective (think maintenance/troubleshooting and code-review) settings can be a real nightmare. If I'm trying to figure out what someone else's code is doing, I really don't want to have to go to the CF Administrator to do so... not if it's avoidable.
# Posted By Simon Horwith | 12/13/06 9:40 PM
Peter Bell's Gravatar Hi Simon,

Agreed. And as usual, it's use case driven. I sell SaaS and co-lo my servers so my only interests are simple "staging to production" builds, easy deployment across web farms and automating disaster recovery, and those are easy to solve with in house build scripts. I don't have to worry about client installs, people using my code on shared server, or anything else, so I take full advantage of that luxury. That said, it really is impressive what can be done to minimize manual configuration requirements if you think about it a little, and an install should never be more complex than it absolutely has to be. I should probably review what is required in my builds and see what could be simplified.
# Posted By Peter Bell | 12/13/06 9:53 PM
Charlie Arehart's Gravatar Thanks for the clarification, Simon, and of course for the kind regards. But I must say it seems you still have some misconception: my entry wasn't about "registering web services in the CF Admin". I certainly wasn't "encouraging" it, either.

Again, for any who may only be half-reading all this: when you invoke a web service in CFML (using CFINVOKE or CFOBJECT/createObject), a java proxy/stub is created to map the available API from that remote web service. If the web service API changes, your CFML code may get errors because that cached proxy/stub (a java .class file) will be out of synch with the new API.

The traditional solution most have recommended is to go into the Admin console, where you'll find an entry for each such web service that's been executed (not "registered"), and you can hit the available "refresh" button for that web service, which will rebuild the proxy/stub.

All I was saying in my blog entry was that if one wanted/needed to do that programmatically, you could, and I showed the code (both the CF7 Admin API and the "older" ServiceFactory approach).

Perhaps, Simon, your thoughts were triggered by the point I made later in the entry about how the Admin API methods used may vary in their use depending on whether one had renamed a web service in the Admin console, something I then also explained. But the entire entry applied to web services regardless of whether one had renamed them or not. That was really a minor sub-story.

I suppose you could make the argument that the notion of renaming a web service is something that you'd argue against from that standpoint of making code portable, and I wouldn't disagree. But boy, one had to read really carefully to make the connection. :-)

Sorry, again, to readers who think I've been picky or defensive about this. It's just that the wording of the entry above really sounded like a blast against the bigger subject of my entry. Now we can see that it should not be read that way. Where's Officer Barbrady (SImpsons) when we need him: "Let's move along folks. Nothing more to see here." :-)
# Posted By Charlie Arehart | 12/13/06 9:58 PM
Damon's Gravatar baodichdusac
# Posted By Damon | 2/5/07 11:04 PM
This site is hosted by HostMySite and runs off of BlogCFC - thanks, Ray.