ColdFusion 8 Application Specific Mappings Work With The CFComponent Extends Attribute

Posted June 11, 2009 at 9:00 AM

Tags: ColdFusion

This blog post is a bit silly because it's merely confirming something that was supposed to work. However, until last night, for some reason, I was under the impression that application-specific mappings in ColdFusion 8 did not work the CFComponent Extends attribute. This wasn't just some wild theory I had - I thought this because I've had a number of problems getting application-specific mappings to work in applications ported from CF7 to CF8 in which frameworks such as ColdSpring were used; even after settings up "ColdSpring" as an application specific mapping, the framework still threw errors about components that it couldn't find.

Last night, I asked about changes to this in ColdFusion 9, and both Ben Forta and Adam Lehman put me straight - it's supposed to work with application-specific mappings in CF8. So, this morning, I put together a small demo to see if I was crazy. I constructed the following directory tree:

/app/
/app/Application.cfc
/app/index.cfm
/com
/com/Base.cfc
/com/Test.cfc

As you can see, the "com" directory is parallel to my "app" directory so, in order to create a CFC in the app, I'm going to need a mapping. But then, the Test.cfc is going to extend the Base.cfc using a mapping as well. Here is my Application.cfc with mapping:

Application.cfc

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • hint="I provide application level settings and event handlers.">
  •  
  • <!--- Defnie request settings. --->
  • <cfsetting showdebugoutput="false" />
  •  
  • <!--- Define application-specific mappings. --->
  • <cfset THIS.Mappings[ "/com" ] = (
  • REReplace(
  • GetDirectoryFromPath( GetCurrentTemplatePath() ),
  • "[^\\/]+[\\/]$",
  • "",
  • "one"
  • ) &
  • "com\"
  • ) />
  •  
  • </cfcomponent>

As you can see, I am creating a mapping, "/com", by moving up one directory from the current directory and then appending "com\" to the end.

In my "com" directory, the Test.cfc extends the Base.cfc, so let's look at the Base.cfc first:

Base.cfc

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • hint="I am the base component for this package.">
  •  
  • <cffunction
  • name="Test"
  • access="public"
  • returntype="string"
  • output="false">
  •  
  • <cfreturn "I am com.Base.Test()" />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, not much going on. The Test() method simply returns a string containing the mapped method that was executed. Now, let's take a look at the Test.cfc that extends the Base. Notice that the Extends attribute of the CFComponent tag uses the application-specific mapping defined in our Application.cfc:

Test.cfc

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • extends="com.Base"
  • output="false">
  •  
  • <cffunction
  • name="Test"
  • access="public"
  • returntype="string"
  • output="false">
  •  
  • <!---
  • Return test string from both the super component
  • (from extends attribute) and the current component.
  • --->
  • <cfreturn (
  • "[ #SUPER.Test()# ] " &
  • "I am com.Test.Test()"
  • ) />
  • </cffunction>
  •  
  • </cfcomponent>

Ok, so now that we have all of our components in place, let's take a look at the code that ties it all together - the index.cfm page. My Index.cfm page simply creates the Test.cfc component using the application-specific mapping and calls its Test() method:

Index.cfm

 Launch code in new window » Download code as text file »

  • <!--- Create an object with the mapping. --->
  • <cfset objTest = CreateObject( "component", "com.Test" ) />
  •  
  • <!--- Test component method.. --->
  • <cfoutput>
  • #objTest.Test()#
  • </cfoutput>

When we run this code, we get the following output:

[ I am com.Base.Test() ] I am com.Test.Test()

As you can see, not only did the application-specific mapping let me create the Test.cfc component, it also allowed the Test.cfc component to extend the mapped Base.cfc component.

Like I said before, this is the expected behavior! Looking at it now, it seems so simple; I am not sure what problems I could have possibly been having with this before, but clearly it was an error on my part. There's nothing more frustrating than when silly mistakes cost you time and energy.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Reader Comments

Jun 11, 2009 at 10:15 AM // reply »
18 Comments

Since I'm not a huge fan of regular expressions (they look like gibberish to me), I tend to go with relative mappings instead.

<cfset this.mappings["/com"] = getDirectoryFromPath(getCurrentTemplatePath()) & "..\com\" />

It looks kinda weird if you dump the mapping, but it still works.


Jun 11, 2009 at 10:16 AM // reply »
25 Comments

I've had problems in the past when doing app mappings as well - especially when specifying expandpath('.') or expandpath('/') for the file system path in an Application.cfc. Then to make matters worse, then a gateway call is the first to initialize your app, you end up with a cfusion.ear/cfusion.war type path instead of perhaps the expected one of say c:\Inetpub\wwwroot or whatever -

