Saturday, September 02, 2006

ColdFusion ColdSpring - Evaluate variables in ColdSpring.xml


As part of my new implementation of ModelGlue:Unity, I found that I have to place the values of different things in many different places. In this situation, that was the applications datasource name, user ID and password. I dislike having more than one or two places to have to place values, in the event they need to change. (A good security policy for databases is to change those passwords every so often.)

In my ColdSpring's XML config file, I had attempted to place the following:

<bean id="oDatabase" class="com.oDatabase" >
<constructor-arg name="dbDatasource">
<value>#APPLICATION.dbName#</value>
</constructor-arg>
<constructor-arg name="dbUser">
<value>#APPLICATION.dbUser#</value>
</constructor-arg>
<constructor-arg name="dbPass">
<value>#APPLICATION.dbPass#</value>
</constructor-arg>
</bean>

However, this only resulted in a query error along the lines of a datasource not being found with a name of "#APPLICATION.dbName#".

Perhaps my solution to my need is not the best way, since I modified ColdSpring files directly. Perhaps there is even another way, but a couple hours of searching did not reveal the answer to me. I thought "there has to be a way to tell CS to evaluate the XML value entry instead of just seeing it as a literal string."

I opened DefaultXMLBeanFactory.cfc in the /coldspring/beans/ directory and modified the "constructBean" function. There are two places you'll find this line, one in a regular bean function setup, I believe, and the other specifically for init() functions.

<cfinvokeargument name="#argDefs[arg].getArgumentName()#" value="#argDefs[arg].getValue()#"/>

I replaced that line with this block:

<cfset VARIABLES.csTempEval = argDefs[arg].getValue() />
<cfif FindNoCase("~~~", VARIABLES.csTempEval) GT 0>
<cfset VARIABLES.csTempEval = mid(VARIABLES.csTempEval, 4, 999999) />
<cfset VARIABLES.csTempEval = Evaluate(VARIABLES.csTempEval) />
</cfif>
<cfinvokeargument name="#argDefs[arg].getArgumentName()#" value="#VARIABLES.csTempEval#"/>

In my ColdSpring XML config file, I changed the section above to look like this:

<bean id="oDatabase" class="com.oDatabase" >
<constructor-arg name="dbDatasource">
<value>~~~#APPLICATION.dbName#</value>
</constructor-arg>
<constructor-arg name="dbUser">
<value>
~~~#APPLICATION.dbUser#</value>
</constructor-arg>
<constructor-arg name="dbPass">
<value>
~~~#APPLICATION.dbPass#</value>
</constructor-arg>

</bean>

Voila! Now my ColdSpring dynamically evaluates APPLICATION.dbName instead of trying to use it as a literal. It will only do this is the tag has the three tildes (~~~) at the front. Obviously, you can modify it to do it with any character combination you find more appealing. I would avoid anything too crazy, or anything you might actually want as a true value down the road. I considered having the changes look for anything beginning and ending with "#" characters but that would eliminate the ability for more complex combinations, if needed, such as "myTest_v#APPLICATION.version#" where it evaluates to "myTest_v01" or similar.

If anyone out there knows of a more ColdSpring acceptable way (that I was unable to find), please let me know. Of course, I made a backup of the original CFC in the event that someone brings it to my attention!

Will Belden,
September 9, 2006




Friday, September 01, 2006

ColdFusion - Crazy Error

While coding today, I received the following error:
"The system has attempted to use an undefined value, which usually indicates a programming error, either in your code or some system code. Null Pointers are another name for undefined values."
After my initial bafflement, it occurred to me that the problem wasn't where it was saying it was, in a call to the CFC, but rather in the CFC function itself. I had specified a function returntype of "numeric", but had neglected to setup to return anything at all. Even when I modified the call to the function to not accept a return, as in:
myFunctionCall(parm1=value1, parm2=value2) ;
instead of
myRetVal = myFunctionCall(parm1=value1, parm2=value2) ;

... it still didn't work and threw the same error.

Hopefully, if you're having this problem, and search on some of that text, you'll end up here with a search and save yourself some headache!

Will Belden
September 1st, 2006