Rollback with CFTransaction, CFThrow and CFDump abort - coldfusion

I have read the documentation about cftransaction but can't figure out the difference between rolling back with <cfdump var="#myVar#" abort>, <cftransaction action="rollback" /> or <cfthrow message="Error">
Assume that var a is preset for example purposes. All queries inside the cftransaction tag only do a rollback when using cfdump abort. With the proper rollback action or a cfthrow tag they do not rollback.
I'm new with CF2016. Can someone explain to me the differences between a rollback with one or another?
<cftransaction action="begin">
...somecode with querys...
<cfif a eq 1>
<cftransaction action="rollback" />
<cfelseif a eq 2>
<cfthrow message="Error">
<cfelse>
<cfdump var="Error" abort>
</cfif>
</cftransaction>

Try to set a save-point before doing the rollback action... I'm not 100% sure if you need to explicitly set that before actually being able to do the rollback
Because <cfabort> stops processing of the page all the queries that were made in the <cftransaction> tag would be reversed,regardless of save points while with the <cftransaction action="rollback" /> you can reset it to a specific point in the set of queries.
For example think about the following flow
<cftransaction>
<Query 1 >
<Query 2 >
<cftransaction action = "setsavepoint" savepoint = "#point#"/>
<Query 3 >
</cftransaction>
If query 3 fails with <cftransaction action="rollback" savepoint="#point#" /> you can roll back to a point where you wouldnt have to redo the <Query 1 > <Query 2 > and just worry about <Query 3 > saving you some overhead by not repeating those queries. With CFABORT in the CFDUMP it would abort the entire transaction
The last example on this page with the withdraws shows the use of savepoints
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-t/cftransaction.html

Related

Forcibly stop the execution of the function in the cfc component

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...

CFIF statement checking if database (MSSQL) is connecting or exists

I am trying to write an if statement in Coldfusion 16 to check to see if it can connect to the database or that it exists. If it can connect to the database then show the page otherwise show down for maintenance. How should I just check to make sure the database is up? Any help with this would be greatly appreciated.
<cfquery name="DBUP" datasource="datasource">
SELECT ID
FROM dbo.Entry
</cfquery>
<cfif DBUP>
Show Page
<cfelse>
Show Down For Maintenance
</cfif>
You shouldn't directly put a error message or file in a catch statement. We should check one more condition ie) Error code is 0 or not. Imagine in some time the developer may give wrong query like undefined column or missing syntax etc... In that time also the catch will executed. So as per the requirement we have to check the datasource exists or not. If we want to check all type of issue means we no need of the error code conditions.
<cftry>
<cfset showPage = true>
<cfquery name="DBUP" datasource="datasource">
SELECT ID FROM dbo.Entry
</cfquery>
<cfcatch type="database">
<!--- The error code 0 is mentioned Datasource could not be found/exits. --->
<cfif cfcatch.cause.errorcode eq 0 >
<cfset showPage = false >
<!--- Data source does not exists --->
</cfif>
</cfcatch>
</cftry>
Based on showPage value do you business logic here
....
....
....
You will need to add it in Application.cfc onRequest method probably.
<cftry>
<cfquery name="DBUP" datasource="datasource">
SELECT ID
FROM dbo.Entry
</cfquery>
<cfcatch type="any">
Show DOwn For Maintenance
<!---everything optional below this line--->
<!---can show some custom message--->
<cfinclude template="errorMsg.cfm">
<!---stop the processing further--->
<cfabort>
</cfcatch>
</cftry>

Coldfusion how to get function name and line number when application crashed or error

i'm working on my legacy system old code of coldfusion, is there a way i can define cfcatch in application.cfc and catch all errors of my application with
Function name
Query name
Line Number of code
Template Name
To debug fast rather then writing everywhere in code.
application developer did not catch any error anywhere in code.i did insert cfcatch in code some of the places but still lot more to do, and because of production i don't want to modify so much of code.
im inserting cfcatch in databse and sending email to development team. because system is in production.
You can use the cferror tag, or onError to direct all errors to a given page/function.
If you use cferror, the exception will be passed in the error variable. If you use OnError, it's a parameter.
To help you along, my own error emails include the following. You will notice that we have special handling to help point out places where a blank may have been passed into a sql integer field, which happens more often than I'd like to admit.
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<!--- Smarter error catching for form fields --->
<cfif (error.message contains "Invalid data '' for CFSQLTYPE CF_SQL_INTEGER") and isdefined("form")>
<!--- This stores a list of the Id fields --->
<cfloop collection="#form#" item="thisField">
<!--- Get the last two characters of the field name --->
<cfset lastTwoChars = right(thisField, 2)>
<!--- Get the value of the field --->
<cfset thisFieldValue = evaluate('form.#thisField#')>
<!--- Check to see if this is an Id field and if it's value is blank. --->
<cfif lastTwoChars eq 'Id' and thisFieldValue eq ''>
<h3 style="font-weight: bold; color: red">#thisField# is blank and it's possibly an integer field.</h3>
</cfif>
</cfloop>
</cfif>
<cfdump var="#error#" label="Error">
<br/>
<cfdump var="#form#" label="Form">
<br/>
<cfdump var="#url#" label="URL">
<br/>
<cfdump var="#session#" label="session">

Coldfusion Ping or check external DSN is online

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.

why can I not catch an error message in a Coldfusion cftry/cfcatch statement?

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.