Tuesday 28 May 2013

CFML: Application.cfc look-ups and linked directories

G'day:
This is a pretty non-descript topic / article, sorry. Someone raised a question on the Adobe ColdFusion forums about the possibility of CF10 not getting along well with Application.cfc look-ups from within a symlinked directory. I doubted this would be an issue, but I wasn't 100% sure so I thought I'd check it out.

Links / junctions
Before I start, just in case you're unsure about symlinks, here's what Wikipedia has to say about them. These are pretty prevalent on *nix systems, but they're under-utilised on Windows. Mostly because people are unaware they exist. Windows has actually been able to effect pretty much the same thing as a symbolic link since Windows 2000. Well: the Windows GUI shell couldn't, but the underlying NTFS file system has supported junction points since Windows 2000 came out. And a junction point amounts to the same thing.

Junction points are bloody handy. I have quite a number of CFML app servers running on this machine: CF5, CFMX7, CF8, CF9 (multiple instances), CF10 (multiple instances... Railo Express, some manner of OpenBD install (gathering dust)... and they all have their own discrete webroots for platform-specific stuff (eg: C:\webroots\cf5, C:\webroots\railo-express, etc). However I have a repository of all my common code @ C:\webroots\shared. Into each of my version-specific webroots I junction-in a reference to shared, so I have C:\webroot\cf10\shared etc. As far as CF10 is concerned, C:\webroot\cf10\shared is a discrete directory. And as far as Railo is concerned, C:\webroots\railo-express\shared is a discrete directory. But it's all the same place. Cool. I dunno how to create these things on *nix (something to do with ln comment), but , but on Windows it's just this:

C:\webroots\cf10> mklink /d shared c:\webroots\shared

Where obviously "shared" is what the junction ends up being called, and c:\webroots\shared is where it points to.

I've never had any problems with these on ColdFusion or Railo, and have been using them for a decade.

Anyway, that was a digression. Another side issue that came up from the original question is how does CF look for which Application.cfc (or .cfm in the case of the original question) to execute, for a given request. I'd forgotten that there were multiple "modes" for doing this settable in CFAdmin, and had never really paid much attention to them, so decided to roll both these concepts / questions into one investigation.

Firstly, lets look at the options on offer in CFAdmin:

As various people have blogged, docs on this setting are a bit thin on the ground, so I can reproduce it in its entirety without blowing out my word count:

Select the order in which ColdFusion searches for Application.cfm or Application.cfc if it is not found in the current project folder. You can set ColdFusion to search as follows:

  • default search order: ColdFusion looks for an Application.cfc/Application.cfm file from the current folder until the system root directory. On Windows, this could be C:\ and on UNIX, /opt.
  • till web root: ColdFusion looks for an Application.cfc/Application.cfm file from the current folder till web root.
  • in web root: ColdFusion looks for an Application.cfc/Application.cfm file in the current folder or web root.
And that's all there is to it. I think perhaps the guidance is wrong for *nix... surely / is the root, not /opt? I suppose it depends on permissions, but the same could be said for the /opt dir as well. And "till"? Sigh. But anyway. There's also another significant terminology error in there, but I'll get to that.

To demonstrate each of these, I have set up the following directory structure on my box:

C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
  temp\
    linked_subdir\
      test.cfm

There are several significant elements to this:
  • The root
  • The ColdFusion instance directory
  • The ColdFusion app root - CF serves files from within here
  • The web root - the web server serves files from within here
  • A junction
  • The directory that is junctioned
  • Test files
In the table below, I highlight where ColdFusion will look for an Application.cfc or Application.cfm (it'll be one or the other, in that order of precedence, btw), given the specified file being requested.



Update 1:
Please see the follow-up article to this. I cannot replicate some of the behaviour I detail below, when I re-tested this. So tread with caution when taking this advice on board. I am going to review my work here and make sure I'm right, or fix it if it's wrong. The short version is: perhaps the look-ups only go as far as the web site web root, not the ColdFusion application root after all. Watch this space though.
Update 2:
I have re-tested this tonight, and I am seeing the original behaviour as documented below. And I cannot now replicate the behaviour I reported on in the first update, above. This is really weird. I will put more effort into replicating both behaviours and trying to work out WTH is going on. Sorry to not be more conclusive about this!

Default
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
 
Until web root
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
In web root
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm
C:\
  apps\
    adobe\
      ColdFusion\
        10\
          cfusion\
            wwwroot\
              docroot\
                test.cfm
                some_dir\
                  linked_subdir\
                    test.cfm
                  local_subdir\
                    test.cfm


Note the graduation of the directories being highlighted is supposed to indicate the precedence of where CF looks, from most likely to least likely. Basically ColdFusion looks from the directory the requested file is in, and from there traverses towards the root of the drive (following one of the three sets of rules as set in CFAdmin) until it finds an Application.cfc / Application.cfm. Then it uses that file, and stops looking. It is important to remember that once CF has found an Application file to run, it only runs that one file. It does not keep looking-for and executing more files further up the food chain. It's also important to note that in the case of Application.cfm and OnRequestEnd.cfm, no similar look-up occurs to locate the OnRequestEnd.cfm file: CF only looks in the directory it ended up finding an Application.cfc file, and runs any OnRequestEnd.cfm file found in there. It does not do any look-up or traversal for OnRequestEnd.cfm files.

One important thing to note here is that the CF docs misuse the notion of "web root". Universally, the concept of "web root" is related to the home website directory (as seen by the visitor to the site, and the web server), not how Adobe are using the term, to mean "the CF application root". This is confusing and misleading, and has indeed mislead at least one "senior" ColdFusion blogger whose material I was reading last night whilst researching this. They took Adobe to be using the term "web root" correctly, rather than in the incorrect way they actually use it. So to be very clear: when Adobe say "web root" they do not mean the web server's / web site's actual web root, they mean the ColdFusion server's application root.


The other thing I confirmed in all of this - and why I didn't bother showing both linked_subir and local_subdir options in the table above: ColdFusion doesn't distinguish between linked directories and actual directories. Indeed if you were to have this code in C:\temp\test.cfm:

<cfoutput>#getCurrentTemplatePath()#</cfoutput>

And then browsed to that file via /some_dir/linked_subdir/test.cfm, you'd get this output:

C:\apps\adobe\ColdFusion\10\cfusion\wwwroot\docroot\some_dir\linked_subdir\test.cfm

And that is about all I can wring out of this topic, actually. So I'll leave you be, and I'll go watch television now.

Righto.

--
Adam