• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Can you Overwrite this.datasource in a ColdFusion App?

Contributor ,
Aug 25, 2021 Aug 25, 2021

Copy link to clipboard

Copied

We all know the benefits of being able to write `this.datasource` in your Application.cfc. However, does anyone know if there's a way to override or change this value elseware in the app?

 

For example, you might have a configuration CFC or JSON file that would need the App to use a different `this.datasource` value.   It would be great to be able to change this on the fly if needed.

My only theory as of now (untested) would be to create a method in the Application.cfc which would re-assign the datasource dynamically like this:

function setDatasource() {
  // read configuration file...
  this.datasource = 'foo';
}

You could then call this method somewhere in your app using the application scope like this:
`application.setDatasource()`

Any thoughts or input would be most welcome 🙂

Views

644

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 25, 2021 Aug 25, 2021

Copy link to clipboard

Copied

Well, when you refer to "the benefits", they are primarily two: if you do a cfquery/cfstoredproc (or their script equivalents), those use the value in this.datasource if you don't specify one on the cfquery/cfstoredproc...but you can of cours ejust specify one there, and it will use that.

 

Or (second) if you are using orm, it uses the value in this.datasource...unless you override it in this.ormsettings, to be some other dsn. 

 

So can you clarify which of those two (or some other benefit) you are getting, and whether somehow either of the offered alternatives don't work? It just wasn't clear from your question.


/Charlie (troubleshooter, carehart.org)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 25, 2021 Aug 25, 2021

Copy link to clipboard

Copied

Hi Charlie,

In this case, I'm using QB (query builder), but the main benefit would be to have the default datasource defined without having to specify `datasource="foo"` in every query.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 26, 2021 Aug 26, 2021

Copy link to clipboard

Copied

Right, that was the first benefit I listed.

 

So your point is you want to be able to use a "different" dsn at times, but not by specifying it on a specific query. Instead, you want to change the special this scope dsn, for a given need, temporarily.

 

I'll say that could be risky. It could affect more than the request in which you change it, since this is about the properties of the application.cfc.

 

It seems a quandary. I realize you feel your need is justified. I'll leave it for others to offer ideas. You may want to also ask elsewhere, in places I list at cf411.com/cfhelp. 


/Charlie (troubleshooter, carehart.org)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 29, 2021 Aug 29, 2021

Copy link to clipboard

Copied

BKBK_0-1630247548666.png

 

We all know the benefits of being able to write `this.datasource` in your Application.cfc. However, does anyone know if there's a way to override or change this value elseware in the app?


By @Homestar9

 

Any answer to the question will contradict the very benefits you mention. Application.cfc contains functionality common to every page and is included at the beginning of a page. As such it is the bootstrapper of the entire application.

 

That is its power. That power would be lost if any page could one-sidedly change Application.cfc's behaviour in the way you propose.

 

An analogy to illustrate. Suppose the State Department uses Fort Knox as the default storage location for all US embassies around the world. An urgent situation arises at the embassy in Kabul. The only viable solution is immediate, temporary storage in Islamabad. 

 

Imagine this change having the effect of automatically switching the storage location of every US embassy to Islamabad. We don't expect their ColdFusion application to do that, do we?

 

BKBK_1-1630247548668.png

 

My only theory as of now (untested) would be to create a method in the Application.cfc which would re-assign the datasource dynamically like this:
function setDatasource() {
  // read configuration file...
  this.datasource = 'foo';
}

You could then call this method somewhere in your app using the application scope like this:
`application.setDatasource()`

 

That is good CFML, which will run. But it won't solve your problem. The reason follows.

 

The this scope pertains to an instance of a CFC. Before ColdFusion runs any page, it includes an instance of Application.cfc. The this scope pertains to that particular instance, during that particular page request. A new page request generates and uses a new Application.cfc instance. Therefore, the value set in the this scope will not persist from one page request to the next.  

 

One way out would be to make the application.setDatasource()* call wherever and whenever needed. But that might amount to just as much work as adding a datasource attribute to any query that needs one. 

 

If the use-case you describe - having the freedom to dynamically change the datasource for the entire application - is important to your application, then you will have to make some code changes. To make the change just once, you  could implement it as follows:

  1. Delete the this.datasource line from Application.cfc. Replace it with application.datasourceUsed="defaultDSN"; in onRequestStart.
  2. Define the datasource attribute for every tag or function that needs it. Give the attribute the value #application.datasourceUsed#.
  3. From here onwards, any change in the value of application.datasourceUsed anywhere, at any time, will instantly be in effect throughout the application.

 

