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>
Related
I have one .cfc that I use for all communication between client and server code. This cfc page has about 10 different function. Each function has different purpose and I have queries for Select, Insert, Update and Delete. I'm wondering if I should set timeout on the top of the .cfc page inside cfcomponent tag or this should be set inside of the each function or do I even need this? In our current system we have some many error messages like: The request has exceeded the allowable time limit Tag: CFQUERY.
I would like to prevent any similar error messages in my app. Here is example of my cfc page:
<cfcomponent>
<cfset currentDate = DateFormat(Now(),'mm/dd/yyyy')>
<cfset currentTime = TimeFormat(Now(),'hh:mm tt')>
<cfinvoke component="appEntry" method="getRecord" returnvariable="CHKAccess">
<cfinvokeargument name="user" value="userdata"/>
<cfinvokeargument name="app" value="myApp"/>
</cfinvoke>
<cfset adminAccess = false>
<cfset userAccess = false>
<cfif CHKAccess.RecordCount EQ 1>
<cfif CHKAccess.pd_hfmAccess EQ 'A'>
<cfset adminAccess = true>
</cfif>
<cfif CHKAccess.pd_hfmAccess EQ 'U'>
<cfset userAccess = true>
</cfif>
</cfif>
<cffunction name="getData" access="remote" output="true" returnformat="JSON">
<cfargument name="keyVal" type="string" required="true">
<cfset fnResults = structNew()>
<cfif userAccess>
<cfquery name="getRec" datasource="tes">
SELECT some columns
FROM Test
</cfquery>
<cfset fnResults.status = "200">
<cfelse>
<cfset fnResults.status = "400">
<cfset fnResults.message = "Invalid access attempt.">
</cfif>
<cfreturn fnResults>
</cffunction>
<!--- More functions below --->
</cfcomponents>
If anyone have suggestion what would be the best fix please let me know. Thank you.
You should set the requesttimeout in the method that contains that long-running cfquery.
You don't want to "punish" all methods for just one method. If you set it for all, how do you know which one is slow and which one is okay, unless you don't care?
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)>
...
UPDATED CODE TO LATEST ITERATION
The following function consumes a webservice that returns address details based on zip code (CEP). I'm using this function to parse the xml and populate an empty query with the address details. I would like to know if there is a more elegant way to achieve the same result. It seems to be a waste to create an empty query and populate it...
Any ideas could my method be modified or the code factored/simplified?
<!--- ****** ACTION: getAddress (consumes web-service to retrieve address details) --->
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<!--- Defaults: strcep (cep (Brazilian zip-code) string webservice would look for), search result returned from webservice --->
<cfargument name="cep" type="string" default="00000000">
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0>
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<cftry>
<!--- Consume webservice --->
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#"></cfhttp>
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText>
<cfset tmp.streetName = nodes[1].nome.XmlText>
<cfset tmp.area = nodes[1].bairro.XmlText>
<cfset tmp.city = nodes[1].cidade.XmlText>
<cfset tmp.state = nodes[1].uf.XmlText>
<cfset tmp.cep = arguments.cep>
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(tmp.state)>
<cfset tmp.stateid = stateid.id>
<cfreturn tmp>
</cfif>
<!--- Display error if any --->
<cfcatch type="any">
<cfoutput>
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfoutput>
</cfcatch>
</cftry>
</cfif>
</cffunction>
<!--- ****** END ACTION getAddress --->
The calling code:
<!--- Get address data based on CEP --->
<cfset session.addressData = getAddress(cep=params.newMember.cep)>
I can't test this because I don't have an example XML file / CEP to test with, but here is a minor rewrite that addresses four things:
Instead of using cfparam and some strange "params" structure, you should pass the CEP into the function as an argument.
The function shouldn't directly modify session data. Instead, you should return the result and let the calling code assign it to the session (or wherever else it might be needed). I'll show this in a 2nd code example.
Cache the xml result per CEP -- assuming this doesn't change often. (You'll have to improve it further if you want time-based manual cache invalidation, but I can help add that if necessary)
Don't use StructInsert. It's not necessary and you're just writing it the long way for the sake of writing it the long way. There is no benefit.
Again, this isn't tested, but hopefully it's helpful:
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<cfargument name="cep" type="string" default="00000000" /><!--- (cep (Brazilian zip-code) string webservice would look for) --->
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0 />
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<cfif not structKeyExists(application.cepCache, arguments.cep)><!--- or cache is expired: you'd have to figure this part out --->
<!--- Consume webservice --->
<cftry>
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#" />
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText />
<cfset tmp.streetName = nodes[1].nome.XmlText />
<cfset tmp.area = nodes[1].bairro.XmlText />
<cfset tmp.city = nodes[1].cidade.XmlText />
<cfset tmp.state = nodes[1].uf.XmlText />
<cfset tmp.cep = arguments.cep />
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(session.addressData.state)>
<cfset tmp.stateid = stateid.id />
</cfif>
<cfreturn duplicate(tmp) />
<!--- Display error if any --->
<cfcatch type="any">
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfcatch>
</cftry>
<cfelse>
<!--- cache exists and is not expired, so use it --->
<cfreturn duplicate(application.cepCache[arguments.cep]) />
</cfif>
</cfif>
<!---
<!--- Redirect to page two of the sign up process --->
<cfset redirectTo(controller="assine", action="perfil")>
--->
</cffunction>
Notice that I commented out the redirect you had at the end. That's because with my function, you'll be returning a value, and the redirect should be done after that, like so:
<cfset session.addressData = getAddress("some-CEP-value") />
<cfset redirectTo(controller="assine", action="perfil")>
If you're going to leave out the caching (As you say in a comment you will), then here is a version that makes no attempt at caching:
<cffunction name="getAddress" access="remote" returntype="any" output="false">
<cfargument name="cep" type="string" default="00000000" /><!--- (cep (Brazilian zip-code) string webservice would look for) --->
<cfset var searchResult = "">
<cfset var nodes = "">
<cfset var cfhttp = "">
<cfset var stateid = 0 />
<cfset var tmp = structNew()>
<!--- Validate cep string --->
<cfif IsNumeric(arguments.cep) AND Len(arguments.cep) EQ 8>
<!--- Consume webservice --->
<cftry>
<cfhttp method="get" url="http://www.bronzebusiness.com.br/webservices/wscep.asmx/cep?strcep=#arguments.cep#" />
<cfset searchResult = xmlparse(cfhttp.FileContent)>
<cfset nodes = xmlSearch(searchResult, "//tbCEP")>
<!--- If result insert address data into session struct --->
<cfif arrayLen(nodes)>
<cfset tmp.streetType = nodes[1].logradouro.XmlText />
<cfset tmp.streetName = nodes[1].nome.XmlText />
<cfset tmp.area = nodes[1].bairro.XmlText />
<cfset tmp.city = nodes[1].cidade.XmlText />
<cfset tmp.state = nodes[1].uf.XmlText />
<cfset tmp.cep = arguments.cep />
<!--- Get state id and add to struct --->
<cfset stateid = model("state").findOneByStateInitials(session.addressData.state)>
<cfset tmp.stateid = stateid.id />
</cfif>
<cfreturn duplicate(tmp) />
<!--- Display error if any --->
<cfcatch type="any">
<h3>Sorry, but there was an error.</h3>
<p>#cfcatch.message#</p>
</cfcatch>
</cftry>
</cfif>
<!---
<!--- Redirect to page two of the sign up process --->
<cfset redirectTo(controller="assine", action="perfil")>
--->
</cffunction>
Note that I did leave in the use of duplicate(). What this does is return a duplicate of the object (in this case, the struct). This is much more important when you start to work on applications where you're passing complex values into and out of functions over and over again. Using duplicate() causes things to be passed by value instead of by reference. It may not bite you in this case, but it's a good habit to get into.
I would also still use the function argument and return a value -- but it's arguable that this is my personal preference. In a way it is. I believe that a function should be fully encapsulated; a total "black box". You give it some input and it gives you back some output. It should not modify anything outside of itself. (Again, just my opinion.)
So assuming you're using this function as part of a larger multi-step process, you should still use it the same way I've described above. The only difference is that you're setting the session variable outside of the function body. Just as previously:
<cfset session.addressData = getAddress("some-CEP-value") />
<cfset redirectTo(controller="assine", action="perfil")>
That looks pretty straightforward. CF doesn't (yet?) have any magical XML-to-Query functions, but that would be pretty cool. If you wanted, you could probably write up an XSL transform to go from XML to WDDX so that you could use the cfwddx tag ... but that's probably putting the cart before the horse.
You need to move your arrayLen() if block into the try block. As it stands, if the cfhttp tag throws an error, the nodes variable will be a string and not an array, thus causing the arrayLen() to throw another error.
Minor nitpick: I wouldn't add a row to the query until inside the arrayLen() block. That way, the calling code can check recordCount to see if the result was a success.
Beyond that ... that's pretty much how it's done.
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>