Monday, November 25, 2002

Ever want to open Studio, Notepad, CF Admin or some other program in a single keystroke in Windows

If you want to open a program like CF Studio or the CF Administrator or just Windows Notepad in a single keystroke, it's remarkably easy. For all my years of experience with Windows I'm surprised I never noticed this before.

You just need to edit the properties for the shortcut to the program (a shortcut is simply what you choose to run the program on your Start>Programs menu, or your desktop, or in my computer/explorer, etc.). Just right click on the shortcut/icon, choose properties, and in the "shortcut key" field shown, press any desired keystroke combination. It will be reflected in that field (such as "Control + Alt + A" if you press those three at once). Press "ok" and you're set. That easy. Now when you press those keys, the program will open. Cool. (I know that there are jaded folks among you wondering what I'm so excited about, but those who prefer the keyboard over the mouse will revel in this if it's news to you too).

Just notice that it doesn't warn you if you're overwriting another shortcut. Indeed, if you press a shortcut that's already taken, it will simply execute that shortcut, even while the focus is in that field for choosing a new one. (At least CF Studio's own keyboard shortcut tool, under Tools>Customize>Keyboard Shortcuts shows you if a key you're choosing is already assigned to something else).

And these shortcuts seem to work from within any other program (even overriding those set for that program, it seems) which is a two-edged sword.

Specifying HTTP headers in CFHTTPPARAM

If you find yourself needing to "spoof" different headers in a CFHTTP operation, such as to change the referer, accept encoding value, or other header you may know that you need to use CFHTTPPARAM and its available TYPE="CGI" attribute. But you may have challenges using that if you try to simply fill the corresponding NAME Attribute with the name of the equivalent CGI variables that CF exposes.

For instance, the Referer variable in the HTTP header becomes CGI.HTTP_REFERER. You might be lulled into thinking that the appropriate way to override/spoof that with a new value in CFHTTPPARAM would be:

<CFHTTPPARAM TYPE="CGI" NAME="HTTP_REFERER" VALUE="SomeRefererValue" >

but that will not produce the desired result. Indeed, what it will do is cause a CGI.HTTP_HTTP_REFERER variable to be created on the page being called.

So you may easily deduce that you should leave off the HTTP_ in the NAME attribute, and that is correct.

But what about the CGI.HTTP_ACCEPT_ENCODING? This is the value that indicates whether the browser you're simulating (with CFHTTP) should accept GZIP compressed output. (More about that in a later blog to come on doing GZip filtering with CFMX). The point is, if you want to set that on a CFHTTPPARAM and try to it as follows:

<CFHTTPPARAM TYPE="CGI" NAME="ACCEPT_ENCODING" VALUE="gzip,deflate" >

that will still not produce the desired result. It will NOT pass create the specified value. Why not? Because though the docs don't clarify it, you need to specify the header names by their standard actual names as defined in the HTTP standard. In the case of this header, it's accept-encoding, using a dash rather than the underscore that CF uses in the name it converts to.

So how'd I know the correct name to use? It's documented at this W3C page.

But how did I know what to value to use for the VALUE attribute for the accept-encoding? I wanted in the CFHTTP to send the same value that a browser would send, so I looked at what the value was in a a dump of CGI.HTTP_ACCEPT_ENCODING while browsing a page in a regular browser, and "gzip,deflate" was that value.

As a somewhat interesting side note, if you don't override the value this way using CFHTTPPARAM, then the value would be null on a page called by CFHTTP. Again, this will be more important in regard to a later blog entry I'll offer on gzip filtering and setting up tests of that using CFHTTP.

One last comment: you don't need to set the user-agent header using CFHTTPPARAM, though. The CFHTTP tag has a special USERAGENT attribute just for this specific requirement.

Sunday, November 24, 2002

Some interesting Oreilly articles about DW, Flash for developers

I try to make my blog more about new discoveries rather than simply point to others articles, but I came across a couple at the O'Reilly site that seem pretty interesting reading for CF developers wondering (or having to debate folks) about RIA and Flash's place in the big picture of web app development, and another about DW for developers.

The first is a digest of an email conversation between Tim O'Reilly and some of his editors at http://www.oreilly.com/editors/ discussing Rich Internet Apps and Flash MX. Nice to see Tim generally defending the Flash/RIA vision. It doesn't end on the nicest of notes because of who they let have the last word, but overall an interesting read. Also see the comments from readers (just a few so far) at http://www.oreilly.com/cgi-bin/comments.