We've thrown out the ability to map at the application level in favor of the cfadmin mapping, which can then be converted to the platform specific path whether its linux or windows - and prevents problems overall.


Jun 11, 2009 at 10:25 AM // reply »
18 Comments

@Kevin,

Using expandPath() will return the directory path to the page that is currently executing, meaning the mapping will be based on the page that initializes your application.

If you use getDirectoryFromPath(getCurrentTemplatePath()), it will return the directory path of the page where the function is being called, which should always be Application.cfc.


Jun 11, 2009 at 10:37 AM // reply »
25 Comments

@Tony

That's more than likely where I went wrong - I never did clearly understand that one -
*thanks


Jun 11, 2009 at 10:40 AM // reply »
6,351 Comments

@Tony,

Cool tip. I can't say that I knew that relative paths would even work that way. Thanks!

@Kevin,

Yeah, as @Tony said, ExpandPath() can be iffy because it's related to the requested template, NOT the executing template. That's why GetCurrentTemplatePath() is such a powerful function.


Ryan McIlmoyl
Jun 11, 2009 at 12:06 PM // reply »
6 Comments

@Kenny,
About platform specific paths, I make it a habit to convert any backslashes to forward slashes in the paths I get back from the various path functions, since forward slashes in CF work on Windows. I can then proceed happily without worrying about path separators in my applications.


Jun 11, 2009 at 12:08 PM // reply »
25 Comments

@Ryan -

Yeah thanks, I've found that to be the case -


Jun 11, 2009 at 12:10 PM // reply »
6,351 Comments

@Ryan,

I try to make that a habbit; but, I believe in the latest versions of ColdFusion, I am pretty sure that that no longer makes a difference.


Jun 11, 2009 at 1:26 PM // reply »
29 Comments

I too was under the impression this did not work.

I think part of the confusion stems from the fact that you cannot use application specific mappings in cfimport tags, so they do not work everywhere you would expect them to.


Jun 11, 2009 at 3:51 PM // reply »
6,351 Comments

@Nathan,

Hmmm, maybe that was my problem. I could have sworn it was a cfc extends issue. Maybe it was a collection of problems :)


Jun 12, 2009 at 11:46 AM // reply »
25 Comments

@Tony -

Would GetBaseTemplatePath() instead of getCurrentTemplatePath() be 'technically' more accurate - as it's the 'Gets the absolute path of an application's base page.' - so it should return your base app path - regardless of where its used in your application -
I understand if your using 'getCurrentTemplatePath' in app.cfc you're good * - but seems getBaseTemplatePath is a handy one if you needed that base reference elsewhere right?


Jun 12, 2009 at 11:52 AM // reply »
25 Comments

Actually no you're right - I see the error of my ways:

If i'm in a cfc off my webroot say in /model/x - and i dump either getbasetemplatepath OR getCurrentTemplatePath, they both return the same value - neither of which are the path to where the application.cfc lives -


Jun 12, 2009 at 12:20 PM // reply »
25 Comments

Ok - I'm still not convinced application specific mappings even work and I have verified 10x that I have the 'enable app settings' turned on in cfadmin.

I have a root of say:
c:\websites\webroot
and I specify
<cfset this.mappings["/root"]="c:\websites\webroot"/>
If i try to instantiate an object off the root, or even include a file in the root such as:
<cfinclude template="/root/test.cfm"/>

It gives me 'Could not find the included template /root/test.cfm'

Even through I've just defined it!

Of course, once I go into the cfadmin and add that mapping in for that instance - then it works fine - I think this is my main cause of concern with this technique.


Jun 12, 2009 at 12:22 PM // reply »
6,351 Comments

@Kevin,

Where in the Application.cfc are you defining this?


Jun 12, 2009 at 12:32 PM // reply »
25 Comments

Ok - sorry for the post barrage - but i think it's worth noting:

In the application.cfc where these mappings are defined in the constructor code, attempting to call a cfinclude or a createobject using one of the predefined mappings simply does not work from what i can tell. However, after that, they do seem to take hold - confidence restored (sort of).

Thanks all for your patience - and feedback - these tips helped me get rid of a few mappings in our application and will ease deployment to multiple servers by not having to define new mappings via cfadmin.


Jun 12, 2009 at 12:36 PM // reply »
25 Comments

@Ben -

Yeah so to be more clear on the implementation that didn't work:

<cfcomponent displayname="Application">

<cfset this.mappings["/root"]="c:\websites\webroot"/>