-----------------------------------------------

*Appendix: Alternative to your setDatasource idea

 

<!--- On the CFM page where required --->
applicationInstance = createObject("component","path.to.Application");
applicationInstance.datasource="foo";

 


 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 29, 2021 Aug 29, 2021

Copy link to clipboard

Copied

Great reply @BKBK and I liked your Fort Knox analogy.   It makes complete sense why my code example wouldn't work because this.datasource would be reset on every subsequent request.

I didn't want to get into the details of why I wanted to change the default datasource for simplicity sake. However, my goal is to utilize different datasources depending on some type of environment detection, similar to how you can have various configurations load in a ColdBox app depending on whether you are in production, development, or staging.  

If you were running a full modern CFML app with Commandbox servers, you could probably use your .env file to specify different datasources depending on the environment.  However, if your production and staging servers relied n more "old school" IIS installations without Commandbox, it can be a little trickier to accomplish the same task.

In a perfect world, I'd love to have Coldbox's /config/coldbox.cfc file change the applications `this.datasource` value based on the environment.  In the past, when I did specify a `datasource` value in all my queries, I had my DAOs check the Coldbox config for the proper datasource name.  Your code examples have given me some ideas for exploring the concept further.

Another possibility would be to bake the environment detection using regular expression checks against the host name into Application.cfc - however IMO it would be much cooler if the DSN swap could be done using the config/coldbox.cfc file.  Either way, thanks for your input!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 30, 2021 Aug 30, 2021

Copy link to clipboard

Copied

Great description of the requirements. I now understand the problem. Are prod, staging, dev separate instances?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Aug 30, 2021 Aug 30, 2021

Copy link to clipboard

Copied

In a perfect world, yes all three environments would be on different instances.  However, with some clients production and staging reside on the same CF server. In the latter case, I might have production/staging databases on the same server with a different DSN like this:
production: 'mydatabase'
staging: 'mydatabase_staging'

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

To return to your original question:

 

Can you Overwrite this.datasource in a ColdFusion App?


Answered already. The short answer is: no.

The long answer is: yes, you can overwrite this.datasource for the current page scope. Even though you cannot overwrite it at the application level.

 

I think the concept of the Application file was created especially to solve problems like these.

 

Case 1:

    Production, Staging and Development environment each on its own distinct instance 
Solution 1:  

    Treat each as a separate application, having its own Application.cfc. In that file, define this.datasource for that environment,  and you're done


Case 2:

    A combination of Production, Staging, Development environments share the same ColdFusion instance
Solution 2:  

    Package each environment within its own folder. Its own Application.cfc should be in that folder. Then follows essentially the same solution as for Case 1  

 

Case 3:

    A combination of Production, Staging, Development environments sharing the same application scope (a design that I would strongly discourage)

Solution 3:  

    Just open the Application.cfc file in a text editor, change this.datasource to the new value and save.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 31, 2021 Aug 31, 2021

Copy link to clipboard

Copied

There are other ways I can think of. Let's suppose you wish to overwrite this.name and/or this.datasource programmatically (as opposed to manually). Then you could proceed as follows:

 

  1. Create a template called Application.dot.cfc. Create placeholders in it for this.name and this.datasource.
  2. Create a CFC, AppPropertyChanger.cfc. It contains a function that will read the template in 1., replace this.name and this.datasource with whichever value you want, then write the result to file as Application.cfc.
  3. To "overwrite" Application.cfc on the fly, from any where in the application, it suffices to create an object of AppPropertyChanger.cfc and call its change-function.

 

The code below illustrates the idea. For simplicity, the files here share the same directory.

 

Application.dot.cfc (template for Application.cfc)

 

component  
{ 
        // Use square brackets to identify the placeholders
	this.name="[AppNamePlaceholder]";
	this.datasource="[DSNPlaceholder]";
	// etc. etc. 
}

 

 

AppPropertyChanger.cfc

 

component output="false" {
	
	/* My defaults are appName="Production" and datasource="prod" */
	void function changeApplicationProperty (string appName="Production", string datasource="prod") {
		
	 	// Read the template for the Application file
	 	 applicationTemplate=fileRead(expandpath("Application.dot.cfc"), "UTF-8");
	 	 
	 	  /* Set the value of argument.appName as the value of 'this.name'' */
	 	 applicationTemplate=reReplaceNoCase(applicationTemplate,"this\.name=""\[(.*?)\]""","this.name=""#arguments.appName#""","all");
	 	
	 	 /* Set the value of argument.datasource as the value of 'this.datasource'' */
	 	 applicationTemplate=reReplaceNoCase(applicationTemplate,"this\.datasource=""\[(.*?)\]""","this.datasource=""#arguments.datasource#""","all");
	 	
	 	 filewrite(expandpath("Application.cfc"), applicationTemplate, "UTF-8");
	 	
	 	 writeLog(type="information", file="ApplicationNameAndDSNChange", text="Application name changed to #arguments.appName#. Datasource changed to #arguments.datasource#");
		
	 }
}  

 

 

changeAppProps.cfm (represents a page you can call anywhere in the application to "overwrite" Application.cfc)

 

<cfscript>
	changer=createobject("component","AppPropertyChanger")
	changer.changeApplicationProperty("Staging", "MySQLStagingDSN");
	
	/*
	changer.changeApplicationProperty("Development", "MSSQLDevDSN");
	*/
	
	/*
	changer.changeApplicationProperty("Prod", "OracleDSN");
	*/	 
</cfscript>

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Sep 03, 2021 Sep 03, 2021

Copy link to clipboard

Copied

@Homestar9, there is an even neater solution, along object-oriented lines:

 

1) Create a base (that is, parent) application file. As it is the base component its contents are general and apply to all sub-applications. Call this file, ApplicationBase.cfc.

2) Sub-applications only have to extend ApplicationBase.cfc, and we're done. Thus, the Application.cfc of each sub-application may now define its own datasource!

 

It goes like this:

 

component extends="ApplicationBase" {
    this.name = "ChildAppName"; /* parent's value="AppName" */
    this.datasource = "childDSN"; /* parent's value="DSN" */
    this.sessiontimeout=createtimespan(0,0,20,0); /* parent's value=30 minutes */
    this.applicationtimeout=createtimespan(1,0,0,0); /* parent's value=2 days */

    boolean function onApplicationStart() {
        application.config="xyz"; /* parent's value="abc" */
        return true;
    }
}

}

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 30, 2021 Aug 30, 2021

Copy link to clipboard

Copied

I'm a little confused, having read through the whole thread. Are you saying you want to be able to change this within different copies of your application (f/e dev, stage & prod) or are you saying you want one part of your application to have a different value of another part of the same application?

 

If it's the former, you can easily do this by having some external set of information that the application reads from wherever, then uses to set the values you want within the Application and/or this scopes. For example, we used to use INI files for this. You'd place an INI file in your dev location with dev values, you'd place another within your prod location with prod values, etc. Then, on startup, your application would read from its own INI file and set all of these values as needed. You wouldn't include the INI file in the application's code repo, you'd have another repo for your infrastructure-as-code stuff and the INI file would go in there. This approach works pretty well.

 

If it's the latter, well, you're saying something that's kind of weird: "I want this minimal time-saving thing that works automatically to not work in this specific case". If that's the case, maybe this.datasource isn't the best tool for your job. Just set the datasource attribute for all your queries.

 

Dave Watts, Eidolon LLC

Dave Watts, Eidolon LLC

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Sep 03, 2021 Sep 03, 2021

Copy link to clipboard

Copied

LATEST

@Dave Watts yes, you are correct. My intent was to use some type of environment detection so that I could set `this.datasource` dynamically.  I like the INI example and it's similar to using an .env file from within Commandbox.

The trick is that `this.datasource` gets reset on every request as @BKBK indicated so I would need to detect the environment on every request as well.  I'm just thinking out loud here, but maybe I could read the INI file and use it to set some `application` scope variables which could contain the proper value for `this.datasource`.

Example (untested code):

 

 

// application.cfc

param name="application.dsn" default="productionDsn";

this.datasource = application.dsn;

function onApplicationStart() {
 // ... read ini/env file
 application.dsn = "..." // set the dsn to the result of environment detection
}

 

 



@BKBK, your BaseApplication.cfc example is interesting and would work well for multiple sub-applications inside of some type of master application.  However, my challenge was to see if I could use `this.datasource` dynamically based on the app's environment (dev/production/staging/etc).  Or perhaps I didn't understand the example properly - I'm still waiting for my morning coffee to kick in. 😉

Side note, I like the AppPropertyChanger.cfc sample code. While, it's not exactly what I'm trying to do in my particular challenge, it's a very creative approach! 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources
Documentation