(Sadly, these links are to generic "editor"and "comments" page and don't incorporate any date, so I suppose in a few weeks they will no longer point to the same article. Here is the Google cached version.)

As for DW for developers, one of the participants in that article, Bruce Epstein, had written "Dreamweaver: A Visual Tool for Serious Web Coders", which while it's about DW 4 and not WMX, it's still rather useful. The first sentences are enticing enough, "With over 20 years of programming experience, I am skeptical of visual Web development environments. Dreamweaver has made me a convert."

I've pointed out before that there are also several articles of that sort about DWMX, and getting into it as a developer who may feel challenged by it compared to Studio/HomeSite, at the MM DesDev site for DWMX. Check those out, too.

Friday, November 22, 2002

Alert: when CF will fail to auto-compile--might be big news to some

Have you heard about people complaining that they find the automatic compile process is sometimes not detecting changed code? Did you wonder how that could ever be? Or had a hard time recreating it?

I can now offer a couple of interesting scenarios that are recreatable and do explain it, and seem worthy of further investigation. The problem has to do with copying old code onto a server when there's already compiled code for a newer version. This is the sort of thing one may do when backing out code versions on a prod or test server.

Of course, the automatic compile process works fine if the "new" code has a date/time stamp greater than the old compiled class file. That's the obvious time when it should recompile, but I've learned that CF has a little harder time dealing with when the date/time stamp of the "new" file is older than that already compiled, such as that described above in a version backout process.

The problems I've found are quite serious and very surprising.

The first problem is that if you copy the old file onto the server while CF is down, it WILL NOT detect the changed old code on restart. Wow.

Second, if you copy such old code onto the server while CF is up and running, it again WILL NOT detect the changed old code if--and this is a weird if--the file has not yet been browsed in the run when the copy is made. Further, even if you copy the old code while the server is running and you HAVE previously browsed the file, it's still not enough. You need to execute it after the copy, otherwise even after a restart it will still not see that new "older" code.

I know it may sound a little confusing, but here are some scenarios with more detail. First, the process of copying an old file while the server is down.

In the following, t1 and t0 are used to represent states of the file at points in time, with t0 being before t1:

filea is created/newly edited
filea (t1) is browsed
- auto-compiled into class under the covers
- is executed
- results show output of filea (t1)

server is stopped
old copy of filea (t0) is copied on to server
server is started

filea (t0) is browsed
- auto-compile does NOT create new class under the covers --- this seems the root of the problem 
- is executed 
- results show output of filea (t1), NOT the results of filea (t0)! -- even though t0 is the current code 

Also, note carefully that I indicated the stopping and starting of the server during which time the old file was copied. This is certainly a reasonable scenario, again as in when a production or testing server has code rolled back to a previous release. It's reasonable to expect that upon restarting the server, the old code should be newly compiled and executed, yet it's not. That's clearly a problem that needs to be solved. 

Now, perhaps more curious is what happens when the old file is copied onto the server while CF is up and running. It's not obvious. Indeed, only sometimes will it detect the changed "older" code. Sometimes it will not. No wonder people are pulling their hair out, both experiencing it and trying to recreate the problem. (If this is already known in support, sorry. I hadn't seen it explained anywhere before.) 

I've found that it depends on whether the file being replaced had been browsed/executed already at least once during the run of the server. If it HAD already been browsed/executed since the server was started and then an old version copied in, then it does detect the change--as long as you execute the page in that run after the copy. Otherwise, even after a restart, that new "older" code is not detected and executed. 

Further, if instead you make the copy during the run when it had NOT yet been browsed/executed since the server was started, then again it will NOT detect the change in that run or on restart. This is all very interesting. Here's a scenario: 

Run 1: 

filea (t1) is newly edited 
filea (t1) is browsed, autocompiled, executed, shows new (t1) output 

Run 2: 

filea (t1) is browsed/executed during this run 
- autocompiled, executed, shows (t1) output 
filea (t0) (older version predating t1) is copied onto server 
filea (t0) is browsed, autocompiled, executed, shows new (correct, t0) output 

That's what we'd all expect (or hope for), of course. 

But if we had not performed that last step of browsing the file after the copy, then even on a restart we'd still see t1! 

