Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Rob Brooks-Bilson
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Rob Brooks-Bilson ( @styggiti )

ColdFusion 8 Application Specific Mappings Work With The CFComponent Extends Attribute

By on
Tags:

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

<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

<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

<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

<!--- 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.

Want to use code from this post? Check out the license.

Reader Comments

28 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.

26 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.

28 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.

15,674 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.

30 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.

15,674 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.

30 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.

15,674 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 :)

26 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?

26 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 -

26 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.

26 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.

26 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 (?).

15,674 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.

26 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.

15,674 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?

26 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).

28 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.

15,674 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.

15,674 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??

26 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.

53 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>

11 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?

26 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.

26 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

26 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.

57 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.

57 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.

15,674 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.

5 Comments

I'm a little late to this discussion but ran into it while trying to solve a shared server mapping issue. In the case Ben discusses at the beginning, the mapping works as shown, but what if I were using a App.cfc in a subdirectory off of /app/, say for an admin secure section and still want my cfc's in /com off the root? I guess I'm stuck using mappings from CF Admin and not App.cfc mappings?

I dont' see how I could set the /cfc from App.cfc in the admin sections. The code as shown would add /app/ to the mapping.

root/app/admin/Application.cfc
root/app/Application.cfc
root/cfc/allmysharedcfc's

root/app2/Application.cfc
root/app2/admin/Application.cfc
use same /cfc/allmysharedcfc's

Doug

15,674 Comments

@DougS,

I am not sure I understand what your issue is. Even if you have an application coming off the primary app, the mapping to the component directory would still work, so long as the sub-app defined the appropriate mappings.

5 Comments

I guess I wasn't very clear. Nothing new there my wife says ;>) My point is that using your example directory setup..

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

and <cfset THIS.Mappings[ "/com" ] = (
REReplace(
GetDirectoryFromPath( GetCurrentTemplatePath() ),
"[^\\/]+[\\/]$",
"",
"one"
) &
"com\"
) />

works fine IF you are in the /app/ directory and want to set the mapping to wwwroot/com.
But if you move the Application.cfc to /app/secure/, it sets the mapping to /app/com. How would I go about setting the mapping in Application.cfc from the /app/secure/ directory, or any sub-directory to map to wwwroot/com? I want to always map to the /com in the root directory using Application.cfc, so the cfc's can be used by several applications.

30 Comments

Doug,

In my application.cfc, I'lll often set the base directory before setting up my mappings. Usually something like this:
this.baseDirectory = replaceNoCase(replace(getCurrentTemplatePath(), "\", "/", "all"), "app/secure/Application.cfc", "");

The inner replace ensures all the slashes are forward, to make the outer replace work cross platform. Then, for the mappings:
this.mappings["\com"] = this.baseDirectory & "com";

15,674 Comments

@DougS,

Ahhh, I see what you're saing. Yeah, you'd have to go up an additional directory when setting up your mapping path. @Ryan has a good pointer there in getting the root-directory first and then building your mappings off of that.

5 Comments

Thanks guys. I just tried Ryan's suggestion and that works for me. I've always thought a detailed blog or writeup of "mappings" would be a really good idea and a brief discussion with some people at RIAUnleashed Boston confirmed to me that others are having some sort of problem with mappings. There are snippets of good blogs around discussing certain aspects of mapping, but no really good detailed discussion covering mapping on shared servers, dedicated servers, etc. all in one place.

Thanks Ben and Ryan.

Doug

15,674 Comments

@DougS,

I am not sure the type of server should matter (shared, dedicated, etc.) as the only thing that is important is file paths. The above technique should work well in any situation, as far as I know.

I was up at RIAUnleashed as well; it was a good conference!

6 Comments

G'day, Ben. Long-time reader, first-time poster. Sorry to be dragging this up after so long, but I haven't been able to find a definitive "yes, it's possible"/"no, it's not possible" answer anywhere (and I thought you might be able to help).

Can a per-application mapping be used when extending the Application.cfc? Example, I've got the following:

{1}/webroot/site-a/Application.cfc
{2}/webroot/site-a/ApplicationProxy.cfc
{3}/webroot/site-a/subdir/Application.cfc

I declare a per-app mapping in Application.cfc{1}
that points to the directory 'site-a' (the site root, so to speak). I call the mapping 'siteroot'. ApplicationProxy.cfc{2} extends Application.cfc{1} (no problems there). I then try to have /subdir/Application.cfc{3} extend ApplicationProxy.cfc{2} using:

extends="siteroot.ApplicationProxy"

My experience so far suggests this is not possible. It works, obviously, if the 'siteroot' mapping is created through the CFAdmin.

If I've done a bad job at explaining things, here's a guy trying for the same outcome: http://stackoverflow.com/questions/307423/extending-application-cfc-in-a-subdirectory

I should also mention I'm on CF8.

Thanks in advance, Ben.

15,674 Comments

@Nic,

The ApplicationProxy.cfc approach does work (this approach was suggested by Sean Corfield a while back). However, Application.cfc (in any directory) cannot take advantage of any per-application mapping as the mappings have *not* yet been defined at the time the Application.cfc is instantiated.

Now, you can use the ApplicationProxy.cfc approach IF you don't use mappings (at least not mappings defined in the same app - CFAdmin-based mappings are fine). The problem with this is that it makes the path to the root App a bit more of a pain to get, especially if the path changes from your local Dev environment to the live Production environment.

If you want to get *creative*, you can do this with CFInclude. Too much to explain here - I'll try to post about this on Monday.

6 Comments

Thanks for replying, Ben; your explanation makes perfect sense.

I'm now faced with my Monday-morning dilemma for the week: leave our structure as is and continue to rely on the CFAdmin for mappings, OR, flatten our Application.cfc heirarchy (moving the overridden methods from the subs to the parent), and do away with CFAdmin-based mappings altogether. (sigh) I need a coffee...

33 Comments

Hi,

Just need a small advise...

If I have a site by the name "Site".
Components are in the components folder like this:

Site\components,

then in cf8, I need to create a mapping to access the cfcs in components folder....

but in cf9, we can directly access using the notation "components." without creating any application specific mappings for this particular folder....

Please do correct me if I am wrong.........

6 Comments

@Viky,

You can access your 'components' directory using dot-notation in both CF8 and CF9 without having to create a mapping, provided your path to the directory always starts from the web-root.

So, if your site structure looks like this: http://yoursite/components/yourCFC.cfc

...you can instantiate yourCFC.cfc using 'components.yourCFC' (this works right back to CF7).

One reason you might want to create a mapping is if your site sits off the web-root when running under different environments. For example, if you have a testing server that has the following structure:

http://testingserver/yoursite/components/yourCFC.cfc

...you'd always need to change your code from 'components.yourCFC' to 'yoursite.components.yourCFC' (and vice-versa) when migrating your site between servers.

Creating a mapping on both servers means your code can always stay as 'yourmapping.yourCFC', where 'yourmapping' points to /components/ on one server, and /yoursite/components/ on the testing server.

I hope I've made sense :)

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel