Skip to main content
Participant
February 5, 2022
Answered

Application datasource Property not accessible in CFM Template

  • February 5, 2022
  • 1 reply
  • 168 views

The following is running on a ColdFusion 2018 server (in the event this is a version-specific issue).

 

I'm setting the Application datasource property in the onApplicationStart() LifeCycle Handler, but the datasource property isn't accessible in a CFM Template.

 

I'm thinking it may have something to do with how the this Scope is handled inside the onApplicationStart() method, but I'm not certain. I tried setting the datasource property using this.datasource as well as Application.datasource, but it's not accessible in the CFM Template either way.

 

Application.cfc

// The usual App config stuff here... (omitted for brevity) 

// Instantiate Instance of Java System Object used to set System Env Vars
this.System = createObject("java", "java.lang.System");

// Include Config files
include "resources/config/AppSettings.cfm";
include "resources/config/onApplicationStart.cfm";

 

AppSettings.cfm

if (! isDefined(this.System.getProperty("DB_DSN_CREATED")))
{
    // Code to read values from .env file here ... (omitted for brevity) 

    // Set System Env Vars
    this.System.setProperty("DB_USER_NAME",  "DB USERNAME FROM .ENV FILE HERE");
    this.System.setProperty("DB_USER_PASSWORD", "DB PASSWORD FROM .ENV FILE HERE");
}

 

onApplicationStart.cfm

public void function onApplicationStart() {
if
(! isDefined(this.System.getProperty("DB_DSN_CREATED"))) { this.datasources = {MY_DSN = { PROPS FOR DB CONNECTION HERE }}; // *** NOTE: This is the Property that isn't accessible in the CFM Template // I also tried Application.datasource, but that didn't work either this.datasource = "MY_DSN"; this.System.setProperty("DB_DSN_CREATED", true); }
}

 

db-test.cfm

variables.appInstance = createObject('component', 'Application');

variables.sql = "SQL STATEMENT HERE";
variables.sqlParams = {};

// *** NOTE: variables.appInstance.datasource below isn't accessible 
//           I also tried Application.datasource but that didn't work either
variables.sqlResult = queryExecute(variables.sql, variables.sqlParams, {datasource = variables.appInstance.datasource});

writeDump(variables.sqlResult);

 

Does anyone see what I'm missing? Thanks in advance for any guidance!

    This topic has been closed for replies.
    Correct answer BKBK

     

    Does anyone see what I'm missing? 


    By @Robert_Frenette

     

    I do.

     

    The this scope belongs to the instance of Application.cfc that ColdFusion creates. The instance is created first, before the call to onApplicationStart. That is because ColdFusion invokes the onApplicationStart event-handler, using the instance as the calling object. In other words, the this scope is already available in memory by the time onApplicationStart is invoked.

     

    In addition, your code's definition of this.datasource in onApplicationStart is a problem. For the same reason, ColdFusion will be unaware of this.datasource when it creates an instance of Application.cfc.

     

    A solution: 

    • Define every this-scoped variable in the so-called constructor area of Application.cfc. That is, in the area between component {} and the event-handlers.
      /* Application.cfc */
      
      // The usual App config stuff here... (omitted for brevity) 
      
      // Include Config files
      include "resources/config/AppSettings.cfm";
      
       ​
      /* AppSettings.cfm */
      this.datasources = {MY_DSN = { PROPS FOR DB CONNECTION HERE }};
      
      /* 
      If you define the datasource like this in Application.cfc,
      it means MY_DSN is the default datasource for the application.
      It will then be available throughout the application.
      Which means, you may then omit it from queries.
      */
      this.datasource = "MY_DSN";
      
      // Set System Env Vars
      /* 
      What you do here (defining and exposing credentials in code as runtime variables) 
      is bad practice.
      */
      // this.System.setProperty("DB_USER_NAME",  "DB USERNAME FROM .ENV FILE HERE");
      // this.System.setProperty("DB_USER_PASSWORD", "DB PASSWORD FROM .ENV FILE HERE");
      
      /* 
      If you insist on using environmental variables, then do it, 
      for example, like this:
          - Following the procedure for setting environmental variables
          in your Operating System, manually set the 2 name-value pairs:
          
          ("DB_USER_NAME","DB_USER_NAME_VALUE") 
          ("DB_USER_PASSWORD","DB_USER_PASSWORD_VALUE")
          
          as environmental variables
       */
      
      this.DB_DSN_CREATED=false;
      
      // System 
      this.System = createObject("java", "java.lang.System");
      
      // Get System Env Vars: a struct with case-sensitive keys
      this.environmentVars = this.System.getenv();
      
      // Get the DB username and password (without exposing them in code)
      // Remember: the keys are case-sensitive
      if (structKeyExists(this.environmentVars,"DB_USER_NAME") && 
          structKeyExists(this.environmentVars,"DB_USER_PASSWORD") ) {
      	this.db_username=environmentVars["DB_USER_NAME"];
      	this.db_user_password=environmentVars["DB_USER_PASSWORD"];
      	
      	this.DB_DSN_CREATED=true;
      }
    • Ensure that onApplicationStart has just one responsibility. Namely, to start the application. It should not be weighed down with the extra responsibility of setting system variables.
      Furthermore, it is best-practice for onApplicationStart to return true if the application may start, and false if it may not.
      /* Application.cfc */
      component {
          public boolean function onApplicationStart() {
      		if (this.DB_DSN_CREATED) {
      			/* 
      			Transfer credentials to application scope, making
      			them available anywhere in the application
      			*/
      			application.db_username=this.db_username;
      			application.db_user_password=this.db_user_password;
      			
      			// the application may start
       			return true;
       		} else {
       			// the application may not start
       			return false;
       		}
          }
      }​

     

    1 reply

    BKBK
    Community Expert
    BKBKCommunity ExpertCorrect answer
    Community Expert
    February 6, 2022

     

    Does anyone see what I'm missing? 


    By @Robert_Frenette

     

    I do.

     

    The this scope belongs to the instance of Application.cfc that ColdFusion creates. The instance is created first, before the call to onApplicationStart. That is because ColdFusion invokes the onApplicationStart event-handler, using the instance as the calling object. In other words, the this scope is already available in memory by the time onApplicationStart is invoked.

     

    In addition, your code's definition of this.datasource in onApplicationStart is a problem. For the same reason, ColdFusion will be unaware of this.datasource when it creates an instance of Application.cfc.

     

    A solution: 

    • Define every this-scoped variable in the so-called constructor area of Application.cfc. That is, in the area between component {} and the event-handlers.
      /* Application.cfc */
      
      // The usual App config stuff here... (omitted for brevity) 
      
      // Include Config files
      include "resources/config/AppSettings.cfm";
      
       ​
      /* AppSettings.cfm */
      this.datasources = {MY_DSN = { PROPS FOR DB CONNECTION HERE }};
      
      /* 
      If you define the datasource like this in Application.cfc,
      it means MY_DSN is the default datasource for the application.
      It will then be available throughout the application.
      Which means, you may then omit it from queries.
      */
      this.datasource = "MY_DSN";
      
      // Set System Env Vars
      /* 
      What you do here (defining and exposing credentials in code as runtime variables) 
      is bad practice.
      */
      // this.System.setProperty("DB_USER_NAME",  "DB USERNAME FROM .ENV FILE HERE");
      // this.System.setProperty("DB_USER_PASSWORD", "DB PASSWORD FROM .ENV FILE HERE");
      
      /* 
      If you insist on using environmental variables, then do it, 
      for example, like this:
          - Following the procedure for setting environmental variables
          in your Operating System, manually set the 2 name-value pairs:
          
          ("DB_USER_NAME","DB_USER_NAME_VALUE") 
          ("DB_USER_PASSWORD","DB_USER_PASSWORD_VALUE")
          
          as environmental variables
       */
      
      this.DB_DSN_CREATED=false;
      
      // System 
      this.System = createObject("java", "java.lang.System");
      
      // Get System Env Vars: a struct with case-sensitive keys
      this.environmentVars = this.System.getenv();
      
      // Get the DB username and password (without exposing them in code)
      // Remember: the keys are case-sensitive
      if (structKeyExists(this.environmentVars,"DB_USER_NAME") && 
          structKeyExists(this.environmentVars,"DB_USER_PASSWORD") ) {
      	this.db_username=environmentVars["DB_USER_NAME"];
      	this.db_user_password=environmentVars["DB_USER_PASSWORD"];
      	
      	this.DB_DSN_CREATED=true;
      }
    • Ensure that onApplicationStart has just one responsibility. Namely, to start the application. It should not be weighed down with the extra responsibility of setting system variables.
      Furthermore, it is best-practice for onApplicationStart to return true if the application may start, and false if it may not.
      /* Application.cfc */
      component {
          public boolean function onApplicationStart() {
      		if (this.DB_DSN_CREATED) {
      			/* 
      			Transfer credentials to application scope, making
      			them available anywhere in the application
      			*/
      			application.db_username=this.db_username;
      			application.db_user_password=this.db_user_password;
      			
      			// the application may start
       			return true;
       		} else {
       			// the application may not start
       			return false;
       		}
          }
      }​

     

    Participant
    February 6, 2022

    Thanks so much for the detailed answer / explanation! This makes perfect sense to me now. I've been away from CF Development for over 20 years, and wanted to use a more "modern" implementation in my approach. As with most things, there's still a lot for me to learn. I do appreciate the reply / info!