I am supporting a ColdFusion 8 website running on Windows Server 2003 R2 (with all Windows Security Updates applied). The website functions smoothly 99.9% of the time. However, about 2 months ago, the ColdFusion 8 Application Server service started crashing and restarting itself every night at 10:30 PM. There is a ColdFusion Scheduled Task that runs at 10:30 PM every night, so I tried running it manually (direct URL in a browser), and sure enough the ColdFusion 8 Application Server service crashed and restarted. So, it's obviously this one template that is causing this to happen.
What this template does is read a directory full of PDF files, then loop through those files to create several thumbnail images for each using CFPDF. This Scheduled Task has been running for years without this issue. The CF service appears to be crashing/restarting almost immediately, before a single file is processed.
I tried running the same template in our Staging environment, and it ran fine - no CF restart. I'm very confused.
Searched through the ColdFusion logs and found nothing.
UPDATE:
Code sample:
<cffunction name="createThumbnails" returntype="Void" output="false">
<cfargument name="sourcePath" type="String" default="" />
<cfargument name="overwriteExisting" type="Boolean" default="true" />
<cfargument name="deleteSourceFile" type="Boolean" default="false" />
<cfset var _image = {} />
<cfif FileExists(ARGUMENTS.sourcePath)>
<cfif ARGUMENTS.overwriteExisting
OR NOT FileExists(getXLargeThumbnailPath())>
<!--- Large Image for MACXpress --->
<cfset _image =
REQUEST.UDFLib.Image.scale(
imagePath = ARGUMENTS.sourcePath,
maxHeight = 777,
maxWidth = 627
) />
<cfimage
action="write"
source="#_image#"
overwrite="true"
destination="#getXLargeThumbnailPath()#" />
</cfif>
<cfif ARGUMENTS.overwriteExisting
OR NOT FileExists(getXLargeThumbnailPath())>
<cfset _image =
REQUEST.UDFLib.Image.scale(
imagePath = ARGUMENTS.sourcePath,
maxHeight = 211,
maxWidth = 215
) />
<cfimage
action="write"
source="#_image#"
overwrite="true"
destination="#getXLargeThumbnailPath()#" />
</cfif>
<cfif ARGUMENTS.overwriteExisting
OR NOT FileExists(getLargeThumbnailPath())>
<cfset _image =
REQUEST.UDFLib.Image.scale(
imagePath = ARGUMENTS.sourcePath,
maxHeight = 265,
maxWidth = 215
) />
<cfimage
action="write"
source="#_image#"
overwrite="true"
destination="#getLargeThumbnailPath()#" />
</cfif>
<cfif ARGUMENTS.overwriteExisting
OR NOT FileExists(getMediumThumbnailPath())>
<cfset _image =
REQUEST.UDFLib.Image.scale(
imagePath = ARGUMENTS.sourcePath,
maxHeight = 100,
maxWidth = 100
) />
<cfimage
action="write"
source="#_image#"
overwrite="true"
destination="#getMediumThumbnailPath()#" />
</cfif>
<cfif ARGUMENTS.overwriteExisting
OR NOT FileExists(getSmallThumbnailPath())>
<cfset _image =
REQUEST.UDFLib.Image.scale(
imagePath = ARGUMENTS.sourcePath,
maxHeight = 50,
maxWidth = 50
) />
<cfimage
action="write"
source="#_image#"
overwrite="true"
destination="#getSmallThumbnailPath()#" />
</cfif>
<cfscript>
if (ARGUMENTS.deleteSourceFile) {
try {
FileDelete(ARGUMENTS.sourcePath);
}
catch (any e) {
}
}
</cfscript>
</cfif>
</cffunction>
REQUEST.UDFLib.PDF:
<cffunction
name="pdfToImageFile"
returntype="String"
output="false"
hint="Converts a phsyical PDF File to a physical Image file and returns the absolute path of the new Image file">
<cfargument name="sourcePath" type="String" default="" />
<cfargument name="destinationPath" type="String" default="" />
<cfargument name="format" type="String" default="png" />
<cfset var LOCAL = {} />
<cfif NOT isValidPDF(Trim(ARGUMENTS.sourcePath))>
<cfthrow
message="Source file not specified or not a valid PDF file." />
</cfif>
<cfif NOT DirectoryExists(Trim(ARGUMENTS.destinationPath))>
<cfthrow message="Inavlid Destination path." />
</cfif>
<cfif
NOT ListFindNoCase(
GetWriteableImageFormats(),
Trim(ARGUMENTS.format)
)>
<cfthrow message="Inavlid Image format specified." />
</cfif>
<cfscript>
LOCAL.DestinationFilePath =
Trim(ARGUMENTS.destinationPath)
& "\"
& VARIABLES.Library.File.getFileNameWithoutExtension(
GetFileFromPath(ARGUMENTS.sourcePath)
)
& "."
& LCase(Trim(ARGUMENTS.format));
LOCAL.RandomAccessFile =
CreateObject("java", "java.io.RandomAccessFile")
.init(
CreateObject("java","java.io.File")
.init(ARGUMENTS.sourcePath),
"r"
);
LOCAL.FileChannel = LOCAL.RandomAccessFile.getChannel();
</cfscript>
<cftry>
<cfset LOCAL.PDFFile =
CreateObject("java", "com.sun.pdfview.PDFFile")
.init(
LOCAL.FileChannel.map(
CreateObject("java", "java.nio.channels.FileChannel$MapMode")
.READ_ONLY,
0,
LOCAL.FileChannel.size()
)
) />
<cfset LOCAL.PDFPage = LOCAL.PDFFile.getPage(1) />
<cfif NOT StructKeyExists(LOCAL, "PDFPage")>
<cfthrow message="PDF cannot be converted - unknown error." />
</cfif>
<cfcatch type="Any">
<cfscript>
LOCAL.RandomAccessFile.close();
</cfscript>
<cfthrow message="PDF cannot be converted - unknown error." />
</cfcatch>
</cftry>
<cfscript>
// Create new image
LOCAL.Rectangle = LOCAL.PDFPage.getBBox();
LOCAL.BufferedImage =
CreateObject("java", "java.awt.image.BufferedImage")
.init(
LOCAL.Rectangle.width,
LOCAL.Rectangle.height,
CreateObject("java", "java.awt.image.BufferedImage")
.TYPE_INT_RGB
);
LOCAL.Graphics = LOCAL.BufferedImage.createGraphics();
LOCAL.Graphics.drawImage(
LOCAL.PDFPage.getImage(
LOCAL.Rectangle.width,
LOCAL.Rectangle.height,
LOCAL.Rectangle,
JavaCast("null", ""),
true,
true
),
0,
0,
JavaCast("null", "")
);
LOCAL.Graphics.dispose();
LOCAL.ImageFile =
CreateObject("java", "java.io.File")
.init(LOCAL.DestinationFilePath);
// Delete existing image file
if (LOCAL.ImageFile.exists())
LOCAL.ImageFile.delete();
// Export the image to the specified format
CreateObject("java", "javax.imageio.ImageIO")
.write(
LOCAL.BufferedImage,
JavaCast("string", Trim(ARGUMENTS.format)),
LOCAL.ImageFile
);
LOCAL.RandomAccessFile.close();
return LOCAL.DestinationFilePath;
</cfscript>
</cffunction>
REQUEST.UDFLib.Image:
<cffunction name="scale" returntype="Any" output="false">
<cfargument name="imagePath" type="String" required="true" />
<cfargument name="action" type="String" default="fit" hint="shrink, enlarge, or fit"/>
<cfargument name="minWidth" type="Numeric" default="-1" />
<cfargument name="minHeight" type="Numeric" default="-1" />
<cfargument name="maxWidth" type="Numeric" default="-1" />
<cfargument name="maxHeight" type="Numeric" default="-1" />
<cfscript>
var scaledDimensions = {
width = -1,
height = -1
};
var scaledImage = ImageNew();
scaledImage = ImageNew(ARGUMENTS.imagePath);
switch (ARGUMENTS.action) {
case "shrink":
scaledDimensions =
getDimensionsToShrink(
imageHeight = scaledImage.getHeight(),
imageWidth = scaledImage.getWidth(),
maxWidth = ARGUMENTS.maxWidth,
maxHeight = ARGUMENTS.maxHeight
);
break;
case "enlarge":
scaledDimensions =
getDimensionsToEnlarge(
imageHeight = scaledImage.getHeight(),
imageWidth = scaledImage.getWidth(),
minWidth = ARGUMENTS.minWidth,
minHeight = ARGUMENTS.minHeight
);
break;
default:
scaledDimensions =
getDimensionsToFit(
imageHeight = scaledImage.getHeight(),
imageWidth = scaledImage.getWidth(),
minWidth = ARGUMENTS.minWidth,
minHeight = ARGUMENTS.minHeight,
maxWidth = ARGUMENTS.maxWidth,
maxHeight = ARGUMENTS.maxHeight
);
break;
}
if (scaledDimensions.width > 0 && scaledDimensions.height > 0) {
// This helps the image quality
ImageSetAntialiasing(scaledImage, "on");
ImageScaleToFit(
scaledImage,
scaledDimensions.width,
scaledDimensions.height
);
}
return scaledImage;
</cfscript>
</cffunction>
Thanks to #MarkAKruger for pointing me to CFROOT\runtime\bin\hs_err_pid*.log files. It looks like a memory issue when trying to convert the PDF to PNG....
Here is a link to the contents of the file from the last time I tried to run this template (to large to include here):
Error Dump File
I would still really appreciate any help figuring out how to fix.....
Take a look in the /runtime/bin directory and see if you hav esome error files - something like hserrorxxxx.log (don't recall the format for java 1.4). This is a "hotspot" error - typically generated on a crash. Open one up and take a look. My guess is you are either running out of memory or there is an RGB image embedded in your PDF that is abending your server. You may be able to figure it out from the stack in the hs (hotspot) error file.
Related
I have implemented ColdFusion XMPP Event Gateway in coldfusion server 10 and it is working fine with google talk. The same thing i want to implement in Railo server but no luck to find something.
Please suggest something to "Railo to talk to XMPP/Jabber/Google Talk from within Railo"
Used cfc file in coldfusion XMPP event gateway
<cfcomponent displayname="EventGateway" hint="Process events from the test gateway and return echo">
<cffunction name="onIncomingMessage" output="no">
<cfargument name="CFEvent" type="struct" required="yes">
<cflog file="#CFEvent.GatewayID#Status" text=" onIncomingMessage; SENDER: #CFEvent.Data.SENDER# MESSAGE:
#CFEvent.Data.MESSAGE# TIMESTAMP: #CFEvent.Data.TIMESTAMP# ">
<!--- Get the message --->
<cfset data=cfevent.DATA>
<cfset message="#data.message#">
<!--- where did it come from? --->
<cfset orig="#CFEvent.originatorID#">
<cfset retValue = structNew()>
<cfif listcontains("XMPP ", arguments.CFEVENT.GatewayType) gt 0>
<cfset retValue.BuddyID = orig>
<cfset retValue.MESSAGE = "echo: " & message>
<cfelseif arguments.CFEVENT.GatewayType is "Socket">
<cfset retValue.originatorID = orig>
<cfset retValue.message = "echo: " & message>
<cfelseif arguments.cfevent.gatewaytype is "SMS">
<cfset retValue.command = "submit">
<cfset retValue.sourceAddress = arguments.CFEVENT.GatewayID>
<cfset retValue.destAddress = orig>
<cfset retValue.shortMessage = "echo: " & message>
</cfif>
<!--- we can write our script to process like database Query here --->
<!--- send the return message back --->
<cfreturn retValue>
</cffunction>
<cffunction name="onAddBuddyRequest">
<cfargument name="CFEvent" type="struct" required="YES">
<cflock scope="APPLICATION" timeout="10" type="EXCLUSIVE">
<cfscript>
// If the name is in the DB once, accept; if it is missing, decline.
// If it is in the DB multiple times, take no action.
action="accept";
reason="Valid ID";
//Add the buddy to the buddy status structure only if accepted.
if (NOT StructKeyExists(Application,"buddyStatus")) {
Application.buddyStatus=StructNew();
}
if (NOT StructKeyExists(Application.buddyStatus,CFEvent.Data.SENDER)) {
Application.buddyStatus[#CFEvent.Data.SENDER#]=StructNew();
}
Application.buddyStatus[#CFEvent.Data.SENDER#].status="Accepted Buddy Request";
Application.buddyStatus[#CFEvent.Data.SENDER#].timeStamp=
CFEvent.Data.TIMESTAMP;
Application.buddyStatus[#CFEvent.Data.SENDER#].message=CFEvent.Data.MESSAGE;
</cfscript>
</cflock>
<!--- Log the request and decision information. --->
<cflog file="#CFEvent.GatewayID#Status" text="onAddBuddyRequest; SENDER: #CFEvent.Data.SENDER# MESSAGE:
#CFEvent.Data.MESSAGE# TIMESTAMP: #CFEvent.Data.TIMESTAMP# ACTION: #action#">
<!--- Return the action decision. --->
<cfset retValue = structNew()>
<cfset retValue.command = action>
<cfset retValue.BuddyID = CFEvent.DATA.SENDER>
<cfset retValue.Reason = reason>
<cfreturn retValue>
</cffunction>
<cffunction name="onAddBuddyResponse">
</cffunction>
<cffunction name="onBuddyStatus">
</cffunction>
<cffunction name="onIMServerMessage">
</cffunction>
</cfcomponent>
Thanks,
Arun
I am using the easySocket udf and code below to send a 2 byte header length of the actual message via tcp/ip. However the UTF-8 character (i.e ( ) (8D hex) is received as ? (3F hex)). I have used that following code that I have also checked on stackoverflow. Is there a way to send byte equivalent of the length so that there will be no character encoding involve? Thanks.
<cffunction name="easySocket" access="private" returntype="any" hint="Uses Java Sockets to connect to a remote socket over TCP/IP" output="false">
<cfargument name="host" type="string" required="yes" default="localhost" hint="Host to connect to and send the message">
<cfargument name="port" type="numeric" required="Yes" default="8080" hint="Port to connect to and send the message">
<cfargument name="message" type="string" required="yes" default="" hint="The message to transmit">
<cfset var result = "">
<cfset var socket = createObject( "java", "java.net.Socket" )>
<cfset var streamOut = "">
<cfset var output = "">
<cfset var input = "">
<cftry>
<cfset socket.init(arguments.host,arguments.port)>
<cfcatch type="Object">
<cfset result = "#cfcatch.Message#<br>Could not connected to host <strong>#arguments.host#</strong>, port <strong>#arguments.port#</strong>.">
<cfreturn result>
</cfcatch>
</cftry>
<cfif socket.isConnected()>
<cfset streamOut = socket.getOutputStream()>
<cfset output = createObject("java", "java.io.PrintWriter").init(streamOut)>
<cfset streamInput = socket.getInputStream()>
<cfset inputStreamReader= createObject( "java", "java.io.InputStreamReader").init(streamInput)>
<cfset input = createObject( "java", "java.io.BufferedReader").init(InputStreamReader)>
<cfset output.print(chr(1) & chr(141) & arguments.message)>
<cfset output.flush()>
<cfset result='Success: '& arguments.message >
<cfset socket.close()>
<cfelse>
<cfset result = "Could not connected to host <strong>#arguments.host#</strong>, port <strong>#arguments.port#</strong>.">
</cfif>
<cfreturn result>
</cffunction>
<cfset txt_msg ='<COS MSG_TYPE_ID="0100" MCC="5999" POS_ENTRY_MODE="012" POS_COND="00" CARD_NUMBER="6417801234567890" ACQUIRER_ID="00000002" TERMINAL_ID="CAL100 " PROC_CODE="300000" STAN="000129" RETRIEVAL_REFERENCE="330145181129" DATE_LOCAL_TRAN="1111" TIME_LOCAL_TRAN="173654" TRANSM_DATE_TIME="1111173654" CARD_ACPT_ID_CODE="TESTDTA" CARD_ACPT_NAME_LOC="TSTESTSTE" />'>
<cfoutput>#easySocket('172.16.0.23','9095',txt_msg)#</cfoutput>
According to the API, "... if you omit the encoding identifier, InputStreamReader and OutputStreamWriter rely on the default encoding." - which may not be "UTF-8". It is usually Cp1252 on windows machines. You can view the default by checking the system properties:
<cfset system = createObject("java", "java.lang.System")>
<cfset defaultEncoding = system.getProperty("file.encoding", "unknown")>
.. or the encoding of your PrintWriter:
<cfdump var="#output.getEncoding()#">
Anyway, try specifying the encoding explicitly when you create the output stream. That should fix the issue.
...
<cfset streamOut = socket.getOutputStream()>
<cfset writer = createObject("java", "java.io.OutputStreamWriter").init(streamOut, "UTF-8")>
<cfset output = createObject("java", "java.io.PrintWriter").init(writer)>
...
Can anyone help me by suggesting a function to extract a .7z file in ColdFusion? I use ColdFusion 10 and cfscript based code. Indeed we have the cfzip tag, but it only extracts .zip and .jar files.
You can use cfexecute, which unfortunately is not availble in cfscript, to execute the 7z extractor on your server and pass through the various commands to extract the file to a place of your choosing.
Luckily for you, it seems Raymond Camden has gone into it in some detail:
http://www.raymondcamden.com/index.cfm/2011/2/21/Working-with-RARs-in-ColdFusion
Function to unrar .rar file in given destination.. use cfexecute tag to run rar exe in command line
<cffunction name="Unrar" access="public" returnType="boolean" output="false">
<cfargument name="archivefile" type="string" required="true">
<cfargument name="destination" type="string" required="true">
<cfset var exeName = "">
<cfset var result = "">
<cfset var errorresult = "">
<cfif not fileExists(arguments.archivefile)>
<cfthrow message="Unable to work with #arguments.arvhiefile#, it does not exist.">
</cfif>
<cfif findnocase(".rar",arguments.archivefile)>
<cfset var exeName = expandpath("WinRAR\rar.exe")>
<cfset var args = []>
<cfif directoryExists(#arguments.destination#)>
<cfset args[1] = "x +o">
<cfelse>
<cfset directoryCreate(#arguments.destination#)>
<cfset args[1] = "x">
</cfif>
<cfset args[2] = arguments.archivefile>
<cfset args[3] = "#arguments.destination#">
</cfif>
<cfexecute name="#exeName#" arguments="#args#" variable="result" errorvariable="errorresult" timeout="99" />
<cfif findNoCase("OK All OK", result)>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
</cffunction>
I'm getting a 500 Internal Server Error trying to use http://fineuploader.com/ with Coldfusion server side https://github.com/Widen/fine-uploader-server/tree/master/coldfusion
Console says:
[FineUploader] Error when attempting to parse xhr response text (SyntaxError: Unexpected token <) at this line in window.console.log('<' + level + '> ' + message);
It seems its coming from the response message in the Server CFC.
<!--- Code provided by Pegasus Web Productions LLC - www.pegweb.com --->
<!--- get stuck use the forums http://github.com/Widen/fine-uploader --->
<!--- Tested with Adobe CF Enterprise 9.x and Fine Uploader --->
<CFCOMPONENT HINT="I do your uploads from Fine Uploader" >
<!--- function for single file submission uploads where XHR is not supported ex:->
<CFFUNCTION NAME="Upload" ACCESS="remote" OUTPUT="false" RETURNTYPE="any" RETURNFORMAT="JSON" >
<CFARGUMENT NAME="qqfile" TYPE="string" REQUIRED="true" />
<CFSET var local = structNew() >
<CFSET local.response = structNew() >
<CFSET local.requestData = GetHttpRequestData() ><!--- get the request headers and body --->
<CFSET UploadDir = "/Test/file2" ><!--- set your upload directory here ex: c:\website\www\images\ --->
<!--- check if XHR data exists --->
<CFIF len(local.requestData.content) GT 0 >
<CFSET local.response = UploadFileXhr(arguments.qqfile, local.requestData.content) >
<CFELSE><!--- no XHR data so process this as standard form submission --->
<!--- upload the file --->
<CFFILE ACTION="upload" FILEFIELD="form.qqfile" DESTINATION="#UploadDir#" NAMECONFLICT="makeunique" >
<!--- populate our structure with information about the image we just uploaded in case we want to use this later for CFIMAGE tags or any other processing --->
<CFSET local.metaData = { clientFile=FILE.clientFile, clientFileExt=FILE.clientFileExt, clientFileName=FILE.clientFileName, contentSubType=FILE.contentSubType, contentType=FILE.contentType, fileSize=FILE.fileSize } />
<!--- return the response --->
<CFSET local.response['success'] = true >
<CFSET local.response['type'] = 'form' >
</CFIF>
<CFRETURN local.response >
</CFFUNCTION>
<!--- function for browsers that support XHR ex: Almost anything but IE --->
<CFFUNCTION NAME="UploadFileXhr" ACCESS="private" OUTPUT="false" RETURNTYPE="struct" >
<CFARGUMENT NAME="qqfile" TYPE="string" REQUIRED="true" />
<CFARGUMENT NAME="content" TYPE="any" REQUIRED="true" />
<CFSET var local = structNew() >
<CFSET local.response = structNew() >
<CFSET UploadDir = "" ><!--- set your upload directory here ex: c:\website\www\images\ --->
<!--- write the contents of the http request to a file. The filename is passed with the qqfile variable --->
<CFFILE ACTION="write" FILE="#UploadDir#\#arguments.qqfile#" OUTPUT="#arguments.content#" NAMECONFLICT="makeunique" >
<!--- populate our structure with information about the image we just uploaded in case we want to use this later for CFIMAGE tags or any other processing --->
<CFSET local.metaData = { clientFile=FILE.clientFile, clientFileExt=FILE.clientFileExt, clientFileName=FILE.clientFileName, contentSubType=FILE.contentSubType, contentType=FILE.contentType, fileSize=FILE.fileSize } />
<!--- return custom JSON if desired--->
<CFSET local.response['success'] = true >
<CFSET local.response['type'] = 'xhr' >
<CFRETURN local.response >
</CFFUNCTION>
Calling page
<div id="thumbnail-fine-uploader">
</div>
<script src="/js/jQueryv1.9.1.js">
</script>
<script src="/Test/file2/jquery.fineuploader-3.4.1.js">
</script>
<script>
$(document).ready(function(){
var thumbnailuploader = new qq.FineUploader({
element: $('#thumbnail-fine-uploader')[0],
request: {
endpoint: 'image-uploader.cfc?method=Upload'
},
multiple: false,
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
sizeLimit: 51200 // 50 kB = 50 * 1024 bytes
},
callbacks: {
onComplete: function(id, fileName, responseJSON){
if (responseJSON.success) {
$('#thumbnail-fine-uploader').append('<img src="img/success.jpg" alt="' + fileName + '">');
}
}
}
});
});
</script>
Got it working, I was missing my upload directory path in the second method.
<CFSET UploadDir = "#expandpath('/test/file2')#" >
Thanks for the help
Could anyone find a way of improving this code a bit? I want to read in an INI file in one felt swoop and create a corresponding data structure.
<cfset INIfile = expandPath(".") & "\jobs.ini">
<cfset profile = GetProfileSections(INIfile)>
<cfloop collection="#profile#" item="section">
<cfloop list="#profile[section]#" index="entry">
<cfset app.workflow[section][entry]=GetProfileString(INIfile, section, entry) >
</cfloop>
</cfloop>
I don't believe you can improve this using CFML power. Do you need to parse huge ini files? If not, why would you like to improve your code, it looks pretty straightforward for me.
Other possible (though common for CF) solution is to try pure Java. See this SO thread for pure Java examples.
P.S. BTW, in case of special performance needs you should consider using another storage for configuration. Simple SELECT query to the old good MySQL can be much faster for large datasets.
To expand on ryber's comment, you might consider using this approach instead. I'm assuming you're using CF8.01 or later, as I make use of nested implicit structure notation. This could easily be converted to CF7/6/etc syntax, but wouldn't be as clean or concise.
Again, this only applies if your ini file isn't used by any other applications or people, and doesn't need to be in ini format.
settings.cfm:
<cfset variables.settings = {
fooSection = {
fooKey = 'fooVal',
fooNumber = 2,
},
fooSection2 = {
//...
},
fooSection3 = {
//...
}
} />
Application.cfc: (only the onApplicationStart method)
<cffunction name="onApplicationStart">
<cfinclude template="settings.cfm" />
<cfset application.workflow = variables.settings />
<cfreturn true />
</cffunction>
In addition, I've use the CFEncode application to encrypt the contents of settings.cfm. It won't protect you from someone who gets a copy of the file and wants to see what its encrypted contents are (the encryption isn't that strong, and there are ways to see the contents without decrypting it), but if you just want to keep some nosy people out, it adds a little extra barrier-to-entry that might deter some people.
Update: Since you just left a comment that says you are on CF7, here's native CF7 syntax:
settings.cfm:
<cfset variables.settings = StructNew() />
<cfset variables.settings.fooSection = StructNew() />
<cfset variables.settings.fooSection.fooKey = 'fooVal' />
<cfset variables.settings.fooSection.fooNumber = 2 />
<!--- ... --->
Alternatively, you could use JSONUtil and CFSaveContent to continue to use a JSON-looking approach (similar to my original syntax), but on CF7:
<cfsavecontent variable="variables.jsonSettings">
{
fooSection = {
fooKey = 'fooVal',
fooNumber = 2,
},
fooSection2 = {
//...
},
fooSection3 = {
//...
}
};
</cfsavecontent>
<cfset variables.settings = jsonUtil.deserializeFromJSON(variables.jsonSettings) />
I created a CFC that I use in a bunch of apps. You give it an ini filepath when you init it and it creates a structure based on the ini file. It also optionally keeps the structure flat or creates sub-structures based on the [Sections] in the ini file. You can then either use its getSetting() method to get individual methods or getAllSettings() to return the entire structure. You may find it helpful.
<cfcomponent hint="converts INI file to a structure">
<cfset variables.settings=structNew() />
<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="configurationFile" type="string" required="yes" />
<cfargument name="useSections" default="false" type="boolean" />
<cfset var local=structNew() />
<cfif fileExists(arguments.configurationFile)>
<!--- Get the [sections] in the .INI file --->
<cfset local.sectionStruct=getProfileSections(arguments.configurationFile) />
<!--- Loop over each of these sections in turn --->
<cfloop collection="#local.sectionStruct#" item="local.item">
<cfset local.workingStruct=structNew() />
<cfloop list="#local.sectionStruct[local.item]#" index="local.key">
<!--- Loop over the keys in the current section and add the key/value to a temporary structure --->
<cfset local.workingStruct[local.key]=getProfileString(arguments.configurationFile,local.item,local.key) />
</cfloop>
<cfif arguments.useSections>
<!--- Copy the temporary structure to a key in the setting structure for the current section --->
<cfset variables.settings[local.item]=duplicate(local.workingStruct) />
<cfelse>
<!--- Append the temporary structure to the setting structure --->
<cfset structAppend(variables.settings,local.workingStruct,"yes") />
</cfif>
</cfloop>
<cfelse>
<cfthrow
message="Configuration file not found. Must use fully-qualified path."
extendedinfo="#arguments.configurationFile#"
/>
</cfif>
<cfreturn this>
</cffunction>
<cffunction name="getAllSettings" access="public" output="false" returntype="struct">
<cfreturn variables.settings>
</cffunction>
<cffunction name="getSetting" access="public" output="false" returntype="string">
<cfargument name="settingName" required="yes" type="string" />
<cfset var returnValue="" />
<cfif structKeyExists(variables.settings,arguments.settingName)>
<cfset returnValue=variables.settings[arguments.settingName] />
<cfelse>
<cfthrow
message="No such setting '#arguments.settingName#'."
/>
</cfif>
<cfreturn returnValue>
</cffunction>
</cfcomponent>