I have to create a web service in ColdFusion. I have tried the below 2 ways. Can anyone help me to find which one is the best way (Both Performance and security enhancement basis)
First Way
Created a cfm page like below;
<cfset result = StructNew() />
<cfset resultStruct = StructNew() />
<cfset validStruct = StructNew() />
<cfset VARIABLES.Sample = CreateObject("component","main.webservice.Sample")>
<cfif NOT isDefined("URL.method")>
<cfset result['status'] = false >
<cfset result['message'] = 'method is missing' />
<cfoutput>#SerializeJSON(result)#</cfoutput>
<cfabort>
</cfif>
<cfswitch expression="#URL.method#">
<cfcase value="get">
<cfset fieldList = "name">
<cfset validStruct = validate(fieldList) />
<cfif validStruct['status']>
<cfset resultStruct = VARIABLES.Sample.get(argumentCollection=URL) />
</cfif>
<cfoutput>#SerializeJSON(resultStruct)#</cfoutput>
<cfbreak>
</cfcase>
<cfcase value="put">
<cfset fieldList = "name,value">
<cfset validStruct = validate(fieldList) />
<cfif validStruct['status']>
<cfset resultStruct = VARIABLES.Sample.put(argumentCollection=URL) />
</cfif>
<cfoutput>#SerializeJSON(resultStruct)#</cfoutput>
<cfbreak>
</cfcase>
<cfdefaultcase>
<cfset result['status'] = false >
<cfset result['message'] = 'Not a valid method' />
<cfoutput>#SerializeJSON(result)#</cfoutput>
<cfbreak>
</cfdefaultcase>
</cfswitch>
And Created a cfc named 'Sample' under webservice folder and called like above.
WebService URL
http://test.com/webservice/Sample.cfm?method=get&name=test
Second Way
Called directly from the CFC Sample
Sample.CFC
<cfcomponent displayname="Sample" hint="Sample WebService" output="false">
<cffunction name="get" access="remote" returntype="struct" returnformat="json">
<cfargument name="name" required="true" type="string" >
<cfreturn StructNew() />
</cffunction>
<cffunction name="put" access="remote" returntype="struct" returnformat="json">
<cfargument name="name" required="true" type="string" >
<cfargument name="value" required="true" type="string" >
<cfreturn StructNew() />
</cffunction>
</cfcomponent>
WebService URL
http://test.com/webservice/Sample.CFC?method=get&name=test
The second method is the standard way to do WebServices in CFML. Along with the functionality, you are seeking you get standards based WSDL returns and definitions. It's a case of rebuilding the wheel. I'm sure the underlying CF code for ws could be optimized, but it's pretty good as is and has been field-tested by millions.
I would suggest setting up RESTful web services in ColdFusion. Here is an excellent article to get you started.
There's also Taffy which claims to make it simpler, although I have not used it.
I'm trying to use autocomplete with a field with commas in it. When I type the comma it will ignore it, and won't return anything. So far I have this:
index.cfm
<!--- A simple form for auto suggest --->
<cfform action="autosuggest.cfm" method="post">
Artist:
<cfinput type="text" name="artist" size="50" autosuggest="cfc:autosuggest.findartist({cfautosuggestvalue})" autosuggestminlength="4" maxresultsdisplayed="5" /><br /><br />
</cfform>
autosuggest.cfc
<cfcomponent output="false">
<!--- Lookup used for auto suggest --->
<cffunction name="findartist" access="remote" returntype="string">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var local = {} />
<!--- Query Location Table --->
<cfquery name="local.query" datasource="#application.datasource#" >
select DISTINCT artist
from items
where artist like <cfqueryparam cfsqltype="cf_sql_varchar" value="#ucase(arguments.search)#%" />
order by artist
</cfquery>
<!--- And return it as a List --->
<cfreturn valueList(local.query.artist)>
</cffunction>
</cfcomponent>
When I try to search for example Brown,James it doesn't return anything. What do I need to put in it to return results with commas.
Thanks
One option is to have your function return an array, instead of a string. Then delimiters are not an issue.
<cffunction name="findartist" access="remote" returntype="array">
...
<cfreturn listToArray(valueList(local.query.artist, chr(30)), chr(30))>
</cffunction>
Update:
As Raymond pointed out, the only sure-fire way to avoid delimiter issues on the CF side is not to use them. Instead loop through the query to build the array, ie:
<cffunction name="findartist" access="remote" returntype="array">
...
<cfset local.arr = []>
<cfloop query="local.query">
<cfset arrayAppend(local.arr, local.query.artist)>
</cfloop>
<cfreturn local.arr>
</cffunction>
I am getting error saying "Error invoking CFC componentname.cfc : The method onRequest was not found in component ..\filepath\Application.cfc".
This works fine with CF8 but not CF9 with same code. Both environments use Apache and Fusebox 5. The method onRequest exists in application.cfc.
Please help.
CODE FOR CFC BIND:
<cfselect name="officeNum" id="officeNum" bind="cfc: userYODSU.GetOfficeList({empID},{fy})" display="officeNameTxt" value="officeNum" bindOnLoad="true"><option value="">---</option></cfselect>
Code in APPLICATION.CFC
<cfcomponent output="false">
<cfprocessingdirective suppresswhitespace="true">
<!---
Copyright 2006-2007 TeraTech, Inc. http://teratech.com/
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--->
<!--- code that must execute at the very start of every single request --->
<!--- FB5: allow "" default - FB41 required this variable: --->
<cfparam name="variables.FUSEBOX_APPLICATION_PATH" default="" />
<!--- FB5: application key - FB41 always uses 'fusebox': --->
<cfparam name="variables.FUSEBOX_APPLICATION_KEY" default="fusebox" />
<!--- FB51: allow application to be included from other directories: --->
<cfparam name="variables.FUSEBOX_CALLER_PATH" default="#replace(getDirectoryFromPath(getBaseTemplatePath()),"\","/","all")#" />
<!--- FB55: easy way to override fusebox.xml parameters programmatically: --->
<cfparam name="variables.FUSEBOX_PARAMETERS" default="#structNew()#" />
<cfparam name="variables.attributes" default="#structNew()#" />
<cfset structAppend(attributes,URL,true) />
<cfset structAppend(attributes,form,true) />
<!--- FB5: uses request.__fusebox for internal tracking of compiler / runtime operations: --->
<cfset request.__fusebox = structNew() />
<!--- FB55: bleeding variables scope back and forth between fusebox5.cfm and this CFC for backward compatibility --->
<cfset variables.exposed = structNew() />
<!--- FB55: scaffolder integration --->
<cfif structKeyExists(attributes,"scaffolding.go")>
<!--- if we're not already executing the scaffolder, branch to it --->
<cfif findNoCase("/scaffolder/",CGI.SCRIPT_NAME) eq 0>
<cftry>
<cfinclude template="/scaffolder/manager.cfm" />
<cfcatch type="missinginclude">
<cfif structKeyExists(attributes,"scaffolding.debug")>
<cfrethrow />
</cfif>
<cfthrow type="fusebox.noScaffolder" message="Scaffolder not found" detail="You requested the scaffolder but /scaffolder/index.cfm does not exist." />
</cfcatch>
</cftry>
</cfif>
</cfif>
<cffunction name="bleed" returntype="any" access="public" output="false">
<cfargument name="outerVariables" type="any" required="true" />
<cfset variables.exposed = arguments.outerVariables />
<!--- expose known variables: --->
<cfset variables.exposed.attributes = variables.attributes />
<cfreturn this />
</cffunction>
<cffunction name="onApplicationStart" output="false">
<!--- FB5: myFusebox is an object but has FB41-compatible public properties --->
<cfset variables.myFusebox = createObject("component","myFusebox").init(variables.FUSEBOX_APPLICATION_KEY,variables.attributes,variables) />
<!--- expose known variables: --->
<cfset variables.exposed.myFusebox = variables.myFusebox />
<!--- FB55: guarantee XFA struct exists --->
<cfparam name="variables.xfa" default="#structNew()#" />
<!--- FB51: ticket 164: add OO synonym for attributes scope --->
<cfparam name="variables.event" default="#createObject('component','fuseboxEvent').init(attributes,xfa,myFusebox)#" />
<!--- expose known variables: --->
<cfset variables.exposed.xfa = variables.xfa />
<cfset variables.exposed.event = variables.event />
<cfset loadFusebox() />
</cffunction>
<cffunction name="onSessionStart" output="false">
</cffunction>
<cffunction name="onRequestStart" output="false">
<cfargument name="targetPage" type="string" required="true" />
<cfset var doCompile = true />
<!--- ensure CFC / Web Service / Flex Remoting calls are not intercepted --->
<cfif right(arguments.targetPage,4) is ".cfc">
<cfset doCompile = false />
<cfset structDelete(variables,"onRequest") />
<cfset structDelete(this,"onRequest") />
</cfif>
<!--- onApplicationStart() may create this (on first request) --->
<cfif not structKeyExists(variables,"myFusebox")>
<!--- FB5: myFusebox is an object but has FB41-compatible public properties --->
<cfset variables.myFusebox = createObject("component","myFusebox").init(variables.FUSEBOX_APPLICATION_KEY,variables.attributes,variables) />
<!--- expose known variables: --->
<cfset variables.exposed.myFusebox = variables.myFusebox />
</cfif>
<!--- FB55: guarantee XFA struct exists --->
<cfparam name="variables.xfa" default="#structNew()#" />
<!--- FB51: ticket 164: add OO synonym for attributes scope --->
<cfparam name="variables.event" default="#createObject('component','fuseboxEvent').init(variables.attributes,variables.xfa,variables.myFusebox)#" />
<!--- expose known variables: --->
<cfset variables.exposed.xfa = variables.xfa />
<cfset variables.exposed.event = variables.event />
<cfif variables.myFusebox.parameters.load>
<cflock name="#application.ApplicationName#_fusebox_#variables.FUSEBOX_APPLICATION_KEY#" type="exclusive" timeout="300">
<cfif variables.myFusebox.parameters.load>
<cfset loadFusebox() />
<cfelse>
<!--- _fba should *not* be exposed --->
<cfset _fba = application[variables.FUSEBOX_APPLICATION_KEY] />
<!--- fix attributes precedence --->
<cfif _fba.precedenceFormOrURL is "URL">
<cfset structAppend(variables.attributes,URL,true) />
</cfif>
<!--- set the default fuseaction if necessary --->
<cfif not structKeyExists(variables.attributes,_fba.fuseactionVariable) or trim(variables.attributes[_fba.fuseactionVariable]) is "">
<cfset variables.attributes[_fba.fuseactionVariable] = _fba.defaultFuseaction />
</cfif>
<cfset variables.attributes[_fba.fuseactionVariable] = trim(variables.attributes[_fba.fuseactionVariable]) />
<cfset variables.attributes.fuseaction = variables.attributes[_fba.fuseactionVariable] />
</cfif>
</cflock>
<cfelse>
<cfset _fba = application[variables.FUSEBOX_APPLICATION_KEY] />
<!--- fix attributes precedence --->
<cfif _fba.precedenceFormOrURL is "URL">
<cfset structAppend(variables.attributes,URL,true) />
</cfif>
<!--- set the default fuseaction if necessary --->
<cfif not structKeyExists(variables.attributes,_fba.fuseactionVariable) or trim(variables.attributes[_fba.fuseactionVariable]) is "">
<cfset variables.attributes[_fba.fuseactionVariable] = _fba.defaultFuseaction />
</cfif>
<cfset variables.attributes[_fba.fuseactionVariable] = trim(variables.attributes[_fba.fuseactionVariable]) />
<cfset variables.attributes.fuseaction = variables.attributes[_fba.fuseactionVariable] />
</cfif>
<!---
Fusebox 4.1 did not set attributes.fuseaction or default the fuseaction variable until
*after* fusebox.init.cfm had run. This made it hard for fusebox.init.cfm to do URL
rewriting. For Fusebox 5, we default the fuseaction variable and set attributes.fuseaction
before fusebox.init.cfm so it can rely on attributes.fuseaction and rewrite that. However,
in order to maintain backward compatibility, we need to allow fusebox.init.cfm to set
attributes[_fba.fuseactionVariable] and still have that reflected in attributes.fuseaction
and for that to actually be the request that gets processed.
--->
<cfif _fba.debug>
<cfset variables.myFusebox.trace("Fusebox","Including fusebox.init.cfm") />
</cfif>
<cftry>
<!--- _fba_ttr_fav and _ba_attr_fa should *not* be exposed --->
<cfset _fba_attr_fav = variables.attributes[_fba.fuseactionVariable] />
<cfset _fba_attr_fa = variables.attributes.fuseaction />
<cfinclude template="#_fba.getCoreToAppRootPath()#fusebox.init.cfm" />
<cfif variables.attributes.fuseaction is not _fba_attr_fa>
<cfif variables.attributes.fuseaction is not variables.attributes[_fba.fuseactionVariable]>
<cfif variables.attributes[_fba.fuseactionVariable] is not _fba_attr_fav>
<!--- inconsistent modification of both variables?!? --->
<cfthrow type="fusebox.inconsistentFuseaction"
message="Inconsistent fuseaction variables"
detail="Both attributes.fuseaction and attributes[{fusebox}.fuseactionVariable] changed in fusebox.init.cfm so Fusebox doesn't know what to do with the values!" />
<cfelse>
<!--- ok, only attributes.fuseaction changed --->
<cfset variables.attributes[_fba.fuseactionVariable] = variables.attributes.fuseaction />
</cfif>
<cfelse>
<!--- ok, they were both changed and they match --->
</cfif>
<cfelse>
<!--- attributes.fuseaction did not change --->
<cfif variables.attributes[_fba.fuseactionVariable] is not _fba_attr_fav>
<!--- make attributes.fuseaction match the other changed variable --->
<cfset variables.attributes.fuseaction = variables.attributes[_fba.fuseactionVariable] />
<cfelse>
<!--- ok, neither variable changed --->
</cfif>
</cfif>
<cfcatch type="missinginclude" />
</cftry>
<cfif doCompile>
<!---
must special case development-circuit-load mode since it causes circuits to reload during
the compile (post-load) phase and therefore must be exclusive
--->
<cfif _fba.debug>
<cfset variables.myFusebox.trace("Fusebox","Compiling requested fuseaction '#variables.attributes.fuseaction#'") />
</cfif>
<!--- _parsedFileData should *not* be exposed --->
<cfif _fba.mode is "development-circuit-load">
<cflock name="#application.ApplicationName#_fusebox_#variables.FUSEBOX_APPLICATION_KEY#" type="exclusive" timeout="300">
<cfset _parsedFileData = _fba.compileRequest(attributes.fuseaction,myFusebox) />
</cflock>
<cfelse>
<cflock name="#application.ApplicationName#_fusebox_#variables.FUSEBOX_APPLICATION_KEY#" type="readonly" timeout="300">
<cfset _parsedFileData = _fba.compileRequest(attributes.fuseaction,myFusebox) />
</cflock>
</cfif>
</cfif>
</cffunction>
<!--- excuse the formatting here - it is done to completely suppress whitespace --->
<cffunction name="onRequest"><cfargument
name="targetPage" type="string" required="true" /><cfsetting
enablecfoutputonly="true">
<cfif variables.myFusebox.parameters.execute>
<cfif _fba.debug>
<cfset myFusebox.trace("Fusebox","Including parsed file for '#variables.attributes.fuseaction#'") />
</cfif>
<cftry>
<!---
readonly lock protects against including the parsed file while
another threading is writing it...
--->
<cflock name="#_parsedFileData.lockName#" type="readonly" timeout="30">
<cfinclude template="#_parsedFileData.parsedFile#" />
</cflock>
<cfcatch type="missinginclude">
<cfif right(cfcatch.missingFileName, len(_parsedFileData.parsedName)) is _parsedFileData.parsedName>
<cfthrow type="fusebox.missingParsedFile"
message="Parsed File or Directory not found."
detail="Attempting to execute the parsed file '#_parsedFileData.parsedName#' threw an error. This can occur if the parsed file does not exist in the parsed directory or if the parsed directory itself is missing." />
<cfelse>
<cfrethrow />
</cfif>
</cfcatch>
</cftry>
</cfif>
<cfsetting enablecfoutputonly="false">
</cffunction>
<cffunction name="onRequestEnd" output="true">
<cfargument name="targetPage" type="string" required="true" />
<cfif structKeyExists(variables,"myFusebox")>
<cfset variables.myFusebox.trace("Fusebox","Request completed") />
</cfif>
<cfif isDefined("_fba.debug") and _fba.debug and structKeyExists(variables,"myFusebox") and right(arguments.targetPage,4) is not ".cfc">
<cfoutput>#variables.myFusebox.renderTrace()#</cfoutput>
</cfif>
</cffunction>
<cffunction name="onSessionEnd" output="false">
</cffunction>
<cffunction name="onApplicationEnd" output="false">
</cffunction>
<cffunction name="onError">
<cfargument name="exception" />
<cfset var stack = 0 />
<cfset var prefix = "Raised at " />
<!--- top-level exception is always event name / expression for Application.cfc (but not fusebox5.cfm) --->
<cfset var caughtException = arguments.exception />
<cfif structKeyExists(caughtException,"rootcause")>
<cfset caughtException = caughtException.rootcause />
</cfif>
<cfif listFirst(caughtException.type,".") is "fusebox">
<cfif isDefined("_fba.debug") and _fba.debug and structKeyExists(variables,"myFusebox")>
<cfset variables.myFusebox.trace("Fusebox","Caught Fusebox exception '#caughtException.type#'") />
<cfif structKeyExists(caughtException,"tagcontext")>
<cfloop index="stack" from="1" to="#arrayLen(caughtException.tagContext)#">
<cfset variables.myFusebox.trace("Fusebox",prefix &
caughtException.tagContext[stack].template & ":" &
caughtException.tagContext[stack].line) />
<cfset prefix = "Called from " />
</cfloop>
</cfif>
</cfif>
<cfif not isDefined("_fba.errortemplatesPath") or (
structKeyExists(variables,"attributes") and structKeyExists(variables,"myFusebox") and
not _fba.handleFuseboxException(caughtException,variables.attributes,variables.myFusebox,variables.FUSEBOX_APPLICATION_KEY)
)>
<cfif isDefined("_fba.debug") and _fba.debug and structKeyExists(variables,"myFusebox")>
<cfoutput>#variables.myFusebox.renderTrace()#</cfoutput>
</cfif>
<cfthrow object="#caughtException#" />
</cfif>
<cfelse>
<cfif isDefined("_fba.debug") and _fba.debug and structKeyExists(variables,"myFusebox")>
<cfset variables.myFusebox.trace("Fusebox","Request failed with exception '#caughtException.type#' (#caughtException.message#)") />
<cfif structKeyExists(caughtException,"tagcontext")>
<cfloop index="stack" from="1" to="#arrayLen(caughtException.tagContext)#">
<cfset variables.myFusebox.trace("Fusebox",prefix &
caughtException.tagContext[stack].template & ":" &
caughtException.tagContext[stack].line) />
<cfset prefix = "Called from " />
</cfloop>
</cfif>
<cfoutput>#variables.myFusebox.renderTrace()#</cfoutput>
</cfif>
<cfthrow object="#caughtException#" />
</cfif>
<!--- if we hit an error before starting the request, prevent the request from running --->
<cfset myFusebox.parameters.execute = false />
</cffunction>
<cffunction name="override" returntype="void" access="public" output="false">
<cfargument name="name" type="string" required="true" />
<cfargument name="value" type="any" required="true" />
<cfargument name="useThisScope" type="boolean" default="false" />
<cfif arguments.useThisScope>
<cfset this[arguments.name] = arguments.value />
<cfelse>
<cfset variables[arguments.name] = arguments.value />
</cfif>
</cffunction>
<cffunction name="onFuseboxApplicationStart">
</cffunction>
<cffunction name="loadFusebox" access="private" output="false">
<!--- ticket 232: extend request timeout value on framework load --->
<cfsetting requesttimeout="600" />
<cfif not structKeyExists(application,variables.FUSEBOX_APPLICATION_KEY) or variables.myFusebox.parameters.userProvidedLoadParameter>
<!--- can't be conditional: we don't know the state of the debug flag yet --->
<cfset variables.myFusebox.trace("Fusebox","Creating Fusebox application object") />
<cfset _fba = createObject("component","fuseboxApplication") />
<cfset application[variables.FUSEBOX_APPLICATION_KEY] = _fba.init(variables.FUSEBOX_APPLICATION_KEY,variables.FUSEBOX_APPLICATION_PATH,variables.myFusebox,variables.FUSEBOX_CALLER_PATH,variables.FUSEBOX_PARAMETERS) />
<cfelse>
<!--- can't be conditional: we don't know the state of the debug flag yet --->
<cfset variables.myFusebox.trace("Fusebox","Reloading Fusebox application object") />
<cfset _fba = application[variables.FUSEBOX_APPLICATION_KEY] />
<!--- it exists and the load is implicit, not explicit (via user) so just reload XML --->
<cfset _fba.reload(variables.FUSEBOX_APPLICATION_KEY,variables.FUSEBOX_APPLICATION_PATH,variables.myFusebox,variables.FUSEBOX_PARAMETERS) />
</cfif>
<!--- fix attributes precedence --->
<cfif _fba.precedenceFormOrURL is "URL">
<cfset structAppend(variables.attributes,URL,true) />
</cfif>
<!--- set the default fuseaction if necessary --->
<cfif not structKeyExists(variables.attributes,_fba.fuseactionVariable) or variables.attributes[_fba.fuseactionVariable] is "">
<cfset variables.attributes[_fba.fuseactionVariable] = _fba.defaultFuseaction />
</cfif>
<!--- set this up for fusebox.appinit.cfm --->
<cfset variables.attributes.fuseaction = variables.attributes[_fba.fuseactionVariable] />
<!--- flag this as the first request for the application --->
<cfset variables.myFusebox.applicationStart = true />
<!--- force parse after reload for consistency in development modes --->
<cfif _fba.mode is not "production" or variables.myFusebox.parameters.userProvidedLoadParameter>
<cfset variables.myFusebox.parameters.parse = true />
</cfif>
<!--- need all of the above set before we attempt any compiles! --->
<cfif variables.myFusebox.parameters.parseall>
<cfset _fba.compileAll(variables.myFusebox) />
</cfif>
<!--- FB55: template method to allow no-XML application initialization --->
<cfif _fba.debug>
<cfset variables.myFusebox.trace("Fusebox","Executing onFuseboxApplicationStart()") />
</cfif>
<cfset onFuseboxApplicationStart() />
<!--- FB5: new appinit include file --->
<cfif _fba.debug>
<cfset variables.myFusebox.trace("Fusebox","Including fusebox.appinit.cfm") />
</cfif>
<cftry>
<cfinclude template="#_fba.getCoreToAppRootPath()#fusebox.appinit.cfm" />
<cfcatch type="missinginclude" />
</cftry>
<!--- ticket 269 ensure there is no double reload at CF startup --->
<cfset variables.myFusebox.parameters.load = false />
</cffunction>
</cfprocessingdirective>
</cfcomponent>
I dunno why you'd be getting the error you're seeing, but in CF9 you don't need to do all that horsing around deleting onRequest(), you can simply have an onCfcRequest() method in there, which'll get called instead of onRequest() when requests for CFCs are made. So doing that could remove the situation in which the error situation arises.
Finally I was able to resolve this issue. I figured that the error shows only in the localhost but not on any servers. Also, found that the problem was only with the cfselect with bind cfc. I didn't see any difference with the settings in localhost and other servers. So I tried some changes to the fusebox code and fixed it. Following change in fusebox5.cfm made it working.
<cftry>
<cfset __fuseboxAppCfc.onRequest(CGI.SCRIPT_NAME) />
<cfset __fuseboxAppCfc.onRequestEnd(CGI.SCRIPT_NAME) />
<cfcatch type="any">
<cfset __fuseboxAppCfc.onError(cfcatch)>
</cfcatch>
</cftry>
Changed this to:
<cftry>
<cfif right(CGI.SCRIPT_NAME,"4") NEQ ".cfc">
<cfset __fuseboxAppCfc.onRequest(CGI.SCRIPT_NAME) />
</cfif>
<cfset __fuseboxAppCfc.onRequestEnd(CGI.SCRIPT_NAME) />
<cfcatch type="any">
<cfset __fuseboxAppCfc.onError(cfcatch)>
</cfcatch>
</cftry>
I have defined a datasource in the admin console of ColdFusion Admin. I have declared the configuration with the same datasource name in set_datasource_util file. I have a webpage that connects to a DB(Oracle) using the datasource given and it works fine most of the time. But lately I am starting to see exceptions like "Variable datasource-name(my app name) is undefined". I am not able to identify the root cause as I am not able to recreate it as it works most of the time. I can say on average on 1000 hits of page, it fails 1 time with that exception. Can anyone help in identifying what could be the possible issue so that I can investigate in that direction.
I have configured WOCD080P_ABC in CFAdmin.
Below is the code from Application.cfm
<cfif client.securityLevel NEQ ''>
<cfinvoke component="#siteroot#.CFC.Set_Data_Source_Util" method="fnSetABCDataSource" returnvariable="ABCDataSourceDefinition">
<cfinvokeargument name="Environment" value="#variables.thisServerType#" />
</cfinvoke>
<cfif ABCDataSourceDefinition.DataSource NEQ 'Error'>
<cfset DATASOURCE_ABC = ABCDataSourceDefinition.DataSource />
<cfset DBUSER_ABC = ABCDataSourceDefinition.DBUser />
<cfset DBPASSWORD_ABC = ABCDataSourceDefinition.DBPassword />
</cfif>
</cfif>
Below is the code from Set_datasource_UTil :
<cffunction name="fnSetABCDataSource" access="public" returntype="struct">
<cfargument name="Environment" type="string" required="yes">
<cfset ReturnParameters = StructNew() />
<cfif Environment IS 'prod'>
<cfset ReturnParameters.DataSource = 'WOCD080P_ABC' />
<cfset ReturnParameters.DBUser = ''/>
<cfset ReturnParameters.DBPassword = '' />
<cfelse>
<cfset ReturnParameters.DataSource = 'WOCD080T_ABC'/>
<cfset ReturnParameters.DBUser = ''/>
<cfset ReturnParameters.DBPassword = '' />
</cfif>
<cfreturn ReturnParameters>
</cffunction>
Below is the code which i am executing from HTML file.
<cfinvoke component="#siteroot#.cfc.ABC_UTIL" method="GetRegion" returnVariable = "USregion" >
<cfinvokeargument name="dbSource" value=#DATASOURCE_ABC# />
<cfinvokeargument name="dbUser" value=#DBUSER_ABC# />
<cfinvokeargument name="dbPass" value=#DBPASSWORD_ABC# />
</cfinvoke>
The code in the HTML file works most of the time but breaks sometime with error Variable DATASOURCE_ABC not defined.
Any help appreciated.
Please ensure you are using the var keyword for local variables inside of CFC Functions:
<cfset var ReturnParameters = StructNew() />
CF9+ local scope can be used in lieu of var:
<cfset local.ReturnParameters = StructNew() />
For extra protection, improve this statement in your application.cfm:
<cfif isDefined("ABCDataSourceDefinition.DataSource")
AND ABCDataSourceDefinition.DataSource NEQ 'Error'>
<cfset DATASOURCE_ABC = ABCDataSourceDefinition.DataSource />
<cfset DBUSER_ABC = ABCDataSourceDefinition.DBUser />
<cfset DBPASSWORD_ABC = ABCDataSourceDefinition.DBPassword />
</cfif>
Also, the could be another source of why a datasource is not being defined. I assume you have an interceptor that catches that before this datasource error occurs and sends them to a login page or something.
I was wondering if there is any strings centralization in ColdFusion similar to Android strings.xml file.
So, that my code remains intact if i want to do any changes in string
ColdFusion is a programming language, not a user interface framework.
There is nothing like Android's string resource management built into ColdFusion, but it would be very easy to implement that yourself.
resources/strings.xml:
<!-- keep as a structure with unique element names -->
<strings>
<heading>This is a test.</heading>
<greetings>
<hello>Hello World!</hello>
<bye>Goodbye World!</bye>
</greetings>
</strings>
ColdFusion utility function (for example in a CFC component util.cfc):
<cffunction name="ReadResouceXml" returntype="struct" access="public" output="no">
<cfargument name="path" type="string" required="yes">
<!--- internal use argument --->
<cfargument name="xmlDoc" type="xml" required="no">
<cfset var xmlElem = "">
<cfset var output = StructNew()>
<!--- read XML file from disk --->
<cfif not StructKeyExists(arguments, "xmlDoc")>
<cffile action="read" file="#ExpandPath(path)#" variable="xmlDoc" charset="UTF-8">
<cfset xmlDoc = XmlParse(xmlDoc).XmlRoot>
</cfif>
<!--- recursively convert XML to a CF struct --->
<cfloop index="i" from="1" to="#ArrayLen(xmlDoc.XmlChildren)#">
<cfset xmlElem = xmlDoc.XmlChildren[i]>
<cfif ArrayLen(xmlElem.XmlChildren) gt 0>
<cfset output[xmlElem.XmlName] = ReadResouceXml("", xmlElem)>
<cfelse>
<cfset output[xmlElem.XmlName] = xmlElem.XmlText>
</cfif>
</cfloop>
<cfreturn output>
</cffunction>
Coldfusion usage:
<cfobject type="component" name="util" component="util">
<cfset strings = util.ReadResouceXml("resources/strings.xml")>
<cfoutput>
<h1>#strings.heading#</h1>
#strings.greetings.hello# - #strings.greetings.bye#
</cfoutput>