<cfinclude template="/root/test.cfm"/>

Won't work -
However, in an onApplicationStart Method - once the Constructor has been setup - using an include or any other referernce to the mapping seems to work fine - seems you just can't use it w/in the constructor block it seems - as the mappings must not be set until the contstructor has completed (?).


Jun 12, 2009 at 12:58 PM // reply »
6,351 Comments

@Kevin,

Yeah, from what I have played with:

http://www.bennadel.com/blog/1583-ColdFusion-8-Per-Application-Settings-Get-Partially-Cached-And-There-s-Nothing-You-Can-Do-About-It-.htm

Not only MUST the mappings be defined inside the pseudo construct (code inside cfcomponent, outside cffunction), you need to wait until you are in an event handler (ex. OnRequest()) before you can use them.


Jun 12, 2009 at 3:53 PM // reply »
25 Comments

@Ben -

Yup - works flawlessly now! You just can't attempt to reference the mappings w/in the pseudo construct.


Jun 12, 2009 at 4:28 PM // reply »
6,351 Comments

@Kevin,

Glad you got it working. Yeah, seems like an odd limitation. I guess it has to do with when the settings get applied.


Jun 12, 2009 at 4:49 PM // reply »
25 Comments

Ok - so here's the ultimate rub:

You need these mappings defined in the cfadmin ANYWAY - if you're going to be working with flex remote objects!

I just found that out - worth a blog post on it's own.

So regardless of the ability to map them w/in the application, when you want flex to interact with them and have the remoting-config.xml 'use-mappings' turned on, you still need to define the path in the cfadmin.

Back to square 1 again ;) having to define them in cfadmin.


Jun 12, 2009 at 4:55 PM // reply »
6,351 Comments

@Kevin,

So, just so we're on the same page, what you're saying is that the Extends attribute of the remote-access CFC doesn't work with app-specific mappings?


Jun 12, 2009 at 6:21 PM // reply »
25 Comments

Well, if you're talking access="remote" on the test.cfc itself and you attempt to call that using flex remote Object call - using an alias of 'com.Test', you may find that it fails unless you specify '/com' mapping in the cfadmin. From what I just found. I had app specific mappings in the application itself, but tried to call a component via flex and got a 'Could not find the ColdFusion Component or Interface' error in the cflogs. Once I added the mapping to the cfadmin, the flex remote call worked again (as I had removed the mapping from cfadmin to prove that the application mappings were the only mappings being taken).


Jun 13, 2009 at 11:22 AM // reply »
18 Comments

@Kevin,

Unfortunately application specific mappings won't work for flash remoting since the call isn't coming from the application, so you'll still need to create the mapping within your cfadmin.

What I do is create the mapping during onApplicationStart() by using the cfadmin API. Works pretty well, but your application will need to be initialized before the flash remoting will work, which I don't think is too much to ask.


Jun 15, 2009 at 8:43 AM // reply »
6,351 Comments

@Tony,

When you say flash remoting, you are specially referring to AMF-style remoting, correct? If the Flash / FLEX movie makes a call using standard HTTP calls, it should work like any page request in the applicaiton.


Jun 15, 2009 at 8:57 AM // reply »
6,351 Comments

@Kevin,

Gotcha. I'm not too familiar with FLEX for Flash remoting. I know there are like 3 different ways to connect to a given URL in FLEX, using different underlying technologies. I think some work and some will not work with the app-specific mappings.

Seems odd, though, as the remote object should have access to the underyling application??


Jun 15, 2009 at 10:45 AM // reply »
25 Comments

@Tony - Thanks for confirmation on that - yeah seems a bit inconsistent that it's not at the application level but more at the app server level then. I agree, the best method would be through admin api - which we're doing for enough other things as well.

I hope this wasn't too far off the original topic - but VERY good information regardless, and not sure how 'well' documented some of these are.


Andrew Bauer
Jun 27, 2009 at 12:28 AM // reply »
45 Comments

I found that as an alternative you can setup a virtual directory for your site.

I worked this out after not being able to set a dynamic mapping for Mach-II's directory, as it is required in the cfcomponent's extend method for Application.cfc.

I have tested it in Apache on my development computer, however am pretty sure that it will work for IIS (as this is the approach taken to setup CFIDE for each site).

For anyone that is curious on how to do this for Apache: Create an Alias for the folder and then set the folder permissions via a directory node. The following has worked for me:

Alias /[Mapping] "[Folder Location]"
<directory "[Folder Location]">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</directory>


Jun 29, 2009 at 8:50 AM // reply »
6,351 Comments

