apologies if this has been asked before but i couldn't find anything that could answer my question.
We have a Coldfusion script that runs from our website and queries an external dsn in our office.
Problem is that the server is located in rural england and sometimes the server is unavailable (thanks to our unreliable UK ISP!!)
Is there a command in coldfusion that could query or ping an external dsn so I can wrap the whole script in a cfif statement and get an email if it fails to connect?
This is an extension to a comment on duncan's answer, but can't put code blocks in comments, so...
<cfset TargetHost = "0.0.0.0" />
<cfset PingAttempts = 4 />
<cfif find('Windows',Server.Os.Name)>
<cfset Args = "-n #PingAttempts# #TargetHost#" />
<cfelse>
<cfset Args = "-c #PingAttempts# #TargetHost#" />
</cfif>
<cfexecute name="ping" arguments=#Args# variable="PingResult" timeout=10 />
<cfif PingResult CONTAINS "100% packet loss"
OR PingResult CONTAINS "100% loss"
>
[send alert]
</cfif>
<cfdump var=#PingResult# />
(In general, I'd still go with checking via an actual query - pinging only confirms the machine is online, not that the database is responding.)
JFGI:
<cfexecute name="C:\winnt\system32\ping.exe" arguments="#request.myIP#" variable="myPing" timeout="8"></cfexecute>
Or on Linux/Unix:
<cfexecute name="ping" arguments="#request.myIP# -c 3" timeout="10" variable="myPing" />
<cfif myPing contains "Request timed out">
<cfmail ...>
<cfelse>
[do something else]
</cfif>
Or http://www.bennadel.com/blog/751-Learning-ColdFusion-8-Ping-User-Defined-Function-Inspired-By-Ray-Camden-.htm
To find out if the datasource works just try to use it:
<cftry>
<cfquery datasource="external" timeout=10 >
SELECT 1
</cfquery>
<cfcatch>
<cfif [timeout error]>
<cfmail ...>No response in 10 seconds</cfmail>
<cfelse>
[do something else]
</cfif>
</cfcatch>
</cftry>
Since that doesn't appear to work, another option might be:
<cfthread action="run" name="QueryThread">
<cfquery datasource="external">SELECT 1</cfquery>
</cfthread>
<cfthread action="run" name="CheckThread">
<cfthread action="sleep" duration="10000" />
<cfif cfthread.QueryThread.Status NEQ 'COMPLETED'>
[handle error]
<cfthread action="terminate" name="QueryThread" />
</cfif>
</cfthread>
Same concept, but using multiple threads should mean that even if the query is too deep for CF to break, you can still trigger the email sending after ten seconds.
Related
Does anyone know how I can forcibly stop a function execution in the cfc component if it works for more than a certain time? Either it may be piece of code, not a whole function, i.e. if it has completed in 5 seconds, I take some actions, otherwise others.
The only way to stop an arbitrary piece of code is to run it in a separate thread and then terminating after a set amount of time. This can be done by calling out to a separate page with a request timeout set or using cfthread.
For example with thread...
(Note.... as Alex pointed out you can use timeout on cfthread)
<cfthread action="run" name="runForLimitedTime">
... code, call to function, etc ...
</cfthread>
<cfset sleep(5000) />
<cfif cfthread.runForLimitedTime.status eq "COMPLETED">
<cfthread action="join" />
<cfelse>
<cfthread action="terminate" name="runForLimitedTime" />
</cfif>
Alternatively with a separate page...
<!--- calling page --->
<cfset error = false />
<cftry>
<cfhttp url="pageSetupForSpecificCall.cfm?timeout=5" throwonerror="true" />
<cfcatch>
<cfset error = true />
</cfcatch>
</cftry>
<cfif error>
something
<cfelse>
something else
</cfif>
<!--- pageSetupForSpecificCall.cfm --->
<cfsetting requesttimeout="#url.timeout#" />
...do things...
Our code base has quite a bit of the following example as we allow a lot of our base pages to be customized to our customers' individual needs.
<cfif fileExists("/custom/someFile.cfm")>
<cfinclude template="/custom/someFile.cfm" />
<cfelse>
<cfinclude template="someFile.cfm" />
</cfif>
I wanted to create a custom CF tag to boilerplate this as a simple <cf_custominclude template="someFile.cfm" />, however I ran into the fact that custom tags are effectively blackboxes, so they aren't pulling in local variables that exist prior to the start of the tag, and I can't reference any variable that was created as a result of the tag from importing the file.
E.G.
<!--- This is able to use someVar --->
<!--- Pulls in some variable named "steve" --->
<cfinclude template="someFile.cfm" />
<cfdump var="#steve#" /> <!--- This is valid, however... --->
<!--- someVar is undefined for this --->
<!--- Pulls in steve2 --->
<cf_custominclude template="someFile.cfm" />
<cfdump var="#steve2#" /> <!--- This isn't valid as steve2 is undefined. --->
Is there a means around this, or should I utilize some other language feature to accomplish my goal?
Well, I question doing this at all but I know we all get handed code at times we have to deal with and the struggle it is to get people to refactor.
This should do what you are wanting. One important thing to note is that you will need to ensure your custom tag has a closing or it won't work! Just use the simplified closing, so like you had it above:
<cf_custominclude template="someFile.cfm" />
This should do the trick, called it has you had it : custominclude.cfm
<!--- executes at start of tag --->
<cfif thisTag.executionMode eq 'Start'>
<!--- store a list of keys we don't want to copy, prior to including template --->
<cfset thisTag.currentKeys = structKeyList(variables)>
<!--- control var to see if we even should bother copying scopes --->
<cfset thisTag.includedTemplate = false>
<!--- standard include here --->
<cfif fileExists(expandPath(attributes.template))>
<cfinclude template="#attributes.template#">
<!--- set control var / flag to copy scopes at close of tag --->
<cfset thisTag.includedTemplate = true>
</cfif>
</cfif>
<!--- executes at closing of tag --->
<cfif thisTag.executionMode eq 'End'>
<!--- if control var / flag set to copy scopes --->
<cfif thisTag.includedTemplate>
<!--- only copy vars created in the included page --->
<cfloop list="#structKeyList(variables)#" index="var">
<cfif not listFindNoCase(thisTag.currentKeys, var)>
<!--- copy from include into caller scope --->
<cfset caller[var] = variables[var]>
</cfif>
</cfloop>
</cfif>
</cfif>
I tested it and it works fine, should work fine being nested as well. Good luck!
<!--- Pulls in steve2 var from include --->
<cf_custominclude template="someFile.cfm" />
<cfdump var="#steve2#" /> <!--- works! --->
I've got the following code in a method:
<cffunction name="serviceTicketValidate" access="public" output="yes" returntype="void" hint="Validate the service ticket">
<cfargument name="service_ticket" type="string" required="yes" hint="The ST to validate" />
<!--- Contact the CAS server to validate the ticket --->
<cfhttp url="#Variables.cas_server#serviceValidate" method="get">
<cfhttpparam name="ticket" value="#Arguments.service_ticket#" type="url" />
<cfhttpparam name="service" value="#Variables.service#" type="url" />
</cfhttp>
<!--- Received a valid XML response --->
<cfif IsXML(cfhttp.FileContent)>
<cfset XMLobj = XmlParse(cfhttp.fileContent)>
<!--- Check for the cas:user tag --->
<cfset CASuser = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:user")>
<!--- Set the username to the value --->
<cftry>
<cfif variables.username NEQ ''>
<cfdump var="#Variables.username#" /><cfreturn/>
</cfif>
<cfif ArrayLen(CASuser)>
<cfset Variables['username'] = CASuser[1].XmlText />
</cfif>
<cfcatch>
<cfdump var="#cfcatch#" /><cfabort/>
</cfcatch>
</cftry>
<!--- Search for cas:attributes --->
<cfset CASattributes = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:attributes")>
<!--- Go through all the attributes and add them to the attributes struct --->
<cfif ArrayLen(CASattributes)>
<cfloop array=#CASattributes[1].XmlChildren# index="attribute">
<cfset StructInsert(Variables.attributes,RemoveChars(attribute.XmlName,1,Find(":",attribute.XmlName)),attribute.XmlText)/>
</cfloop>
</cfif>
</cfif>
Note I added the cftry and cfcatch to see what is going on exactly. I've also added the if username != blank to debug as well. This method is called in another method like so:
<cfinvoke method="serviceTicketValidate">
<cfinvokeargument name="service_ticket" value="#service_ticket#" />
</cfinvoke>
<cfdump var="test2" /><cfabort/>
Again I've added the dump and abort for testing. The variable.username is defied and set to an empty string when the component is initiated and the component is initiated into a session variable.
So get this... when the whole process runs the first time I get output on my screen test2 as expected. Then, the next time the same thing is run, the session exists, thus the variable.username is set to something. In the first code block I can dump variables.username and see the username. However if I try to use variables.username in a conditional expression (like in that if statement) or if I remove the if statement and let the script try to change the value of variable.username, there are no errors, it just breaks out of the script completely. It ends that method, and the method that called it and I don't see test2 like I would think. It all just ends for some reason.
If you need further details I can provide more code but I tried to trim out as much as I thought was relevant. All methods are in the same component, all methods are public. Why can't I change the value of variables.username and why is there no error?
EDIT:
I think it may have something to do with the cflock but I'm debugging some stuff right now. I had a redirect inside the code block that is inside the lock. So I guess it never unlocks. But I even waited after the timeout and it still remained locked. I thought the lock was supposed to expire after the timeout.
I'm a little confused but it seems like you're trying to use a cfc's variables scope to set caller variables. The variables scope is not available to the caller the way it seems you are trying to use it.
index.cfm
<cfoutput>
<cfset objTest = createObject("component", "testscope").init()><br><Br>
<cfset objTest.checkValue()><br><br>
Calling page is checking the existence of testvar: #isDefined("variables.testvar")#
</cfoutput>
testscope.cfm
<cfcomponent displayname="testscope">
<cffunction name="init" access="public">
init() just set variables.testvar.
<cfset variables.testvar = "Okay, set">
<cfreturn This>
</cffunction>
<!--- Set some more variables --->
<cffunction name="checkValue" access="public">=
<cfoutput>Checkvalue is checking the value of testvar: #variables.testvar#</cfoutput>
</cffunction>
</cfcomponent>
The output is
init() just set variables.testvar.
Checkvalue is checking the value of testvar: Okay, set
Calling page is checking the existence of testvar: false
My application pulls lots of data from different applications according to the selected scopes. For e.g. Org level and SubOrg level scopes, this can cause to bring server down and everyday causes server crashing and timeouts.
So I wanted to know whether wrapping cfthread around my cfhttp will solve my timeout problem?
<cfinclude template="fundedScopes.cfm">
<!--- Call the funded scopes --->
<cfset dynVarName = "funded" & bizforShort>
<cfif structKeyExists(variables,dynVarName)>
<cfset howManyCustomScopes = listLen(structkeylist(variables[dynVarName],"|" ),"|" )>
<cfmodule template="#Request.Library.CustomTags.VirtualPath#Scheduler_LogDetail.cfm"
Step="Funded Level Cache" Detail="Custom Scopes to be cached: #howManyCustomScopes#"
LogData=""></cfmodule>
<cfloop collection="#variables[dynVarName]#" item="t">
<cfset tempurl = variables[dynVarName][t]["url"]>
<cfset tempurl = tempurl & "&retainCache=1">
<cfoutput>
<cfhttp url="#tempurl#" method="GET" resolveurl="false" timeout="3000">
#tempurl#<br>
<cfset scopesCachedCounter = scopesCachedCounter + 1>
<cfmodule template="#Request.Library.CustomTags.VirtualPath#Scheduler_LogDetail.cfm" Step="Funded Scopes Cache" Detail="#scopesCachedCounter#.- #t#" LogData="#tempurl#"></cfmodule>
</cfoutput>
</cfloop>
</cfif>
I have a form where I can upload logos a plenty. I'm validating files (empty form fields, wrong extension, ...) inside a cftry/cfcatch statement.
When I find an error in the cftry, I do this:
<cfthrow type="FileNotFound" message="#tx_settings_app_error_create_first#" />
and inside my 'cfcatch'
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#cfcatch.message#");window.location.href="hs_apps.cfm"; } </script></cfoutput>
</cfcatch>
This works fine, catches all errors and alerts the user what is wrong.
Now I wanted to use the same handler on a database call where I'm checking for duplicate username. Still the same:
<cfquery datasource="#Session.datasource#" name="duplicates">
SELECT a.app_alias
FROM apps AS a
WHERE a.iln = <cfqueryparam value = "#Session.loginID#" cfsqltype="cf_sql_varchar" maxlength="13">
AND a.app_alias = <cfqueryparam value = "#form.app_basic_alias#" cfsqltype="cf_sql_varchar" maxlength="50">
</cfquery>
<cfif duplicates.recordcount GT 0>
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
</cfif>
The cfcatch is also the same.
However. This now procudes a server error page and I'm thrown out of the application.
Question:
Any idea, why I'm struggling to get cftry/cfcatch to work here? I'm clueless.
Thanks!
EDIT:
Here is the full code
<cfif isDefined("send_basic")>
<cfset variables.timestamp = now()>
<cfset variables.orderview = "1">
<cfif form.send_basic_type EQ "new">
<cftry>
<cfif module_check.recordcount GT 0>
<cfloop query="module_check">
<cfif module_check.module_name EQ "preorder">
<cfset variables.module_name = module_check.module_name>
<cfset variables.b2b_preord_ok = "true">
</cfif>
</cfloop>
<cfif form.app_basic_orderview EQ "preo">
<cfset variables.orderview = "0">
</cfif>
</cfif>
<!--- PROBLEM HERE: DUPLICATES --->
<cfquery datasource="#Session.datasource#" name="duplicates">
SELECT a.app_alias
FROM apps AS a
WHERE a.iln = <cfqueryparam value = "#Session.loginID#" cfsqltype="cf_sql_varchar" maxlength="13">
AND a.app_alias = <cfqueryparam value = "#form.app_basic_alias#" cfsqltype="cf_sql_varchar" maxlength="50">
</cfquery>
<cfif duplicates.recordcount GT 0>
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
</cfif>
<!--- IF PASS, CREATE/UPDATE --->
<cfquery datasource="#Session.datasource#">
INSERT INTO apps ( ... )
VALUES( ... )
</cfquery>
<cfset variables.app_action = "Applikation erstellt">
<!--- success --->
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_cfm_create#");}</script></cfoutput>
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_err_create#");}</script></cfoutput>
</cfcatch>
</cftry>
<cfelse>
<cftry>
<!--- UPDATE --->
<cfquery datasource="#Session.datasource#">
UPDATE apps
SET ... = ...
</cfquery>
<cfset variables.app_action = "Applikation aktualisiert">
<!--- success --->
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_cfm_update#");}</script></cfoutput>
<cfcatch>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_app_err_update#");}</script></cfoutput>
</cfcatch>
</cftry>
</cfif>
</cfif>
The error message I'm getting it the message I specify =
<cfthrow type="FileNotFound" message="#tx_settings_apps_error_dup#" />
Which if caught should alert the text behind tx_settings_apps_error_dup. If I dump the cfcatch, cfcatch.message is my specified text, so the error gets caught allright, only I get a server error page vs. an alert. I'm using exactly the same handler for fileuploads and form submits. I don't get why it's not working here?
Thanks for helping out!
WORKAROUD:
Note nice, but suffice(s):
<cfif dups.recordcount NEQ 0>
<cfoutput><script type="text/javascript">window.onload = function(){alert("#tx_settings_apps_error_dup#"); location.href = "hs_apps_detail.cfm?create=newApp&id=none";}</script
</cfoutput>
<cfabort>
</cfif>
So when a duplicate is found I alert the user, reload the exact same page and cfabort to prevent the old page from processing further. Patch that is.
(moved this down from being just a comment)
OK, so the catch is definitely catching the exception if you're able to dump it, so it's not that the try/catch ain't working, it's something else. Bear in mind that processing will continue until the end of the request after your catch block, and only then will the output buffer be flushed, and your alert() sent to the browser. It sounds to me like after your output the alert, and processing continues, some OTHER error is occurring which is causing the server's error page to display. I recommend getting rid of the error page temporarily and eyeballing the actual error CF is raising.
NB: if you want processing to stop immediately in the catch block after you output the alert(), you're going to need to tell CF that, ie: with a <cfabort>. Otherwise, as per above, it'll just keep going.
I think exceptions that are caught by the server error page are still logged in the exception log, but I could be wrong. You could always put an onError() handler in your Application.cfc, log whatever error occurred, then rethrow the error so the error page still deals with it. That way you get the info on the error, and the punter still sees the nice error page.