Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ilya Fedotov
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ilya Fedotov@ilyafedotov )

Namespacing Components With Per-Application Mappings In ColdFusion

By on
Tags:

In my ColdFusion applications, I use a lot of components. But, these components are, for the most part, application-specific and live seamlessly alongside my application's custom mappings. Recently, however, I've been thinking about writing a "module" (ie, a set of related components) that I might want to use in multiple applications. And, it wasn't immediately obvious to me how I might define my ColdFusion per-application mappings in order to avoid conflicts with an application's existing component tree. As such, I wanted to experiment with using per-application mappings to create "namespaces" for shared ColdFusion components.

In my mind, a "namespace" is a path-prefix that uniquely identifies a set of ColdFusion components. I'm not a Java developer; but, I believe that a common practice in the Java world is to define namespaces using this convention:

{ inverse-domain }.{ product }.{ module }

Meaning, the "Acme" corporation might use the following namespace for their analytics package:

com.acme.data.analytics

... and the "Cyberdyne Systems" corporation might use the following namespace for their analytics package:

systems.cyberdyne.data.analytics

This way, a developer could theoretically import both analytics packages without causing namespace conflicts.

If I wanted to create a product called "Strangler" (as in the Strangler pattern), I might want to use the namespace:

com.bennadel.strangler

Doing this in ColdFusion requires us to think a bit about how component paths map onto folder paths. Normally, a component path is the same as the folder path; except, with the slashes (/) replaced by dots (.). So, a ColdFusion component that resides at:

app/lib/MyComponent.cfc

... can be instantiated using the given dot-path:

new app.lib.MyComponent()

When you want to use a dot-path that doesn't correspond to a physical folder path, you can use a per-application custom mapping to define a dot-path prefix. Historically, when I've done this, my "prefix" has been a single token, like lib or vendor or extensions. But, I don't believe there is any technical reason why we can't create a custom mapping that includes multiple, non-existent folders.

Going back to my product namespace for a second:

com.bennadel.strangler

If I were to consume this dot-path without a per-application custom mapping, I would have to have the physical folder path:

com/bennadel/strangler

But, if I only want to use this as an internal namespace and not as a file-system requirement, I can set up a per-application custom mapping that maps /com/bennadel/strangler onto an existing directory within my ColdFusion application.

To see this in action, let's create an Application.cfc that maps the non-existent directory, /com/bennadel/kablamo, onto the current directory. Then, let's try to use the dot-path, com.bennadel.kablamo to instantiate ColdFusion components:

component
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define the application settings.
	this.name = "CompoundPathMappingTest";
	this.applicationTimeout = createTimeSpan( 0, 1, 0, 0 );

	// When referencing ColdFusion components, paths are delimited with "." instead of
	// with "/". However, those paths ultimately map to folder structures. As such, if we
	// want to create a "namespace" (so to speak) for a set of components, we have to
	// create a ColdFusion mapping that lays-out the virtual file-based paths for our
	// namespace.
	// --
	// CAUTION: The mapping keys cannot end in a slash - this will break Adobe ColdFusion.
	// But, appears to work fine in Lucee CFML.
	this.mappings = {
		"/com/bennadel/kablamo": getDirectoryFromPath( getCurrentTemplatePath() )
	};

	// ---
	// PUBLIC METHODS.
	// ---

	/**
	* I handle the execution of the requested script.
	*/
	public void function onRequest() {

		// Our namespace - "com.bennadel.kablamo" - is using the "/com/bennadel/kablamo"
		// per-application custom mapping.
		var self = new com.bennadel.kablamo.lib.SubClass()
			.getSelf()
		;

		writeDump(
			label = "Compound Path Mapping Test",
			var = self
		);

	}

}

As you can see in my onRequest() event-handler, I'm using the namespace com.bennadel.kablamo to instantiate a ColdFusion component that lives in my local ./lib directory. This component is also using the same namespace to defined its structure:

/**
* NOTE: Using the namespace in "extends".
*/
component
	extends = "com.bennadel.kablamo.lib.BaseClass"
	{

	this.isSubClass = true;

	/**
	* NOTE: Using the namespace in the return type signature.
	*/
	public com.bennadel.kablamo.lib.SubClass function getSelf() {

		return( this );

	}

}

As you can see, this ColdFusion component is using the com.bennadel.kablamo namespace in both its extends attribute as well as in the return type of one of its functions.

Now, if we run this application in either Adobe ColdFusion 2021 or Lucee CFML 5.3.8.201, we get the following output:

ColdFusion component that was successfully instantiated using a compound namespace.

This output demonstrates a number of key points:

  • We were able to use per-application mappings to create the namespace com.bennadel.kablamo on top of non-existing folders.

  • We were able to use that namespace in the component's extends attribute.

  • We were able to use that namespace in the component's method signatures.

I don't believe I've ever considered using a per-application custom mapping that includes a totally non-existent folder path! Normally, I'm just creating short aliases to existing folders. But, it's awesome that this actually works; and, works in both Adobe ColdFusion and Lucee CFML.

A Note on the import Tag in ColdFusion

The downside of namespaces in ColdFusion is that the component dot-paths become pretty verbose. One way to get around these long paths is to import a component directory; and then reference the components without paths. Unfortunately, the import tag is a compile time directive, not a runtime directive. As such, we can't use per-application mappings in our import paths.

If you define your custom mappings in the ColdFusion admin, they will work with the import tag (or so says the documentation). However, that makes it harder to keep your custom paths in source control. Which is why I'm opting for the per-application, albeit more verbose, custom mapping solution.

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

Reader Comments

Post A Comment — I'd Love To Hear From You!

Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.