@Andrew,

Interesting technique. I have not played with virtual mappings very much.


Drew Wells
Jun 30, 2009 at 11:44 AM // reply »
6 Comments

We use application level variables to map our CFCs, like so:
<cfset application.cfc.udf = createObject("component","cfc.UDF") />

These are run once variables in the onApplicationStart(). Would I be able to use application level mappings (this.mappings) to define my cfc paths?


Jun 30, 2009 at 1:34 PM // reply »
6,351 Comments

@Drew,

Yes.


Jul 7, 2009 at 1:12 PM // reply »
25 Comments

Just to add another item to this thread - I came accross a reference in code from Adobe that had this:

getPageContext().getServletContext().getRealPath("/")

This seems to work nicely to get the 'root' directory path as well - and in a clean format.


Jul 7, 2009 at 1:19 PM // reply »
6,351 Comments

@Kevin,

Does this return a "/" starting path? Or an expanded path?


Jul 7, 2009 at 2:56 PM // reply »
25 Comments

It returned the absolute path to the root of my application - which is a struggle to get at from various parts of an app if needed

i.e. c:\inetpub\wwwroot\mysite


Jul 7, 2009 at 5:06 PM // reply »
6,351 Comments

@Kevin,

Looks good. Does it matter where you call that from? Are you calling from within Application.cfc?


Jul 7, 2009 at 5:43 PM // reply »
25 Comments

No doesn't seem to matter
- however it does return the my 'cfusion.ear\cfusion.war\' path if I have an app under that area vs. outside - so may be useful depending on your situation.

Try before you buy I guess.


Jul 7, 2009 at 5:55 PM // reply »
6,351 Comments

@Kevin,

Ok, sounds good. I'll have to check it out.


Don
Aug 28, 2009 at 3:28 PM // reply »
30 Comments

Kevin, that doesn't work if you are using virtual directories in IIS. I have my dev app on e:\myapp and when I put in the code getPageContext().getServletContext().getRealPath("/")

It returned c:\inetpub\wwwroot so it looks for the default webroot not the app root.

So far I haven't been able to get this working. Even Ben's example doesn't work because I get the E:\ back but not E:\myapp so all my mappings go to E:\. That doesn't work of course.


Don
Aug 28, 2009 at 3:54 PM // reply »
30 Comments

So exactly where do I put the mappings? I tried in the whole "this" area of the app.cfc but that doesn't work. I put it in the onRequestStart and that doesn't work.


Sep 2, 2009 at 9:21 AM // reply »
6,351 Comments

@Don,

I have had serious problems with files paths with virtual directories because the URL no longer aligns itself nicely with the actual file path (which doesn't include the virtual directory).

As far as the mappings, you have to put them in the pseudo constructor of the Application.cff (at the top, outside of any CFFunction tags). The biggest trick is that the map key *must start with* "/". If it doesn't it won't work.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 3, 2009 at 10:55 PM
Project HUGE: Active Release Technique (ART) With Dr. Christopher Anselmi In NYC
A peaceful mind and body keeps a man happy.Muscle release techniques are employed to people with severe sports injuries and stress pains.A great websites offers a wide range of muscle release techniq ... read »
Nov 3, 2009 at 10:54 PM
jQuery Demo: Creating A Sliding Image Puzzle Plug-In
Very nice list, thank you. http://www.likespy.com/ ... read »
Nov 3, 2009 at 10:26 PM
IIS MOD-Rewrite: R6016 Not Enough Space For Thread Data
Very nice list, thank you. ... read »
Nov 3, 2009 at 7:34 PM
Dude! I just posted this!
Just upgraded my URL handler... testing. ... read »
Mike Leung
Nov 3, 2009 at 7:23 PM
A Moment That Touched Me - The Fountainhead
re: Cooljj: Any example of Rand's "genius" anyone has ever cared to share that I've seen -- like Roarke calling someone a fool completely overlooking how he's demonstrating as much concern over what ... read »
Nov 3, 2009 at 6:28 PM
Making ColdFusion's QueryNew() More Readable
No - but I will now, just got to figure it out, never used WDDX method before. ... read »
Nov 3, 2009 at 3:58 PM
A Moment That Touched Me - The Fountainhead
@Darren, I like just about anything that can be related back to software! ... read »
Darren Walker
Nov 3, 2009 at 3:56 PM
A Moment That Touched Me - The Fountainhead
IMHO the ideal is where self interest is accepted and nurtured, yet where the only common belief is that the best way to achieve your selfish goals is to help others achieve their goals. Like the mos ... read »