Further, if in run 2, you did NOT browse t1 during the run before copying in t0, again you will NOT see the output of t0 on a refresh of the page. You'll still see t1 from the previous compilation. And again, a restart of the server also won't fix this. 

So this is ugly. Really ugly. It seems old code copied in will only be executed a) if the server is up and b) the file has been executed in that run before the copy is made and c) the file if browsed/executed after being copied in. 

Has this been recognized? I'm running on the built-in web server, if that's significant, and running with the updater. 

I'd think this warrants a KB, and hopefully a fix. 

In the meantime, for those needing a workaround, there are a couple: 

First is that while the -f fails to work you could instead delete the corresponding class files from the server (in wwwroot\WEB-INF\cfclasses) before making the copy in of the old code (or at least before starting the server after making the copy). Then when you run the code there will be no class file there and CF will auto-compile it. 

Unfortunately, it's not trivial mapping a given filename (and its directory location) into the equivalent filename used in the cfclasses, to be able to delete just the files you'd be interested in. There's a hashing algorithm that I've seen explained but not well enough that I was able to devise (nor have I seen) a routine to do that conversion for us. If anyone offers it, I'll post it here. 

Another alternative may seem (and some have done this) to delete ALL the files in the cfclasses directory. Sadly, this is way overkill. The directory holds all the classes for all files in all directories (even virtually mapped ones outside the wwwroot), so deleting them all to just effect a change for one or even a few is a pretty high price to pay. It will force recompilation of everything, thus slowing down the server and the end-user experience of the first person hitting each page. While the precompile.bat may help if you then recompiled everything before restarting, this all just seems like hitting a gnat with a hammer. 

Another solution is simply to take advantage of the observation I made that if you start the server, then browse the templates, then make the copies while the server is up, and then browse them, then indeed the changes will be reflected. This is arduous if you need to automate the process (such as overnight), to automatically run the templates and then copy them and execute them again. Of course, you could use CFHTTP to execute the pages--and have it look at some list of files you are going to be copying. And you could use CFFILE to do the copying also from that list, and then use CFHTTP again to re-execute the newly copied files. 

Update:

I had said originally here that
One other idea (again, if we had the agorithm to map source dir/file names to their compiled cfclasses counterparts) would be to automate the process and have it CF automatically run a check at startup to find any files that have dates older than their previously compiled counterparts (to catch such copied older files) and run them so that if they were copied in during the run they'd "take" immediately on their next run. CFHTTP could be used to execute the code.

Now that I think about it, that's no solution at all. All that matters is that anytime old code is copied in during a run, the code needs to have been run once and then be run again after the copy in, in order for the change to be respected (for a new class to be created based on that older code). The bottom line is that there's no automation at startup to do. You simply need to manually run the code, copy it in, then run it again.

That still won't solve the problem of old code copied in while the server was down. It's not enough to execute it in the next run, you need to browse it in a new run, copy it during that run, and browse it again during that run). Otherwise the server will just never see that new "older" code that was copied in.

There are so many permutations and variables here that it's possible I've missed or left something out. Open to thoughts.

Hopefully, MM will recognize this and either has a fix coming or can add one soon. I know some updaters are planned to be released in the very near future.

BTW, my testing has been on the built-in web server. Don't know if that will have an impact. And again I do have the first updater applied. Don't know if things are different pre-updater.

update:
Steve Ringo, manager of the South Africa CFUG, made this suggestion:

I was thinking about an easy workaround - how about this? Use a file "touch" utility to change all dates/times to "now()". This should be fine for a production server, as the date and time modified will still be intact for the development server, where the original date and time may be useful to have - for source control programs for instance.

I found a great freeware one at http://stevemiller.net/apps/ (See win32 Console ToolBox 1.0). It also can recurse subdirectories. Just pop this line into your precompile batch file:
touch /s *.cfm

You can add /q to "quieten" the output if you dont want a status report on each file - hence touch /s/q *.cfm

I haven't had the chance to try it yet in CFMX, but the utility works fine testing with some arbitrary files on my hard drive (with subdirs). AFAIK, the touch utlity is standard with most flavours of Unix (I dont think it has subdir recursion though).

Sounds like it could work. Thanks for that, Steve. One negative is that it would change the date/time of the file on the server which would make it out of synch with the place from which it was copied (whether another server, a develoepr's workstation, or a version control system). It's not a show-stopper, especially if it solve the problem, but it may annoy some as much as the problem. But otherwise worth a look. Thanks